I am using the following function.
private static Location CoordinateAtADistance(double latOrigin, double lonOrigin, double radius, double angle)
{
double lonDestination;
double R = 6371.0;
double d = radius / R; // d = angular distance covered on earth's surface
double lat1 = ToRadian(latOrigin);
double lon1 = ToRadian(lonOrigin);
double brng = ToRadian(angle);
double latDestination = lat1 + d * Math.Cos(brng);
double dLat = d * Math.Cos(brng);
double dPhi = Math.Log(Math.Tan(latDestination / 2 + Math.PI / 4) / Math.Tan(lat1 / 2 + Math.PI / 4));
double q = (double.IsNaN(dLat / dPhi)) ? dLat / dPhi : Math.Cos(lat1); // E-W line gives dPhi=0
double dLon = d * Math.Sin(brng) / q;
// check for some daft bugger going past the pole
if (Math.Abs(latDestination) > Math.PI / 2)
latDestination = latDestination > 0 ? Math.PI - latDestination : -(Math.PI - latDestination);
lonDestination = (lon1 + dLon +3* Math.PI) % (2 * Math.PI) - Math.PI;
Location nextPoint = new Location();
if (angle == 0)
{
nextPoint.Latitude = ToDegree(latDestination);
nextPoint.Longitude = lonOrigin;
}
if (angle == 90)
{
nextPoint.Latitude = latOrigin;
nextPoint.Longitude = ToDegree(lonDestination);
}
return nextPoint;
}
Here radius is the distance.
Now the problem is when I calculate short distances e.g. a few hundreds kilometers it works perfectly. But for large distances say 11,000 kilometers it gives in correct longitude.
Please not I only move either along latitude or longitude so one of them will not change in any case. While moving for latitude I get correct answer but for longitude values are not even closer.
Please post comments if any thing is unclear.
double latDestination = lat1 + d * Math.Cos(brng);
double dLat = d * Math.Cos(brng);
double dPhi = Math.Log(Math.Tan(latDestination / 2 + Math.PI / 4) / Math.Tan(lat1 / 2 + Math.PI / 4));
double q = (double.IsNaN(dLat / dPhi)) ? dLat / dPhi : Math.Cos(lat1); // E-W line gives dPhi=0
double dLon = d * Math.Sin(brng) / q;
// check for some daft bugger going past the pole
if (Math.Abs(latDestination) > Math.PI / 2)
latDestination = latDestination > 0 ? Math.PI - latDestination : -(Math.PI - latDestination);
lonDestination = (lon1 + dLon + 3 * Math.PI) % (2 * Math.PI) - Math.PI;
A little correction was needed and the above formula works fine.
Related
I'm creating a distance calculator in c# using the haversine equation to calculate the distance between longitudes and latitudes but it is giving the wrong output can anyone see why?
the first long and lat values are for a place in Wales (Bangor) and the other is for a place in England (Manchester)
Here is the code:
using System;
public static class Program
{
static double toRadians(double angle)
{
return (angle * Math.PI) / 180;
}
static double CalcDistance(double lon1, double lon2, double lat1, double lat2)
{
lon1 = toRadians(lon1);
lon2 = toRadians(lon2);
lat1 = toRadians(lat1);
lat2 = toRadians(lat2);
//haversine formula
double dlat, dlon;
dlat = lat2 - lat1;
dlon = lon2 - lon1;
double a = Math.Pow(Math.Sin(dlat / 2), 2) *
Math.Cos(lat1) * Math.Cos(lat2) *
Math.Pow(Math.Sin(dlon / 2), 2);
double c = 2 * Math.Asin(Math.Sqrt(a));
// earths radius is KM, use 3956 for miles
double earthRadius = 6371;
return (c * earthRadius);
}
static void Main(String[] args)
{
double lat1, lat2, lon1, lon2;
lon1= 53.222469;
lat1 = -4.129424;
lon2 = 53.244697;
lat2 = -2.13195;
Console.WriteLine(CalcDistance(lon1, lon2, lat1, lat2) + " KM");
}
}
The output given is 0.04301075336978381 KM when the output should be roughly 130KM
The error is a * vs + (the first one in CalcDistance), but here's a direct conversion from https://www.movable-type.co.uk/scripts/latlong.html, for reference (also adding this to the static double toRadians(this double angle) so it works as an extension method):
static double CalcDistance(double lon1, double lon2, double lat1, double lat2)
{
const double R = 6371;
var φ1 = lat1.toRadians();
var φ2 = lat2.toRadians();
var Δφ = (lat2 - lat1).toRadians();
var Δλ = (lon2 - lon1).toRadians();
var a = Math.Sin(Δφ / 2) * Math.Sin(Δφ / 2) +
Math.Cos(φ1) * Math.Cos(φ2) *
Math.Sin(Δλ / 2) * Math.Sin(Δλ / 2);
var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
var d = R * c;
return d;
}
Let's say I have 2 coordinates that are ~222.33 meters away from each other :
A: 49.25818, -123.20626
B: 49.25813, -123.2032
Those 2 points makes a segment.
How can I calculate the coordinate of point Z that is X meters away from either A or B but towards the other point?
I already know the distance between my 2 points using System.Device.Location library.
GeoCoordinate A = new GeoCoordinate(49.25818, -123.20626);
GeoCoordinate B = new GeoCoordinate(49.25813, -123.2032);
var distanceInMeters = A.GetDistanceTo(B);
// distanceInMeters = 222.33039783713738
I'm looking for something like this:
GeoCoordinate GetPointTowards(GeoCoordinate fromPoint, GeoCoordinate towardPoint, double distanceInMeter) {
[???]
}
I think I may need the bearing or something to be able to get the new point location.
Most examples I've found are for iOS, Android or GMaps with specific libraries..
Here's an outline of how I would do it. With this approach, there is no need to explicitly deal with the difference in units between coordinates and distances because taking the ratio of target to total distance eliminates the unit.
totalDistance = distance in meters between point A and point B.
targetDistance = distance in meters to travel from point A to point B
ratio = targetDistance / totalDistance
diffX = B.X - A.X
diffY = B.Y - A.Y
targetX = A.X + (ratio * diffX)
targetY = A.Y + (ratio * diffY)
But this wouldn't handle the edge cases like being at 179 degrees longitude and adding 3 degrees which would put you at -178 longitude.
This is my code converted to C# from http://www.movable-type.co.uk/scripts/latlong.html. The fraction is from 0 to 1 and is the fraction along the distance from the first point to the second point the output position will be. You could always modify it to take a straight distance value.
public static (double Lat, double Lon) IntermediatePoint((double Lat, double Lon) StartPoint, (double Lat, double Lon) EndPoint, double fraction)
{
if (fraction < 0 || fraction > 1)
throw new ArgumentOutOfRangeException();
double angDist = Distance(StartPoint, EndPoint) / radius;
double lat1 = StartPoint.Lat * (Math.PI / 180);
double lon1 = StartPoint.Lon * (Math.PI / 180);
double lat2 = EndPoint.Lat * (Math.PI / 180);
double lon2 = EndPoint.Lon * (Math.PI / 180);
double a = Math.Sin((1 - fraction) * angDist) / Math.Sin(angDist);
double b = Math.Sin(fraction * angDist) / Math.Sin(angDist);
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(lat2);
double lat3 = Math.Atan2(z, Math.Sqrt(Math.Pow(x, 2) + Math.Pow(y, 2)));
double lon3 = Math.Atan2(y, x);
return (lat3 * (180 / Math.PI), lon3 * (180 / Math.PI));
}
public static double Distance((double Lat, double Lon) point1, (double Lat, double Lon) point2)
{
double φ1 = point1.Lat * (Math.PI / 180.0);
double φ2 = point2.Lat * (Math.PI / 180.0);
double Δφ = (point2.Lat - point1.Lat) * (Math.PI / 180.0);
double Δλ = (point2.Lon - point1.Lon) * (Math.PI / 180.0);
double a = Math.Sin(Δφ / 2) * Math.Sin(Δφ / 2) + Math.Cos(φ1) * Math.Cos(φ2) * Math.Sin(Δλ / 2) * Math.Sin(Δλ / 2);
double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
return radius * c;
}
radius is a constant representing the Earth's radius in meters.
I'm searching for an algorithm to convert Lambet72 coordinates to lat/long.
So for example Lambert72 coordinates 148990,169450 must be converted into 50.83546802746704, 4.354415218851164 but most of the algorithm have a little offset (see attachments).
Image 1
Image 2
This is one of the algorithms I found which is close, but still have an error.
Someone has got a better algorithm?
static void Lambert72ToLatLong(double x, double y, ref double longitude, ref double latitude)
{
const double n = 0.77164219;
const double F = 1.81329763;
const double thetaFudge = 0.00014204;
const double e = 0.08199189;
const double a = 6378388;
const double xDiff = 150000;
const double yDiff = 5400088.44;
const double theta0 = 0.07604294;
double xReal = xDiff-x;
double yReal = yDiff-y;
double rho = Math.Sqrt(xReal*xReal + yReal * yReal);
double theta = Math.Atan(xReal/-yReal);
longitude = (theta0 + (theta + thetaFudge) / n) * 180 / Math.PI;
latitude = 0;
for(int i=0; i<10; ++i)
{
latitude = (2 * Math.Atan(Math.Pow(F * a / rho, 1 / n) * Math.Pow((1 + e * Math.Sin(latitude)) / (1 - e * Math.Sin(latitude)), e / 2))) - Math.PI / 2;
}
latitude *= 180 / Math.PI;
}
It seems you are trying to convert Lambert72 (Belgium Dattum 72 LCC 3p) to
WGS84 GPS coordinates. Thus, after converting to spherical latitude and
longitude coordinates, an additional step has to be performed to end up
with the WGS84 coordinates. Here is an alternative code in C# with the full conversion
of Lambert72 to WGS74 which complies with the required decimal accurary, as
you easily can verify for the coordinates presented in your quoted images.
static void Lambert72toWGS84latlong(double X, double Y)
{
double LongRef = 0.076042943; //
double nLamb = 0.7716421928;
double aCarre = Math.Pow(6378388.0,2.0);
double bLamb = 6378388.0 * (1.0 - (1.0 / 297.0));
double eCarre = (aCarre - Math.Pow(bLamb, 2.0)) / aCarre;
double KLamb = 11565915.812935;
double eLamb = Math.Sqrt(eCarre);
double eSur2 = eLamb / 2.0;
double Tan1 = (X - 150000.012) / (5400088.437 - Y);
double Lambda = LongRef + (1.0 / nLamb) * (0.000142043 + Math.Atan(Tan1));
double RLamb = Math.Sqrt(Math.Pow((X - 150000.012) , 2.0) + Math.Pow ((5400088.437 - Y) ,2.0));
double TanZDemi = Math.Pow((RLamb / KLamb),(1.0 / nLamb));
double Lati1 = 2.0 * Math.Atan(TanZDemi);
double eSin;
double Mult1, Mult2, Mult;
double LatiN, Diff;
double lat, lng ;
int i=0;
do
{
eSin = eLamb * Math.Sin(Lati1);
Mult1 = 1.0 - eSin;
Mult2 = 1.0 + eSin;
Mult = Math.Pow((Mult1 / Mult2) , (eLamb / 2.0));
LatiN = (Math.PI / 2.0) - (2.0 * (Math.Atan(TanZDemi * Mult)));
Diff = LatiN - Lati1;
Lati1 = LatiN;
i++;
} while (Math.Abs(Diff)> 0.0000000277777);
lat=LatiN;
lng=Lambda;
double SinLat = Math.Sin(lat);
double SinLng = Math.Sin(lng);
double CoSinLat = Math.Cos(lat);
double CoSinLng = Math.Cos(lng);
double dx = -125.8;
double dy = 79.9;
double dz = -100.5;
double da = -251.0;
double df = -0.000014192702;
double LWf = 1.0 / 297.0;
double LWa = 6378388.0;
double LWb = (1 - LWf) * LWa;
double LWe2 = (2.0 * LWf) - (LWf * LWf);
double Adb = 1.0 / (1.0 - LWf);
double Rn = LWa / Math.Sqrt(1.0 - LWe2 * SinLat * SinLat);
double Rm = LWa * (1 - LWe2) /Math.Pow((1.0 - LWe2 * lat * lat) ,1.5);
double DLat = -dx * SinLat * CoSinLng - dy * SinLat * SinLng + dz * CoSinLat;
DLat = DLat + da * (Rn * LWe2 * SinLat * CoSinLat) / LWa;
DLat = DLat + df * (Rm * Adb + Rn / Adb) * SinLat * CoSinLat;
DLat = DLat / (Rm + 0.0);
double DLng = (-dx * SinLng + dy * CoSinLng) / ((Rn + 0.0) * CoSinLat);
double Dh = dx * CoSinLat * CoSinLng + dy * CoSinLat * SinLng + dz * SinLat;
Dh = Dh - da * LWa / Rn + df * Rn * lat * lat / Adb;
double LatWGS84 = ((lat + DLat) * 180.0) / Math.PI;
double LngWGS84 = ((lng + DLng) * 180.0) / Math.PI;
MessageBox.Show("WGS84-Latitude=" + LatWGS84.ToString("###.######") +
"--WGS84 Longitude=" + LngWGS84.ToString("###.######"));
}
Hope these are helpful.
You have to correct the values for
xDiff
and
yDiff
The correct values should be
xDiff=149910;
yDiff=5400150;
You have also to correct the
i<10
to become
i<5
inside the for-loop for the calculation of the latitude. Then you will get the desired results.
Hope these help!
I have a set of latitude Longitude points. If I wanted to test if a new point was within x metres of any of the existing points would this be possible?
Would it be best if I use this way?
ForEach(Coordinate coord in Coordinates)
{
var distance = GeoCoordinate.GetDistance(lat,lon);
if(distance <= x)
{
addToQualifyingList(coord);
}
}
and compare the new coordinate with every point in the set and check to see it is within x metres?
Here is a method to calculate the distance between 2 points (lat1, lon1 and lat2, lon2)
public enum DistanceUnit { miles, kilometers, nauticalmiles }
public double GetDistance( double lat1, double lon1 , double lat2 , double lon2, DistanceUnit unit)
{
Func<double, double> deg2rad = deg => (deg * Math.PI / 180.0);
Func<double, double> rad2deg = rad => (rad / Math.PI * 180.0);
double theta = lon1 - lon2;
double dist = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) + Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(deg2rad(theta));
dist = Math.Acos(dist);
dist = rad2deg(dist);
dist = dist * 60 * 1.1515;
if (unit == DistanceUnit.kilometers)
{
dist = dist * 1.609344;
}
else if (unit == DistanceUnit.nauticalmiles)
{
dist = dist * 0.8684;
}
return (dist);
}
To determine all Coordinates with distance below 1 kilometer:
List<Coordinate> result = Coordinates.Where(x => GeoCoordinate.GetDistance(lat,lon, x.lan, x.lon, DistanceUnit.kilometers) < 1).ToList();
Latitude and Longitude are both needed to know the position on earth or any other sphere shaped object. These are given in degree from their respective zero line / point.
http://www.movable-type.co.uk/scripts/latlong.html
Has a solution in Java
> var R = 6371e3; // metres
> var φ1 = lat1.toRadians();
> var φ2 = lat2.toRadians();
> var Δφ = (lat2-lat1).toRadians();
> var Δλ = (lon2-lon1).toRadians();
>
> var a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
> Math.cos(φ1) * Math.cos(φ2) *
> Math.sin(Δλ/2) * Math.sin(Δλ/2);
> var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
>
> var d = R * c;
Apparently, this is this is one of the best solutions for small distances.
You should be able to just copy and paste it to C#, youll jsut have to change the variable names.
i used this code which takes longitude and latitude of two different location and calculates the distance between them
my code is
protected void Button1_Click(object sender, EventArgs e)
{
double lat1= Convert.ToDouble(TextBox1.Text);
double lon1= Convert.ToDouble(TextBox2.Text);
double lat2= Convert.ToDouble(TextBox3.Text);
double lon2= Convert.ToDouble(TextBox4.Text);
var rlat1 = Math.PI * lat1/180;
var rlat2 = Math.PI * lat2/180;
var rlon1 = Math.PI * lon1/180;
var rlon2 = Math.PI * lon2 / 180;
var theta = lon1-lon2;
var rtheta = Math.PI * theta/180;
var dist = Math.Sign(rlat1) * Math.Sign(rlat2) + Math.Cos(rlat1) * Math.Cos(rlat2) * Math.Cos(rtheta);
dist = Math.Acos(dist);
dist = dist * 180/Math.PI;
dist = dist * 60 * 1.1515;
dist = dist * 1.609344 ;
TextBox5.Text = dist.ToString("0.######");
}
but for all the input values the result i am getting is NaN.please help me.
Ok, because I led you up the garden path with my previous answer, I ported this to give you an algorithm that works:
void Main()
{
double lat1=12.916933d,
lon1=77.562658d,
lat2=12.930140d,
lon2=77.587732d;
double dist = GetDistanceFromLatLonInKm(lat1, lon1, lat2, lon2);
// dist == 3.08890370651166 yay!
}
double GetDistanceFromLatLonInKm(double lat1,
double lon1,
double lat2,
double lon2) {
var R = 6371d; // Radius of the earth in km
var dLat = Deg2Rad(lat2 - lat1); // deg2rad below
var dLon = Deg2Rad(lon2 - lon1);
var a =
Math.Sin(dLat / 2d) * Math.Sin(dLat / 2d) +
Math.Cos(Deg2Rad(lat1)) * Math.Cos(Deg2Rad(lat2)) *
Math.Sin(dLon / 2d) * Math.Sin(dLon / 2d);
var c = 2d * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1d - a));
var d = R * c; // Distance in km
return d;
}
double Deg2Rad(double deg) {
return deg * (Math.PI / 180d);
}
Your calculation appears to work correctly when you supply fixed values:
var lat1 = 0d;
var lon1 = 52d;
var lat2 = 0d;
var lon2 = -52d;
which rather implies that these conversions are failing:
double lat1 = Convert.ToDouble(TextBox1.Text);
double lon1 = Convert.ToDouble(TextBox2.Text);
double lat2 = Convert.ToDouble(TextBox3.Text);
double lon2 = Convert.ToDouble(TextBox4.Text);
If you're still having trouble, place a breakpoint on these lines and take a look at the values of TextBox1.Text etc.
For more predictable parsing of floating point numbers, it's best to supply culture information:
Convert.ToDouble("1.2", System.Globalization.CultureInfo.InvariantCulture)
Check if decimal delimiter is valid. It has to be comma or dot, depending on your regional settings.