Intermediate points between 2 geographic coordinates - c#

I am trying to develop an algorithm that involves normalizing GPS coordinates (latitude/longitude). That means, that being given two points A (lat1,lon1) and B(lat2,lon2) I would like to insert a point C that is linear with AB (same arc) and is placed at a specific distance from A and B (eg: A to B distance is 0.5km and I want point C to be at 0.1 km from A, on the AB arc). How can I calculate the coordinates for point C?
For the purpose given, it is enough to approximate Earth as a perfect spherical object.
I have found this article, but it gives the formula for midpoint only (and I don't fully understand it, in order to adapt).
midpoint between two latitude and longitude
Thank you.
Edit: I tried this but it gives wrong answers
public static void normalizedPoint(double lat1, double lon1, double lat2, double lon2, double dist){
double constant=Math.PI/180;
double angular = dist/6371;
double a = Math.Sin( 0* angular )/Math.Sin(angular);
double b = Math.Sin(1*angular)/Math.Sin(angular);
double x = a * Math.Cos(lat1) * Math.Cos(lon1) + b * Math.Cos(lat2) * Math.Cos(lon2);
double y = a * Math.Cos(lat1) * Math.Sin(lon1) + b * Math.Cos(lat2) * Math.Sin(lon2);
double z = a * Math.Sin(lat1) + b * Math.Sin (lon2);
double lat3 = Math.Atan2(z, Math.Sqrt( x*x + y*y ));
double lon3 = Math.Atan2(y, x);
Console.WriteLine(lat3/constant + " " + lon3/constant );
}
As far as I understood the original formulas this should return one of the 2 original points, but it does not(because the fraction used is 1). Also the variable dist is the distance from the 2 points and is properly calculated (checked with the same website).
Edit 2: I am providing as inputs coordinates for 2 geographic points (lat1, lon1, lat2 lon2) and the distance between them. I'm trying to get an intermediary point (lat3,lon3).

As I point out in an answer on the linked to question, you need to change all of your inputs to use radians rather than degrees.
I believe you also had an error for z where you used lon2 rather than lat2.
With those corrections, I get the answer you're seeking:
public static void normalizedPoint(double lat1, double lon1,
double lat2, double lon2,
double dist)
{
double constant = Math.PI / 180;
double angular = dist / 6371;
double a = Math.Sin(0 * angular) / Math.Sin(angular);
double b = Math.Sin(1 * angular) / Math.Sin(angular);
double x = a * Math.Cos(lat1* constant) * Math.Cos(lon1* constant) +
b * Math.Cos(lat2* constant) * Math.Cos(lon2* constant);
double y = a * Math.Cos(lat1* constant) * Math.Sin(lon1* constant) +
b * Math.Cos(lat2* constant) * Math.Sin(lon2* constant);
double z = a * Math.Sin(lat1* constant) + b * Math.Sin(lat2* constant);
double lat3 = Math.Atan2(z, Math.Sqrt(x * x + y * y));
double lon3 = Math.Atan2(y, x);
Console.WriteLine(lat3 / constant + " " + lon3 / constant);
}
Of course, the above can be vastly simplified by only converting angles ones, avoiding repeated calculations of the same Sin/Cos values, etc.
Calling:
normalizedPoint(47.20761, 27.02185, 47.20754, 27.02177, 1);
I get the output:
47.20754 27.02177

Not sure if the original author found some answer, but since I had similar problem and developed working solution, I think it would be good to post it here.
The Problem
Having two geographical points, A and B, find intermediate point C which lies exactly on the direct way from A to B and is N kilometers far from A (where N is less than distance between A and B, otherwise C = B).
My Context
I was developing small pet project based on microservices architecture. The idea was to launch missile from given deployment platform (point A) to chosen target location (point B). I had to create some kind of simulator that sends some messages about current missile Geo location after it is launched, so I had to find those intermediate points between A and B somehow.
Solution Context
Eventually, I developed C# based solution based on this great web page - https://www.movable-type.co.uk/scripts/latlong.html.
That web page has all the explanations, formulas and JavaScript code at the bottom. If you are not familiar with the C#, you can use their JavaScript implementation.
My C# Implementation
Your input is Location A, Location B and the distance.
You need to find bearing from A to B (see 'Bearing' section on that site)
You need to find position C from A having bearing (see 'Destination point given distance and bearing from start point' on that site)
The Code
I have working solution as part of my pet project and it can be found here - https://github.com/kakarotto67/mlmc/blob/master/src/Services/MGCC.Api/ChipSimulation/CoordinatesHelper.cs.
(Since original class is subject to change in the future, you might need to refer to this gist - https://gist.github.com/kakarotto67/ef682bb5b3c8bd822c7f3cbce86ff372)
Usage
// 1. Find bearing between A and B
var bearing = CoordinatesHelper.FindInitialBearing(pointA, pointB);
// 2. Find intermediate point C having bearing (above) and any distance in km
var pointC = CoordinatesHelper.GetIntermediateLocation(pointA, bearing, distance);
I hope somebody will find this helpful.

def get_intermediate_point(lat1 , lon1 , lat2 , lon2 , d):
constant = np.pi / 180
R = 6371
φ1 = lat1 * constant
λ1 = lon1 * constant
φ2 = lat2 * constant
λ2 = lon2 * constant
y = np.sin(λ2-λ1) * np.cos(φ2);
x = np.cos(φ1)*np.sin(φ2) - np.sin(φ1)*np.cos(φ2)*np.cos(λ2-λ1)
θ = np.arctan2(y, x)
brng = (θ*180/np.pi + 360) % 360; #in degrees
brng = brng * constant
φ3 = np.arcsin( np.sin(φ1)*np.cos(d/R ) + np.cos(φ1)*np.sin(d/R )*np.cos(brng) )
λ3 = λ1 + np.arctan2(np.sin(brng)*np.sin(d/R )*np.cos(φ1), np.cos(d/R )-np.sin(φ1)*np.sin(φ2));
return φ3/constant , λ3/constant

Related

Calculate a 3D trajectory by start point, end point and height

I've already figured out how to make a 3D trajectory using a start point and an angle.
However, I am trying to make a trajectory from a start point, an end point, and a height.
I tried taking the approach of a parabola on a 2D plane in a 3D space. I calculated the Prabola's A, B, and C values as well as the plane it's on given 3 points on the Parabola.
However, I've had a few complications with this sort of calculation, I assume it has to do with the inability to properly calculate a Z-axis without a plane but I cannot tell.
Other than a 2D parabola on a plane google did not provide another possible answer and a 3D trajectory yields a formula using a start point, an angle, and a power multiplier.
Is there any way to calculate a 3D trajectory given the start point, end point, and height?
Appreciating your help
Edit:
My code to calculate a parabola using 3 points (in case someone would like to know how I've done that and perhaps fix what I've done wrong)
public Parabola(Vector3 pa, Vector3 pb, Vector3 pc)
{
this.pa = pa;
this.pc = pc;
float a1 = -pa.x * pa.x + pb.x * pb.x, b1 = -pa.x + pb.x, c1 = -pa.y + pb.y;
float a2 = -pb.x * pb.x + pc.x * pc.x, b2 = -pb.x + pc.x, c2 = -pb.y + pc.y;
float bm = -(b2 / b1), a3 = bm * a1 + a2, c3 = bm * c1 + c2;
float a = c3 / a3, b = (c1 - a1 * a) / b1, c = pa.y - a * pa.x * pa.x - b * pa.x;
this.a = a; this.b = b; this.c = c;
plane = Vector3.Cross(pb - pa, pc - pa);
}
public Vector3 GetPoint(float x)
{
float angle = Mathf.Atan2(pc.z - pa.z, pc.x - pa.x) * Mathf.Rad2Deg;
float xs = Mathf.Cos(angle * Mathf.Deg2Rad) * x, zs = Mathf.Sin(angle * Mathf.Deg2Rad) * x;
return new Vector3(xs, a * x * x + b * x + c, zs);
}
public Vector3 ProjectOn(float x) => Vector3.ProjectOnPlane(GetPoint(x), plane);
The result looks ok when it's only on 2 Axis, but not 3.
here are 2 images for demonstration:
Looking at the second image, the parabola seems to be correct aside from being scaled incorrectly. Let's take a look at your code.
public Vector3 GetPoint(float x)
{
float angle = Mathf.Atan2(pc.z - pa.z, pc.x - pa.x) * Mathf.Rad2Deg;
float xs = Mathf.Cos(angle * Mathf.Deg2Rad) * x, zs = Mathf.Sin(angle * Mathf.Deg2Rad) * x;
return new Vector3(xs, a * x * x + b * x + c, zs);
}
I'm making a lot of assumptions here, but it seems like x is meant as a value that goes from 0.0 to 1.0, representing the start and end of the parabola, respectively. If so, you are determining the X and Z coordinates of this point based exclusively on the sine/cosine of the angle and x. This means that the values xs and zs should only ever be able to be between -1 and 1, limiting yourself to the confines of the unit circle.
The values xs and zs look like they need to be scaled by a factor s calculated by measuring the 2D distance of the start and end points when projected onto the XZ plane. This should stretch the parabola just enough to reach the end point.
I found an answer, but it's kinda a workaround.
Before messing around with Parabolas in 3D, I messed around with linear equations in 3D.
Unlike parabolas, lines have a defined equation even in 3D(Pn = P0 + t x V)(Pn vector containing XYZ, P0 initial point containing XYZ, t float, V Vector3)
In addition, there's only ONE line that goes through 2 points, even in 3D.
I used that to make a trajectory that's made out of 2 points and a height.
I make a new point in the center of those two points and add the height value to the highest Y value of the points, thus creating an Apex.
then I use the same calculations as before to calculate the A, B, and C values that
a parabola with those 3 points would have had.
I made a method that takes in an X value and returns a Vector3 containing the point this X is on a linear equation, but instead, changing the vector's Y value based on the parabola's equation.
Practically creating an elevated line, I made something that looks and behaves perfectly like a 3D parabola.
If you're in C#, here is the code(images):
FIX!!
in the Linear's GetX(float x) method.
it should be:
public Vector3 GetX(float x) => => r0 + (x - r0.x)/v.x * v;
I made a slight mistake in the calculations which I noticed immediately and changed.

Weird sign issue with mapping from cartesian to bispherical coordinates

I want to map cartesian coordinates (x,y,z) to bispherical coordinates (sigma,tau,phi) and back accourding to wikipedia.
Here are my functions:
public static Vector3 ProjectBisphericalToCartesian(Vector3 bispherical, double a)
{
var sigma = (double)bispherical.X;
var tau = (double)bispherical.Y;
var phi = (double)bispherical.Z;
var d = Math.Cosh(tau) - Math.Cos(sigma);
var s = a / d;
var x = s * Math.Sin(sigma) * Math.Cos(phi);
var y = s * Math.Sin(sigma) * Math.Sin(phi);
var z = s * Math.Sinh(tau);
return new Vector3(
(float)x,
(float)y,
(float)z
);
}
public static Vector3 ProjectCartesianToBispherical(Vector3 cartesian, double a)
{
var x = (double)cartesian.X;
var y = (double)cartesian.Y;
var z = (double)cartesian.Z;
var R = Math.Sqrt(x * x + y * y + z * z);
var s = R * R + a * a;
var t = 2.0 * a * z;
var Q = Math.Sqrt(s * s - t * t);
var sigma = Math.Acos((R * R - a * a) / Q);
var tau = Asinh(t / Q);
var phi = Math.Atan(y / x);
return new Vector3(
(float)sigma,
(float)tau,
(float)phi
);
}
// sinh^-1 ("areasinus hyperbolicus")
private static double Asinh(double x)
{
return Math.Log(x + Math.Sqrt(x * x + 1.0));
}
I test the function by generating random numbers for (x,y,z) and a, convert them to bispherical coordinates and back to cartesian and check whether or not they are equal to the original cartesian corrdinates (up to some small tolerance).
It turns out that some cartesian coordinates flip the sign of its x and y component (z is okay) while other's do not. For example {X:-5,3434 Y:2,569566 Z:-1,195607} flips the sign of the x and y component while {X:7,586471 Y:-6,154929 Z:1,494778} works fine.
I can not find an error in my code unfortunately.
The issue appears to be caused by negative x values in the original cartesian coordinates which will cause the sign of x and y to be flipped after conversion and back. I just don't see why that happens.
Does anybody have an idea what is going on here ?
Well, I don’t actually have the answer for you but maybe by putting our heads together we can do it.
First of all it looks to me like you did correctly transcribe the Wikipedia formulas. So what kind of a problem is this? Either the Math functions don’t do exactly the same thing as the functions referenced in Wikipedia, or else there is an error in Wikipedia.
At a guess I would not think that the methods in Math are differently defined. These functions have been around a long time and are very conventional. Maybe Wikipedia is wrong.
The type of error you have is a sign error. So you should look at what happens when the signs of one or two of the inputs changes. For example if we flip the sign of x then the sign of phi changes (it moves 180 degrees) in the conversion to bispherical. Correspondingly in the conversion to Cartesian, if we flip the sign of phi then the sign of x changes due to the cos(phi).
However if we flip the signs of both x and y then the sign of phi does not change. Neither do the other bispherical calculations change since they only use x and y within squaring. On the other hand if we change the sign of phi in the conversion to Cartesian, the signs of x and y do change. So there are two points in the Cartesian system that map to the same bispherical coordinates, while the two corresponding bisperical coordinates do map back to different Cartesian coordinates. It would seem that you need to introduce a sign in the conversion of x and y to phi. (Or, conceivably a sign in one of the other bispherical coordinates to make up for the lack of sign in phi.)
Okay, I got it. Wolfram was mentioning something about half-planes so it occurred to me that using Atan2(y, x) instead of Atan(y/x) could fix the issue and it did.

Find point C that forms perpendicular line BC to line AB

I have Line segment AB defined by two 2D points A,B.
What I am trying to do is find a point C, with distance d away from B.
The two constraints are that BC has to be perpendicular to AB and BC is always 90 degrees anti-clockwise relative to AB.
So far I have the following
double d = .01;
Coordinate C = new Coordinate(A.Lat - B.Lat, A.Long - B.Long);
C.Long = C.Long * A.Long * Math.Cos(90);
C.Lat = C.Lat * A.Lat * Math.Cos(90);
C.Long = A.Long + C.Long * d;
C.Lat = A.Lat + C.Lat * d;
Essentially what I am asking is, where am I going wrong with this? Is it the c# code? Is it the logic? What are the steps to solve for C using those two constraints.
Normalize AB vector and rotate it by 90 degrees:
ABY = B.Y - A.Y
ABX = B.X - A.X
Len = Sqrt(ABY*ABY + ABX*ABX)
C.X = B.X - d * ABY / Len
C.Y = B.Y + d * ABX / Len
Note that for geographic coordinates (Lat/Long) and large distances result is not exact.
Link for further reference (sections Bearing then Destination point given distance and bearing from start point)
MBo has the correct answer for your task (as you got 90 degrees turn) I just wanted to show you how to repair your own code (I deduced you wanted to do this) which is usable to any angular turn (but slower as it require goniometric):
d = .01;
a = atan2(B.y - A.y,B.x - A.x) (+/-) 90.0; // sign depends on your coordinate system
C.x = B.x + d*cos(a)
C.y = B.y + d*sin(a)
So you should obtain directional angle a of your AB and shift it by 90 deg. Then you just add d rotated by the a to the C which can be done by parametric circle equation.
Beware all the angles should be in units your goniometric functions accepts (so either degrees or radians) as I do not code in C# I have not a clue but in languages I code in it is usually in radians. In which case line:
a = atan2(B.y - A.y,B.x - A.x) (+/-) 90.0; // sign depends on your
Would change to:
a = atan2(B.y - A.y,B.x - A.x) (+/-) 90.0*Pi/180.0; // sign depends on your
Where Pi=3.1415926535897932384626433832795.

Interpolating Z values when given complete and incomplete XYZ pairs

I am building a windows form application that works with PolyLineZ (ESRI Shapefile) data and rewrites outlying Z values. The minimum and maximum Z-values are defined by the user through the interface
Let's take the following as an example, let's say the minimum is 0 and the maximum is 10:
XY Z
1,1 0
1,3 1
1,5 7
1,7 11*
1,10 10
The value with the 11 would need to be interpolated as it does not fall into the range defined by the user. This is a very simplified example obviously. Some PolyLines can be missing more values.
What I've done:
I've researched linear interpolation. Looked at example youtube videos, having a hard time wrapping my head around it.
What I need:
Code examples from either any language or an "English" explanation of the theory behind linear/bilinear/trilinear interpolation so that I can implement it into my program. My math skills aren't the greatest, so I have a hard time understanding wikipedias definition of it.
I'm also assuming that linear interpolation is what I need to research,
EDIT: Currently implementing the following, stop me if I'm wrong
I'm using what I think is Pythagorean Theory type approach. I haven't made it catch exceptions yet (ie, making sure the left point is actually left, make sure the list doesn't run out of bounds, etc), that can come later
internal static double calculateDistance(XYPoints a, XYPoints b)
{
double xd = b.X - a.X;
double yd = b.Y - a.Y;
return Math.Sqrt(xd * xd + yd * yd);
}
for (var i = 0; i < polylinez.ZPoints.Count;i++)
{
if (polylinez.ZPoints[i] > maxValue || (polylinez.ZPoints[i] < minValue))
{
//polylinez.ZPoints[i] = (((1 - polylinez.XYpoints[i].X) * polylinez.ZPoints[i - 1]) + (polylinez.XYpoints[i].X * polylinez.ZPoints[i + 1]));
double prevdistance = calculateDistance(polylinez.XYpoints[i - 1], polylinez.XYpoints[i]);
double nextdistance = calculateDistance(polylinez.XYpoints[i], polylinez.XYpoints[i + 1]);
double fraction = prevdistance / nextdistance;
double diffsBetweensZ = polylinez.ZPoints[i + 1] - polylinez.ZPoints[i - 1];
Console.WriteLine(polylinez.ZPoints[i - 1] + (diffsBetweensZ * fraction));
}
}
return polylinez;
It returns 9.12 as an answer for the above example table. This sounds about right to me. I checked my distance calculator method with sample data on the internet, and it seems to be doing the trick.
First step, create a routine for calculating distances:
internal static double calculateDistance(XYPoints a, XYPoints b)
{
double xd = b.X - a.X;
double yd = b.Y - a.Y;
return Math.Sqrt(xd * xd + yd * yd);
}
I changed the variable names to something more logical (my variable names were different)
//get distance frpm previous point to point in question
double prevdistance = calculateDistance(prevXYpoint, currentXYPoint);
//get distance frpm point in question to the next point
double nextdistance = calculateDistance(currentXYPoint, nextXYPoint);
//generate a ratio
double fraction = prevdistance / (nextdistance + prevdistance);
//find out the difference between the two known points
double diffsBetweensZ = nextZpointValue - prevZpointValue;
//interpolate!
double newZvalue = (prevZpointValue + (diffsBetweensZ * fraction));
I checked this on several sets of data and it's the most accurate thing I can find... what blows my mind is that I couldn't find any existing code anywhere to do this.

Find closest city to given location

I am trying to find the closest city to given location. I have stored location of some cities that I want to work with. And I have my location, but I dont know how to find the closest city to my location ?
Cities
New York - Lat 40.714353; Long -74.005973
Washington - Lat 38.895112; Long -77.036366
....more cities
My location
Philadephia - Lat 39.952335; Long -75.163789
So how should I compare the coords to find the closest city ? I am doing program in C# but just knowing the solution of algorythm is enaught for me :)
Thanks for any help
You should use your high school knowledge to solve this problem, your alghorithm is:
closest = sqrt ( (lat2 - lat1) ^2 + (Long2-Long1) ^2 )
now this give you your air distance.
so, when you do this for an array of values, you can use asort function to compare which one is closest to you.
Strictly, you'd want to use the Haversine formula.
However, while you could perhaps be just slightly out in far northern or far southern points, you could probably get by by pretending that Mercator projections are accurate for distance, and ignoring the curvature of the earth. This is especially true if you are going to have lots of cities, as the error is greater, the further points are from the target point. Hence you would just use Pythagoras':
relDist = √((xLat - yLat) × (xLat - yLat) + (xLng - yLng) × (xLng - yLng))
But since you only care about (and only get) a relative ordering, you can skip the square-root bit, which is the heaviest step:
relDist = (xLat - yLat) × (xLat - yLat) + (xLng - yLng) × (xLng - yLng)
As well as being faster in and of itself, it can also be reasonably preformed on integers, should you store your coordinates as multiples of the actual coordinate (e.g. storing New York's (40.664167, -73.938611) as the pair (406642, -739386). This can be a big boost if you want to quickly sort a large number of places in order of proximity to a given point.
If however you really care about precision in the face of the fact that the earth is round, then the following implements Haversine:
private const double radiusE = 6378135; // Equatorial radius
private const double radiusP = 6356750; // Polar radius
private const double radianConv = 180 / Math.PI;
public static double GetDistanceBetweenPoints(double lat1, double long1, double lat2, double long2)
{
double dLat = (lat2 - lat1) / radianConv;
double dLong = (long2 - long1) / radianConv;
double a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(lat2) * Math.Sin(dLong/2) * Math.Sin(dLong/2);
return Math.Sqrt((Math.Pow(radiusE * radiusP * Math.Cos(lat1 / radianConv), 2)) / (Math.Pow(radiusE * Math.Cos(lat1 / radianConv), 2) + Math.Pow(radiusP * Math.Sin(lat1 / radianConv), 2))) * (2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)));
}
The distance bitween two points (x1, y1) and (x2, y2) is
d = sqrt((x1 - x2) ^ 2 + (y1 - y2) ^ 2)
so in c# you we will have:
public City FindNearestCity(double currentLatitude, double currentLogitude, List<City> cities)
{
Dictionary<City, double> distances = new Dictionary<City, double>();
foreach (City city in cities)
{
double distance = Math.Sqrt(Math.Pow(city.latitude - currentLatitude, 2) + Math.Pow(city.Longitude - currentLogitude, 2));
distances.Add(city, distance);
}
double minimumDistance = distances.Min(distance => distance.Value);
return distances.First(distance => distance.Value == minimumDistance).Key;
}
Visit here
you can find two c# function using Brute force and divide-and-conquer algorithms to find the closest two points among a set of given points in two dimensions.
Jon's answer is very inspiring, although there're few missing pieces.
lat1 should be in a
double a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(lat1/ RadianConv) * Math.Cos(lat2/ RadianConv) * Math.Sin(dLong / 2) * Math.Sin(dLong / 2);
The simulated radius in last statement gave 2000ish sometimes, which should be close to either RadiusE or RadiusP, so I used mean radius instead.
return 6371* (2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)));

Categories