I use the following code to generate the fibonacci lattice, see page 4 for the unit sphere. I think the code is working correctly. Next, I have a list of points (specified by latitude and longitude in radians, just as the generated fibonacci lattice points). For each of the points I want to find the index of the closest point on the fibonacci lattice. I.e. I have latitude and longitude and want to get i. How would I do this?
I specifically don't want to iterate over all the points from the lattice and find the one with minimal distance, as in practice I generate much more than just 50 points and I don't want the runtime to be O(n*m) if O(m) is possible.
FWIW, when talking about distance, I mean haversine distance.
#!/usr/bin/env python2
import math
import sys
n = 50
phi = (math.sqrt(5.0) + 1.0) / 2.0
phi_inv = phi - 1.0
ga = 2.0 * phi_inv * math.pi
for i in xrange(-n, n + 1):
longitude = ga * i
longitude = (longitude % phi) - phi if longitude < 0 else longitude % phi
latitude = math.asin(2.0 * float(i) / (2.0 * n + 1.0))
print("{}-th point: ".format(i + n + 1))
print("\tLongitude is {}".format(longitude))
print("\tLatitude is {}".format(latitude))
// Given latitude and longitude of point A, determine index i of point which is closest to A
// ???
What you are probably looking for is a spatial index: https://en.wikipedia.org/wiki/Spatial_database#Spatial_index. Since you only care about nearest neighbor search, you might want to use something relatively simple like http://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.spatial.KDTree.html.
Note that spatial indexes usually consider points on a plane rather than a sphere. To adapt it to your situation, you'll probably want to split up the sphere into several regions that can be approximated by rectangles. You can then find several of the nearest neighbors according to the rectangular approximation and compute their actual haversine distances to identify the true nearest neighbor.
It's somewhat easier to use spherical coordinates here.
Your spherical coordinates are given by lat = arcsin(2 * i / (2 * N + 1)), and lon = 2 * PI * i / the golden ratio.
Reversing this is not a dead end - it's a great way to determine latitude. The issue with the reverse approach is only that it fails to represent longitude.
sin(lat) = 2 * i / (2 * N + 1)
i = (2 * N + 1) * sin(lat) / 2
This i is an exact representation of the index of a point matching the latitude of your input point. The next step is your choice - brute force, or choosing a different spiral.
The Fibonacci spiral is great at covering a sphere, but one of its properties is that it does not preserve locality between consecutive indices. Thus, if you want to find the closest points, you have to search a wide range - it is difficult to even estimate bounds for this search. Brute force is expensive. However, this is already a significant improvement over the original problem of checking every point - if you like, you can threshhold your results and bound your search in any way you like and get approximately accurate results. If you want to accomplish this in a more deterministic way, though, you'll have to dig deeper.
My solution to this problem looks a bit like this (and apologies, this is written in C# not Python)
// Take a stored index on a spiral on a sphere and convert it to a normal vector
public Vector3 UI2N(uint i)
{
double h = -1 + 2 * (i/n);
double phi = math.acos(h);
double theta = sqnpi*phi;
return new Vector3((float)(math.cos(theta) * math.sin(phi)), (float)math.cos(phi), (float)(math.sin(theta) * math.sin(phi)));
}
// Take a normalized vector and return the closest matching index on a spiral on a sphere
public uint N2UI(Vector3 v)
{
double iT = sqnpi * math.acos(v.y); // theta calculated to match latitude
double vT = math.atan2(v.z, v.x); // theta calculated to match longitude
double iTR = (iT - vT + math.PI_DBL)%(twoPi); // Remainder from iTR, preserving the coarse number of turns
double nT = iT - iTR + math.PI_DBL; // new theta, containing info from both
return (uint)math.round(n * (math.cos(nT / sqnpi) + 1) / 2);
}
Where n is the spiral's resolution, and sqnpi is sqrt(n * PI).
This is not the most efficient possible implementation, nor is it particularly clear. However, it is a middle ground where I can attempt to explain it.
The spiral I am using is one I found here:
https://web.archive.org/web/20121103201321/http://groups.google.com/group/sci.math/browse_thread/thread/983105fb1ced42c/e803d9e3e9ba3d23#e803d9e3e9ba3d23%22%22
(Orion's spiral is basically the one I'm using here)
From this I can reverse the function to get both a coarse and a fine measure of Theta (distance along the spiral), and combine them to find the best-fitting index. The way this works is that iT is cumulative, but vT is periodic. vT is a more correct measure of the longitude, but iT is a more correct measure of latitude.
I strongly encourage that anyone reading this try things other than what I'm doing with my code, as I know that it can be improved from here - that's what I did, and I would do well to do more. Using doubles is absolutely necessary here with the current implementation - otherwise too much information would be lost, particularly with the trig functions and the conversion to uint.
Related
I have problems with shifting coordinates by 100 meters horizontaly/verticaly in Python. I found that If your displacements aren't too great (less than a few kilometers) and you're not right at the poles, use the quick and dirty estimate that 111,111 meters (111.111 km) in the y direction is 1 degree (of latitude) and 111,111 * cos(latitude) meters in the x direction is 1 degree (of longitude). here: https://gis.stackexchange.com/questions/2951/algorithm-for-offsetting-a-latitude-longitude-by-some-amount-of-meters
I wrote two functions in Python. This function works, that is it computes the shift in latitude:
def vertikalne(shift):
return shift/111_111
Check that distance between (49.550586, 18.859254) and (49.550586 + vertikalne(100), 18.859254) is really 100 meters.
But this function does not work and I don`t know what is the problem:
import math
def horizontalne(latitude, shift):
return (shift/111_111) * math.cos(latitude*math.pi/180) # UPDATE: I just converted to radians
Distance between (49.550586, 18.859254) and (49.550586 , 18.859254 + horizontalne(49.550586 , 100)) is 49 which is nonsense.
Can you help me please?
Correct solutions is:
import math
def horizontalne(latitude, shift):
return shift/(111_111 * math.cos(math.radians(latitude)))
Change math.cos(latitude) to math.cos(math.pi/180*latitude) as the math.cos() expects angles in radians and not in degrees.
math.cos() takes radians as input not degrees. You need to convert.
def horizontalne(latitude, shift):
return (shift/111_111) * math.cos(2*math.pi()*latitude/360)
something like this.
And if you want to calculate distances you need to apply Pythagoras' theorem. Maybe even take the average of the latitudes for the cos.
--
Mine is wrong. #vojtam is correct. (parentheses...)
It is rather unclear what the function is supposed to do. Maybe better to give your params more meaningful names. e.g. shift_distance, shift_long, shift_lat etc.
I think it is supposed to do the following:
The functions both take a distance as input and as output it gives the fraction of 1 degree of the arc of the intended circle around the earth. (for vertikalne it is the meridian, for horizontalne it is the parallel at the given latitude. (https://www.britannica.com/science/latitude )
111_111 meters is indeed more or less the distance over the earth of 1 degree. (at least along a meridian, the equator is slightly larger....)
So your evaluation should be different:
A = (49.550586, 18.859254) # lat,long
B = (49.550586, 18.859254 + horizontalne(49.550586 , 100)) # B is 100m east of A along the parallel at lat 49.550586
(B[0] - A[0], (B[1] - A[1]) )
gives:
Out[42]: (0.0, 0.0005838993787818936)
Which means: moving 100 meters horizontally along the earth's parallel at latitude 49.55... means changing the longitude angle for 0.0005838993787818936 of a degree. Makes sense to me.
I want to calculate a point/vector with the least Euclidian distance to a given set of N lines (e.g. given by a point and a vector for the direction) in a D dimensional space (for example by least squares)
Since I use Python for my project, I was wondering, whether there are already appropriate implementations for this general problem in some standard library like numpy, but I have not found any.
There are already related questions like:
Finding the centre of multiple lines using least squares approach in Python
nearest intersection point to many lines in python
However, these questions did not consider a dimension larger than 3 and in my case, I would like to adapt the problem to dimensions like 100.
I also found this resource for Matlab, which does not seem to be used that much, but it deals with the same problem:
https://de.mathworks.com/matlabcentral/fileexchange/59805-line-line-intersection-n-lines-d-space?s_tid=FX_rc1_behav
If each line with index i is given by a unit vector column
vi = {
v1i,
v2i,
v3i,
...
vDi
}
pointing along the line i, and a point given by a vector column
pi = {
p1i,
p2i,
p3i,
...
pDi
}
, where i = 1...N, then the point x you seek, given as a column, is given by the equation
x = inverse( sum(i=1:N, I - vi * transposed(vi)) ) * sum(i=1:N, (I - vi * transposed(vi)) * pi);
Here I is the D-dimensional identity square matrix.
If each line is given by two points pi and qi then you can calculate
vi = (qi - pi) / sqrt(transposed(qi - pi) * (qi - pi))
First of all, will appreciate if someone will give me a proper term for "annulus with a shifted hole", see exactly what kind of shape I mean on a picture below.
Back to main question: I want to pick a random point in the orange area, uniform distribution is not required. For a case of a usual annulus I would've picked random point in (r:R) range and a random angle, then transform those to x,y and it's done. But for this unusual shape... is there even a "simple" formula for that, or should I approach it by doing some kind of polygonal approximation of a shape?
I'm interested in a general approach but will appreciate an example in python, javascript or any coding language of your choice.
Here's a simple method that gives a uniform distribution with no resampling.
For simplicity assume that the center of the outer boundary circle (radius r_outer) is at (0, 0) and that the center of the inner circular boundary (radius r_inner) lies at (x_inner, y_inner).
Write D for the outer disk, H1 for the subset of the plane given by the off-center inner hole, and H2 for the central disk of radius r_inner, centered at (0, 0).
Now suppose that we ignore the fact that the inner circle is not central, and instead of sampling from D-H1 we sample from D-H2 (which is easy to do uniformly). Then we've made two mistakes:
there's a region A = H1 - H2 that we might sample from, even though those samples shouldn't be in the result.
there's a region B = H2 - H1 that we never sample from, even though we should
But here's the thing: the regions A and B are congruent: given any point (x, y) in the plane, (x, y) is in H2 if and only if (x_inner - x, y_inner - y) is in H1, and it follows that (x, y) is in A if and only if (x_inner - x, y_inner - y) is in B! The map (x, y) -> (x_inner - x, y_inner - y) represents a rotation by 180 degress around the point (0.5*x_inner, 0.5*y_inner). So there's a simple trick: generate from D - H2, and if we end up with something in H1 - H2, rotate to get the corresponding point of H2 - H1 instead.
Here's the code. Note the use of the square root of a uniform distribution to choose the radius: this is a standard trick. See this article, for example.
import math
import random
def sample(r_outer, r_inner, x_inner, y_inner):
"""
Sample uniformly from (x, y) satisfiying:
x**2 + y**2 <= r_outer**2
(x-x_inner)**2 + (y-y_inner)**2 > r_inner**2
Assumes that the inner circle lies inside the outer circle;
i.e., that hypot(x_inner, y_inner) <= r_outer - r_inner.
"""
# Sample from a normal annulus with radii r_inner and r_outer.
rad = math.sqrt(random.uniform(r_inner**2, r_outer**2))
angle = random.uniform(-math.pi, math.pi)
x, y = rad*math.cos(angle),rad*math.sin(angle)
# If we're inside the forbidden hole, reflect.
if math.hypot(x - x_inner, y - y_inner) < r_inner:
x, y = x_inner - x, y_inner - y
return x, y
And an example plot, generated by the following:
import matplotlib.pyplot as plt
samples = [sample(5, 2, 1.0, 2.0) for _ in range(10000)]
xs, ys = zip(*samples)
plt.scatter(xs, ys, s=0.1)
plt.axis("equal")
plt.show()
Do you really need exact sampling? Because with acceptance/rejection it should work just fine. I assume big orange circle is located at (0,0)
import math
import random
def sample_2_circles(xr, yr, r, R):
"""
R - big radius
r, xr, yr - small radius and its position
"""
x = xr
y = yr
cnd = True
while cnd:
# sample uniformly in whole orange circle
phi = 2.0 * math.pi * random.random()
rad = R * math.sqrt(random.random())
x = rad * math.cos(phi)
y = rad * math.sin(phi)
# check condition - if True we continue in the loop with sampling
cnd = ( (x-xr)**2 + (y-yr)**2 < r*r )
return (x,y)
Since you have shown no equation, algorithm, or code of your own, but just an outline of an algorithm for center-aligned circles, I'll also just give the outline of an algorithm here for the more general case.
The smaller circle is the image of the larger circle under a similarity transformation. I.e. there is a fixed point in the larger circle and a ratio (which is R/r, greater than one) such that you can take any point on the smaller circle, examine the vector from the fixed point to that point, and multiply that vector by the ratio, then the end of that vector when it starts from the fixed point is a point on the larger circle. This transformation is one-to-one.
So you can choose a random point on the smaller circle (choose the angle at random between 0 and two-pi) and choose a ratio at random between 1 and the proportionality ratio R/r between the circles. Then use that the similarity transformation with the same fixed point but using the random ratio to get the image point of the just-chosen point on the smaller circle. This is a random point in your desired region.
This method is fairly simple. In fact, the hardest mathematical part is finding the fixed point of the similarity transformation. But this is pretty easy, given the centers and radii of the two circles. Hint: the transformation takes the center of the smaller circle to the center of the larger circle.
Ask if you need more detail. My algorithm does not yield a uniform distribution: the points will be more tightly packed where the circles are closest together and less tightly packed where the circles are farthest apart.
Here is some untested Python 3.6.2 code that does the above. I'll test it and show a graphic for it when I can.
import math
import random
def rand_pt_between_circles(x_inner,
y_inner,
r_inner,
x_outer,
y_outer,
r_outer):
"""Return a random floating-point 2D point located between the
inner and the outer circles given by their center coordinates and
radii. No error checking is done on the parameters."""
# Find the fixed point of the similarity transformation from the
# inner circle to the outer circle.
x_fixed = x_inner - (x_outer - x_inner) / (r_outer - r_inner) * r_inner
y_fixed = y_inner - (y_outer - y_inner) / (r_outer - r_inner) * r_inner
# Find a a random transformation ratio between 1 and r_outer / r_inner
# and a random point on the inner circle
ratio = 1 + (r_outer - r_inner) * random.random()
theta = 2 * math.pi * random.random()
x_start = x_inner + r_inner * math.cos(theta)
y_start = y_inner + r_inner * math.sin(theta)
# Apply the similarity transformation to the random point.
x_result = x_fixed + (x_start - x_fixed) * ratio
y_result = y_fixed + (y_start - y_fixed) * ratio
return x_result, y_result
The acceptance/rejection method as described by Severin Pappadeux is probably the simplest.
For a direct approach, you can also work in polar coordinates, with the center of the hole as the pole.
The polar equation (Θ, σ) (sorry, no rho) of the external circle will be
(σ cosΘ - xc)² + (σ sinΘ - yc)² = σ² - 2(cosΘ xc + sinΘ yc)σ + xc² + yc² = R²
This is a quadratic equation in σ, that you can easily solve in terms of Θ. Then you can draw an angle in 0, 2π an draw a radius between r and σ.
This won't give you a uniform distribution, because the range of σ is a function of Θ and because of the polar bias. This might be fixed by computing a suitable transfer function, but this is a little technical and probably not tractable analytically.
I have a large quantity of pixel colors (96 thousands different colors):
And I want to get some kind of a mathematically-defined probability region like in this question:
The main obstacle I see right now – all methods on Google are mainly about visualisations and about two-dimensional spaces, yet there is no algorithm for finding coefficients of an equation like:
a1x2 + b1y2 + c1y2 + a2xy + b2xz + c2yz + a3x + b3y + c3z = 0
And this paper is too difficult for me to implement it in python. :(
Anyway, what I just want is to determine if some pixel is more-or-less lies within the diapason I have.
I tried making it using scikit clustering, but I failed due to having only one
set of data, probably. And creating an array 2563 elements
representing each pixel color seems a wrong way.
I wonder if there is an easy way to determine boundaries of this point cluster?
Or, maybe I'm just overthinking it and there is something like OpenCV
cv2.inRange() function?
this can be solved by optimization and fitting of the ellipsoid polynomial. However I would start with geometrical approach which is much faster:
find avg point position
that will be the center of your ellipsoid
p0 = sum (p[i]) / n // average
i = { 0,1,2,3,...,n-1 } // of all points
If your point density is not homogenuous then it is safer to use bounding box center instead. So find xmin,ymin,zmin,xmax,ymax,zmax and the middle between them is your center.
find most distant point to center
that will give you main semi axis
pa = p[j];
|p[j]-p0| >= |p[i]-p0| // max
i = { 0,1,2,3,...,n-1 } // of all points
find second semi-axises
so vector pa-p0 is normal to plane in which the other semi-axises should be. So find most distant point to p0 from that plane:
pb = p[j];
|p[j]-p0| >= |p[i]-p0| // max
dot(pa-p0,p[j]-p0) == 0 // but inly if inside plane
i = { 0,1,2,3,...,n-1 } // from all points
beware that the result of dot product may not be precisely zero so it is better to test against something like this:
|dot(pa-p0,p[j]-p0)| <= 1e-3
You can use any threshold you want (should be based on the ellipsoid size).
find last semi-axis
So we know that last semi-axis should be perpendicular to both
(pa-p0) AND (pb-p0)
So find point such that:
pc = p[j];
|p[j]-p0| >= |p[i]-p0| // max
dot(pa-p0,p[j]-p0) == 0 // but inly if inside plane
dot(pb-p0,p[j]-p0) == 0 // and perpendicular also to b semi-axis
i = { 0,1,2,3,...,n-1 } // from all points
Ellipsoid
Now you have all the parameters you need to form your ellipsoid. vectors
(pa-p0),(pb-p0),(pc-p0)
are the basis vectors of your ellipsoid (you can make them perpendicular by using cross product). Their size gives you the radiuses. And p0 is the center. You can also use this parametric equation:
a=pa-p0;
b=pb-p0;
c=pc-p0;
p(u,v) = p0 + a*cos(u)*cos(v)
+ b*cos(u)*sin(v)
+ c*sin(u);
u = < -0.5*PI , +0.5*PI >
v = < 0.0 , 2.0*PI >
This whole process is just O(n) and the results can be used as start point for both optimization and fitting to speed them up without the loss of accuracy. If you want to further improve accuracy See:
How approximation search works
The sub links shows you examples of fitting ...
You can also take a look at this:
Algorithms: Ellipse matching
which is basically similar to your task but only in 2D still may bring you some ideas.
Here is unstrict solution with fast and simple random search approach*. Best side - no heavy linear algebra library required**. Seems it worked fine for mesh collision detection.
Is assumes that ellipsoid center matches cloud center and then uses some sort of mirrored average to search for main axis.
Full working code is slightly bigger and placed on git, idea of main axis search is here:
np.random.shuffle(pts)
pts_len = len(pts)
pt_average = np.sum(pts, axis = 0) / pts_len
vec_major = pt_average * 0
minor_max, major_max = 0, 0
# may be improved with overlapped pass,
for pt_cur in pts:
vec_cur = pt_cur - pt_average
proj_len, rej_len = proj_length(vec_cur, vec_major)
if proj_len < 0:
vec_cur = -vec_cur
vec_major += (vec_cur - vec_major) / pts_len
major_max = max(major_max, abs(proj_len))
minor_max = max(minor_max, rej_len)
It can be improved/optimized even more at some points. Examples what it will produce:
And full experiment code with plots
*i.e. adjusting code lines randomly until they work
**was actually reason to figure out this solution
I want to compute the distance between an arc and a point in a 3D space. All I found is the distance between a circle and a point link (which is either wrong, or where I made a mistake, as I get wrong values):
P = np.array([1,0,1])
center = np.array([0,0,0])
radius = 1
n2 = np.array([0,0,1])
Delta = P-center
dist_tmp = np.sqrt( (n2*Delta)**2 + (np.abs(np.cross(n2, Delta))-radius)**2 )
dist = np.linalg.norm(dist_tmp)
I have a circle in the x-y-plane with origin at x-y-z = 0 and radius = 1. The point of interest is in distance 1 above the circle. The result of the distance from the code is 1.73.. and not 1.
What is the right equation for point-circle distance?
How can I extend it to point-arc distance?
You have several errors in your code. Here is the answer to your first question.
First, you try to implement the dot product of n2 and Delta as n2*Delta, but that is not what the multiplication of 2 np arrays does. Use np.dot() instead. Next, you try to take the "absolute value" (magnitude) of a vector with np.abs, but that latter is for real and complex numbers only. One way to get the vector magnitude is np.linalg.norm(). Changing those gives you the proper answer, and you don't need the calculation you used for variable dist. So use
Delta = P-center
dist = np.sqrt(np.dot(n2, Delta)**2 + (np.linalg.norm(np.cross(n2, Delta))- radius)**2)
That gives the proper answer for dist, 1.0.