Related
I can't quite figure this one out.
I am trying to approximate the location (latitude / longitude) of a beacon based on 3 distance measurements from 3 fixed locations. However the distance readings available may have an error of up to 1 km.
Similar questions regarding trilateration have been asked here (with precise measurements), here, here (distance measurement errors in Java, but not in lat/lon coordinates and no answers) as well as others. I also managed to dig up this paper dealing with imperfect measurement data, however it for one assumes a cartesian coordinate system and is also rather mathematical than close to a usable implementation.
So none of above links and answers are really applicable to the following problem:
All available distance measurements are approximated in km (where data most frequently contains readings in-between 1 km and 100 km, in case this matters)
Measurement errors of up to 1 km are possible.
3 distance measurements are performed based on 3 fixed (latitude / longitude known) positions.
target approximation should also be a latitude / longitude combination.
So far I have adapted this Answer to C#, however I noticed that due to the measurement inaccuracies this algorithm does not work (as the algorithm assumes the 3 distance-circles to perfectly intersect with each other):
public static class Trilateration
{
public static GeoLocation Compute(DistanceReading point1, DistanceReading point2, DistanceReading point3)
{
// not my code :P
// assuming elevation = 0
const double earthR = 6371d;
//using authalic sphere
//if using an ellipsoid this step is slightly different
//Convert geodetic Lat/Long to ECEF xyz
// 1. Convert Lat/Long to radians
// 2d. Convert Lat/Long(radians) to ECEF
double xA = earthR * (Math.Cos(Radians(point1.GeoLocation.Latitude)) * Math.Cos(Radians(point1.GeoLocation.Longitude)));
double yA = earthR * (Math.Cos(Radians(point1.GeoLocation.Latitude)) * Math.Sin(Radians(point1.GeoLocation.Longitude)));
double zA = earthR * Math.Sin(Radians(point1.GeoLocation.Latitude));
double xB = earthR * (Math.Cos(Radians(point2.GeoLocation.Latitude)) * Math.Cos(Radians(point2.GeoLocation.Longitude)));
double yB = earthR * (Math.Cos(Radians(point2.GeoLocation.Latitude)) * Math.Sin(Radians(point2.GeoLocation.Longitude)));
double zB = earthR * (Math.Sin(Radians(point2.GeoLocation.Latitude)));
double xC = earthR * (Math.Cos(Radians(point3.GeoLocation.Latitude)) * Math.Cos(Radians(point3.GeoLocation.Longitude)));
double yC = earthR * (Math.Cos(Radians(point3.GeoLocation.Latitude)) * Math.Sin(Radians(point3.GeoLocation.Longitude)));
double zC = earthR * Math.Sin(Radians(point3.GeoLocation.Latitude));
// a 64 bit Vector3 implementation :)
Vector3_64 P1 = new(xA, yA, zA);
Vector3_64 P2 = new(xB, yB, zB);
Vector3_64 P3 = new(xC, yC, zC);
//from wikipedia
//transform to get circle 1 at origin
//ransform to get circle 2d on x axis
Vector3_64 ex = (P2 - P1).Normalize();
double i = Vector3_64.Dot(ex, P3 - P1);
Vector3_64 ey = (P3 - P1 - i * ex).Normalize();
Vector3_64 ez = Vector3_64.Cross(ex, ey);
double d = (P2 - P1).Length;
double j = Vector3_64.Dot(ey, P3 - P1);
//from wikipedia
//plug and chug using above values
double x = (Math.Pow(point1.DistanceKm, 2d) - Math.Pow(point2.DistanceKm, 2d) + Math.Pow(d, 2d)) / (2d * d);
double y = ((Math.Pow(point1.DistanceKm, 2d) - Math.Pow(point3.DistanceKm, 2d) + Math.Pow(i, 2d) + Math.Pow(j, 2d)) / (2d * j)) - ((i / j) * x);
// only one case shown here
double z = Math.Sqrt(Math.Pow(point1.DistanceKm, 2d) - Math.Pow(x, 2d) - Math.Pow(y, 2d));
//triPt is a vector with ECEF x,y,z of trilateration point
Vector3_64 triPt = P1 + x * ex + y * ey + z * ez;
//convert back to lat/long from ECEF
//convert to degrees
double lat = Degrees(Math.Asin(triPt.Z / earthR));
double lon = Degrees(Math.Atan2(triPt.Y, triPt.X));
return new GeoLocation(lat, lon);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static double Radians(double degrees) =>
degrees * Math.Tau / 360d;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static double Degrees(double radians) =>
radians * 360d / Math.Tau;
}
Above code most often than not does not work in my case, and instead only returns "Not a number" as it tries to take the square root of a negative number when calculating the final z value (due to measurement inaccuracies).
In my case measurements may return data like this (visualized with some random online tool):
where only 2 or even none of the distance circles intersect:
What I am looking for is the an algorithm returning the best possible approximation of the target location based on three distance measurements with a known maximum error of 1 km or further approaches I could take.
I have also thought of iterating over points on the circles to then determining the minimum average distance to all the points on the other circles but the 3-dimensional sphere geometry of the earth is giving me a headache. Also there's probably a way better and simpler approach to this which I just can't figure out right now.
As this is more of an algorithmic problem, rather than any language-specific thing, I appreciate any help in whatever programming language, pseudo code or natural language.
If you have access to a scientific computing library which provides non-linear optimization utilities, then you could try finding the point which minimizes the following:
(||x - p_1|| - r_1)^2 + (||x - p_2|| - r_2)^2 + (||x - p_3|| - r_3)^2 + (||x - p_earth|| - r_earth)^2
where p_i is the location (in Cartesian coordinates) of the ith location you measure from, r_i is the corresponding distance reading, p_earth is the location of the Earth, r_earth is the radius of the earth, and ||a|| denotes the norm/length of the vector a.
Each term in the expression is trying to minimize the residual radius error.
This can of course be modified to suit your needs - e.g. if constrained optimization is available, you could encode the requirement that the point be on the surface on the earth as a constraint rather than a term to optimize for. If spherical earth model isn't accurate enough, you could define an error from the Earth's surface, or just project your result onto the Earth if that is accurate enough.
I was doing one of the examples on the p5.js website - https://p5js.org/examples/form-regular-polygon.html. I got really confused by the way rotate function worked in that code . IN the below function if I just pass rotate(frameCount) , in the browser it shows me rotating two triangles intersected within forming a star , but as soon as I divide the frameCount it disappears. Also the equation used in the code - can some one give the mathematical intuition on how we reached to this point.
let sx = x + cos(a) * radius;
let sy = y + sin(a) * radius;
push();
translate(width * 0.2, height * 0.5);
rotate(frameCount / 50);
polygon(0,0,82,3);
pop();
Regarding "two triangles intersected within forming a star":
By default, the rotate function takes radians. When you do rotate(frameCount), you are increasing the angle by 1 radian at each frame. One radian equals about 57 degrees, so your triangle would rotate about 57 degrees at each frame. At frame 3, the triangle would have rotated about 120 degrees, and it would roughly overlap with the triangle at frame 1. Similarly, the triangle at frame 4 would roughly overlap with the triangle at frame 2.
The "two triangles" you are seeing is just two groups of triangles, one group being triangles at frame 1, 3, 5... and another group being triangles at frame 2, 4, 6...
That is why you should divide frameCount by some number if you would like to obtain a rather continuous rotation. Alternatively, you could also set angleMode to DEGREES. In that case, you don't have to divide frameCount anymore because at each frame the triangle would only rotate 1 degree instead of 1 radian.
Regarding the math formula:
In fact, the function used in that example should be called regularPolygon instead of polygon because it only draws regular polygons.
Now, how do you draw a regular polygon? You know the distance from each vertex to the center is a constant number. In this example, that number is the radius variable. And you know if you use polar coordinates with the center of the polygon as the origin point, the angle difference between every two adjacent vertices is also a constant number. In this example, that number is the angle variable.
More precisely, the polar coordinates of the vertices should take the form of:
v1 = (radius, 0)
v2 = (radius, angle)
v3 = (radius, angle*2)
...
Convert them to cartesian coordinates, you would obtain something like:
v1 = (cos(0) * radius, sin(0) * radius)
v2 = (cos(angle) * radius, sin(angle) * radius)
v3 = (cos(angle*2) * radius, sin(angle*2) * radius)
...
But what if the center of the polygon is not the origin point, but point (x, y), as in the example? Now the cartesian coordinates of the vertices become:
v1 = (x + cos(0) * radius, y + sin(0) * radius)
v2 = (x + cos(angle) * radius, y + sin(angle) * radius)
v3 = (x + cos(angle*2) * radius, y + sin(angle*2) * radius)
So when you do:
for (let a = 0; a < TWO_PI; a += angle) {
let sx = x + cos(a) * radius;
let sy = y + sin(a) * radius;
vertex(sx, sy);
}
You are really drawing the vertices v1, v2, v3....
I have two known Google Geolocation points A and B. I need to return GeoLocation point C which is on AB line and on distance x from point A:
Geolocation returnGeolocationC(Geolocation A, Geolocation B, double x) {
...
return C;
}
I know that I can use Haversine formula and I can calculate AB distance and therefore I also have AC and CB distance. Any idea or hint how to implement this?
Edit: Line is straight, no need to consider roads.
Well, this is a good problem which solution will depend on the area of interest, for instance:
Consider the situation faced by a botanist studying a stand of oak trees on a small plot of land. One component of the data analysis involves determining the location of these trees and calculating the distance betwee
n them. In this situation, straight line or Euclidean distance is the most logical choice. This only requires the use of the Pythagorean Theorem to calculate the shortest distance between two points:
straight_line_distance = sqrt ( ( x2 - x1 )**2 + ( y2 - y1 )**2 );
The variables x and y refer to co-ordinates in a two-dimensional plane and can reflect any unit of measurement, such as feet or miles.
Consider a different situation, an urban area, where the objective is to calculate the distance between customers’ homes and various retail outlets. In this situation, distance takes on a more specific meaning, usually road distance, making straight line distance less suitable. Since streets in many cities are based on a grid system, the typical trip may be approximated by what is known as the Manhattan, city block or taxi cab distance (Fothering-
ham, 2002):
block_distance = ( abs( x2 - x1 ) + abs( y2 - y1 ) ) ;
Instead of the hypotenuse of the right-angled triangle that was calculated for the straight line distance, the above formula simply adds the two sides that form the right angle. The straight line and city block formulae are closely related, and can be generalized by what are referred to as the Minkowski metrics, which in this case are restricted to two dimensions:
minkowski_metric = ( abs(x2 - x1)**k + abs(y2 - y1)**k )**(1/k);
The advantage of this formula is that you only need to vary the exponent to get a range of distance measures. When k = 1, it is equivalent to the city block distance; when k=2, it is the Euclidean distance. Less commonly,
other values of k may be used if desired, usually between 1 and 2. In some situations, it may have been determined that actual distances were greater than the straight line, but less than the city block, in which case a value such as "1.4" may be more appropriate. One of the interesting features of the Minkowski metric is that for values considerably larger than 2 (approaching infinity), the distance is the larger of two sides used in the city block calculation, although this is typically not applicable in a geographic context.
So pseudocode would be something like the following:
distance2d (x1, y1, x2, y2, k)
(max( abs(x2 - x1), abs(y2 - y1) ) * (k > 2))
+
((abs(x2 - x1)**k + abs(y2 - y1)** k )**(1/ k)) * (1 <=k<=2)
end
If 1 <= k <=2, the basic Minkowski metric is applied, since (1 <= k <=2) resolves to 1 and (k > 2) resolves to 0. If k > 2, an alternate formula is applied, since computations become increasingly intensive for large values of k. This second formula is not really necessary, but is useful in demonstrating how modifications can be easily incorporated in distance measures.
The previous distance measures are based on the concept of distance in two dimensions. For small areas like cities or counties, this is a reasonable implification. For longer distances such as those that span larger countries
or continents, measures based on two dimensions are no longer appropriate, since they fail to account for the curvature of the earth. Consequently, global distance measures need to use the graticule, the co-ordinate system
comprised of latitude and longitude along with special formulae to calculate the distances. Lines of latitude run in an east to west direction either above or below the equator. Lines of longitude run north and south through the poles, often with the Prime Meridian (running through Greenwich, England) measured at 0°. Further details of latitude and longitude are available (Slocum et al., 2005). One issue with using latitude and longitude is that the co-ordinates may require some transformation and preparation before they are suitable to use in distance calculations. Coordinates are often expressed in the sexagesimal system (similar to time) of degrees, minutes, and seconds, in which each degree consists of 60 minutes and each
minute is 60 seconds. Furthermore, it is also necessary to provide and indication of the position relative to the equator (North or South) and the Prime Meridian (East or West). The full co-ordinates may take on a variety of formats; below is a typical example that corresponds approximately to the city of Philadelphia:
39° 55' 48" N 75° 12' 12" W
As you mentioned Harvesine, and also I am extending a lot, we can compare results using law of cosines and Harvesine, so pseudocode:
begin
ct = constant('pi')/180 ;
radius = 3959 ; /* 6371 km */
#Both latitude and longitude are in decimal degrees ;
lat1 = 36.12;
long1 = -86.67;
lat2 = 33.94;
long2 = -118.40 ;
#Law of Cosines ;
a = sin(lat1*ct) * sin(lat2*ct) ;
b = cos(lat1*ct) * cos(lat2*ct) * cos((long2-long1) *ct);
c = arcos(a + b) ;
d = radius * c ;
put 'Distance using Law of Cosines ' d
# Haversine ** ;
a2 = sin( ((lat2 - lat1)*ct)/2)**2 +
cos(lat1*ct) * cos(lat2*ct) * sin(((long2 - long1)*ct)/2)**2
c2 = 2 * arsin(min(1,sqrt(a2))) ;
d2 = radius * c2 ;
put 'Distance using Haversine formula =' d2
end
In addition to the constant that will be used to convert degrees to radians, the radius of the earth is required, which on average is equal to 6371 kilometres or 3959 miles. The Law of Cosines uses spherical geometry to
calculate the great circle distance for two points on the globe. The formula is analogous to the Law of Cosines for plane geometry, in which three connected great arcs correspond to the three sides of the triangle. The Haversine formula is mathematically equivalent to the Law of Cosines, but is often preferred since it is less sensitive to round-off error that can occur when measuring distances between points that are located very close tog
ether (Sinnott, 1984). With the Haversine, the error can occur for points that are on opposite sides of the earth, but this is usually less of a problem.
You can find a really easy formula at this link.
Since you have the distance from one of the points and not the fraction of the distance on the segment you can slightly modify the formula:
A=sin(d-x)/sin(d)
B=sin(x)/sin(d)
x = A*cos(lat1)*cos(lon1) + B*cos(lat2)*cos(lon2)
y = A*cos(lat1)*sin(lon1) + B*cos(lat2)*sin(lon2)
z = A*sin(lat1) + B*sin(lat2)
lat=atan2(z,sqrt(x^2+y^2))
lon=atan2(y,x)
where x is the required distance and d is the distance between A and B (that you can evaluate with Haversine), both divided by the Earth radius.
You can also use another formula for sin(d):
nx = cos(lat1)*sin(lon1)*sin(lat2) - sin(lat1)* cos(lat2)*sin(lon2)
ny = -cos(lat1)*cos(lon1)*sin(lat2) + sin(lat1)* cos(lat2)*cos(lon2)
nz = cos(lat1)*cos(lon1)*cos(lat2)*sin(lon2) - cos(lat1)*sin(lon1)*cos(lat2)*cos(lon2)
sind = sqrt(nx^2+ny^2+nz^2)
It's more complex than the Haversine formula, but you can memoize some of the factors in the two steps.
As the OP posted a non working Java implementation, this is my corrections to make it work.
private static GpsLocation CalcGeolocationWithDistance(GpsLocation pointA, GpsLocation pointB, double distanceFromA)
{ //distanceFromA = 2.0 km, PointA and PointB are in Europe on 4.0km distance.
double earthRadius = 6371000.0;
double distanceAB = CalcDistance(pointA.Latitude, pointA.Longitude, pointB.Latitude, pointB.Longitude);
//distance AB is calculated right according to Google Maps (4.0 km)
double a = Math.Sin((distanceAB - distanceFromA) / earthRadius) / Math.Sin(distanceAB / earthRadius);
double b = Math.Sin(distanceFromA / earthRadius) / Math.Sin(distanceAB / earthRadius);
double x = a * Math.Cos(pointA.Latitude * Math.PI / 180) * Math.Cos(pointA.Longitude * Math.PI / 180) + b * Math.Cos(pointB.Latitude * Math.PI / 180) * Math.Cos(pointB.Longitude * Math.PI / 180);
double y = a * Math.Cos(pointA.Latitude * Math.PI / 180) * Math.Sin(pointA.Longitude * Math.PI / 180) + b * Math.Cos(pointB.Latitude * Math.PI / 180) * Math.Sin(pointB.Longitude * Math.PI / 180);
double z = a * Math.Sin(pointA.Latitude * Math.PI / 180) + b * Math.Sin(pointB.Latitude * Math.PI / 180);
double lat = Math.Atan2(z, Math.Sqrt(x * x + y * y)) * 180 / Math.PI;
double lon = Math.Atan2(y, x) * 180 / Math.PI;
//lat and lon are mo more placed somewhere in Africa ;)
return new GpsLocation(lat, lon);
}
The Question
Okay so basically what I'm trying to do, is calculating the Y location on a Cubic Curve / Bezier Curve / Spline when the X location is given.
I've searched everywhere on Stack Overflow and Google and I could find anything that actually worked!
The Curve Points
x1 = 50d;
y1 = 400d / 2d + 100d;
x2 = 400d;
y2 = 400d / 2d + 100d;
x3 = 600d - 400d;
y3 = 400d / 2d - 100d;
x4 = 600d - 50d;
y4 = 400d / 2d - 100d;
The reason that I calculate "600 - 400" and I don't just write "200" is because in my code "600" is actually the width of the window, which the Cubic Curve is rendered in. Thereby it actually says "width - 400" in my code.
So the following code after, can calculate the X & Y on a Cubic Curve when T is given!
t = 0.5d;
cx = 3d * (x2 - x1);
cy = 3d * (y2 - y1);
bx = 3d * (x3 - x2) - cx;
by = 3d * (y3 - y2) - cy;
ax = x4 - x1 - cx - bx;
ay = y4 - y1 - cy - by;
point_x = ax * (t * t * t) + bx * (t * t) + cx * t + x1;
point_y = ay * (t * t * t) + by * (t * t) + cy * t + y1;
So again, what I'm trying to calculate is the Y location of a Curve when you know an X location. But the only thing that I'm able to calculate is both an X & Y location on the Curve when T is given.
This is my first post, so if something isn't written 100% correctly, I apologize!
A cubic curve can have multiple 'y' values for an 'x' value, so you're going to have to perform root finding, after rotating the cubic curve so that it's aligned with the x/y axes. http://pomax.github.io/bezierinfo/#intersections covers this concept, but the idea is this:
take your curve, defined by points {p1,p2,p3,p4}, and take your line at x=X, defined by points {p5,p6} (where p5 is some coordinate (x,...) and p6 is another coordinate(x,...). The important part is that it's a vertical line, so both points have the same x value).
translate and rotate your cubic curve and your line, together, so that your line becomes horizontal, at new height y = 0.
you can now perform cubic root finding on the curve's y function. This may generate 0, 1, 2, or 3 distinct t values.
for each of these t values: in your normal, unrotated curve, compute the (x,y) coordinate given that t value. You will now have all the y values for a given x (all points will have the same x value, too).
In the article linked, have a look at the source for the cubic curve/line example, and to see how the rotation/rootfinding works, see the BezierCurve align(Point start, Point end) {... and float[] findAllRoots(int derivative, float[] values) {... functions. (especially note that after rotation, we're only finding the roots for the y-function! the x-function has become irrelevant for what we want to do)
If your formulas are correct, theoretically one might use Formula for cubic function roots to express t dependency from x. Then from x you find t, from t you find y.
For a square grid the euclidean distance between tile A and B is:
distance = sqrt(sqr(x1-x2)) + sqr(y1-y2))
For an actor constrained to move along a square grid, the Manhattan Distance is a better measure of actual distance we must travel:
manhattanDistance = abs(x1-x2) + abs(y1-y2))
How do I get the manhattan distance between two tiles in a hexagonal grid as illustrated with the red and blue lines below?
I once set up a hexagonal coordinate system in a game so that the y-axis was at a 60-degree angle to the x-axis. This avoids the odd-even row distinction.
(source: althenia.net)
The distance in this coordinate system is:
dx = x1 - x0
dy = y1 - y0
if sign(dx) == sign(dy)
abs(dx + dy)
else
max(abs(dx), abs(dy))
You can convert (x', y) from your coordinate system to (x, y) in this one using:
x = x' - floor(y/2)
So dx becomes:
dx = x1' - x0' - floor(y1/2) + floor(y0/2)
Careful with rounding when implementing this using integer division. In C for int y floor(y/2) is (y%2 ? y-1 : y)/2.
I assume that you want the Euclidean distance in the plane between the centers of two tiles that are identified as you showed in the figure. I think this can be derived from the figure. For any x and y, the vector from the center of tile (x, y) to the center of tile (x + dx, y) is (dx, 0). The vector from the center of tile (x, y) and (x, y + dy) is (-dy / 2, dy*sqrt(3) / 2). A simple vector addition gives a vector of (dx - (dy / 2), dy * sqrt(3) / 2) between (x, y) and (x + dx, y + dy) for any x, y, dx, and dy. The total distance is then the norm of the vector: sqrt((dx - (dy / 2)) ^ 2 + 3 * dy * dy / 4)
If you want the straight-line distance:
double dy = y2 - y1;
double dx = x2 - x1;
// if the height is odd
if ((int)dy & 1){
// whether the upper x coord is displaced left or right
// depends on whether the y1 coordinate is odd
dx += ((y1 & 1) ? -0.5 : 0.5);
}
double dis = sqrt(dx*dx + dy*dy);
What I'm trying to say is, if dy is even, it's just a rectangular space. If dy is odd, the position of the upper right corner is 1/2 unit to the left or to the right.
A straight forward answer for this question is not possible. The answer of this question is very much related to how you organize your tiles in the memory. I use odd-q vertical layout and with the following matlab code gives me the right answer always.
function f = offset_distance(x1,y1,x2,y2)
ac = offset_to_cube(x1,y1);
bc = offset_to_cube(x2,y2);
f = cube_distance(ac, bc);
end
function f = offset_to_cube(row,col)
%x = col - (row - (row&1)) / 2;
x = col - (row - mod(row,2)) / 2;
z = row;
y = -x-z;
f = [x,z,y];
end
function f= cube_distance(p1,p2)
a = abs( p1(1,1) - p2(1,1));
b = abs( p1(1,2) - p2(1,2));
c = abs( p1(1,3) - p2(1,3));
f = max([a,b,c]);
end
Here is a matlab testing code
sx = 6;
sy = 1;
for i = 0:7
for j = 0:5
k = offset_distance(sx,sy,i,j);
disp(['(',num2str(sx),',',num2str(sy),')->(',num2str(i),',',num2str(j),')=',num2str(k)])
end
end
For mathematical details of this solution visit: http://www.redblobgames.com/grids/hexagons/ . You can get a full hextile library at: http://www.redblobgames.com/grids/hexagons/implementation.html
This sounds like a job for the Bresenham line algorithm. You can use that to count the number of segments to get from A to B, and that will tell you the path distance.
If you define the different hexagons as a graph, you can get the shortest path from node A to node B. Since the distance from the hexagon centers is constant, set that as the edge weight.
This will probably be inefficient for large fields though.