How to find distance from the latitude and longitude of two locations? - algorithm
I have a set of latitudes and longitudes of locations.
How to find distance from one location in the set to another?
Is there a formula ?
The Haversine formula assumes a spherical earth. However, the shape of the earh is more complex. An oblate spheroid model will give better results.
If such accuracy is needed, you should better use Vincenty inverse formula.
See http://en.wikipedia.org/wiki/Vincenty's_formulae for details. Using it, you can get a 0.5mm accuracy for the spheroid model.
There is no perfect formula, since the real shape of the earth is too complex to be expressed by a formula. Moreover, the shape of earth changes due to climate events (see http://www.nasa.gov/centers/goddard/earthandsun/earthshape.html), and also changes over time due to the rotation of the earth.
You should also note that the method above does not take altitudes into account, and assumes a sea-level oblate spheroid.
Edit 10-Jul-2010: I found out that there are rare situations for which Vincenty inverse formula does not converge to the declared accuracy. A better idea is to use GeographicLib (see http://sourceforge.net/projects/geographiclib/) which is also more accurate.
Here's one: http://www.movable-type.co.uk/scripts/latlong.html
Using Haversine formula:
R = earth’s radius (mean radius = 6,371km)
Δlat = lat2− lat1
Δlong = long2− long1
a = sin²(Δlat/2) + cos(lat1).cos(lat2).sin²(Δlong/2)
c = 2.atan2(√a, √(1−a))
d = R.c
Apply the Haversine formula to find the distance. See the C# code below to find the distance between 2 coordinates. Better still if you want to say find a list of stores within a certain radius, you could apply a WHERE clause in SQL or a LINQ filter in C# to it.
The formula here is in kilometres, you will have to change the relevant numbers and it will work for miles.
E.g: Convert 6371.392896 to miles.
DECLARE #radiusInKm AS FLOAT
DECLARE #lat2Compare AS FLOAT
DECLARE #long2Compare AS FLOAT
SET #radiusInKm = 5.000
SET #lat2Compare = insert_your_lat_to_compare_here
SET #long2Compare = insert_you_long_to_compare_here
SELECT * FROM insert_your_table_here WITH(NOLOCK)
WHERE (6371.392896*2*ATN2(SQRT((sin((radians(GeoLatitude - #lat2Compare)) / 2) * sin((radians(GeoLatitude - #lat2Compare)) / 2)) + (cos(radians(GeoLatitude)) * cos(radians(#lat2Compare)) * sin(radians(GeoLongitude - #long2Compare)/2) * sin(radians(GeoLongitude - #long2Compare)/2)))
, SQRT(1-((sin((radians(GeoLatitude - #lat2Compare)) / 2) * sin((radians(GeoLatitude - #lat2Compare)) / 2)) + (cos(radians(GeoLatitude)) * cos(radians(#lat2Compare)) * sin(radians(GeoLongitude - #long2Compare)/2) * sin(radians(GeoLongitude - #long2Compare)/2)))
))) <= #radiusInKm
If you would like to perform the Haversine formula in C#,
double resultDistance = 0.0;
double avgRadiusOfEarth = 6371.392896; //Radius of the earth differ, I'm taking the average.
//Haversine formula
//distance = R * 2 * aTan2 ( square root of A, square root of 1 - A )
// where A = sinus squared (difference in latitude / 2) + (cosine of latitude 1 * cosine of latitude 2 * sinus squared (difference in longitude / 2))
// and R = the circumference of the earth
double differenceInLat = DegreeToRadian(currentLatitude - latitudeToCompare);
double differenceInLong = DegreeToRadian(currentLongitude - longtitudeToCompare);
double aInnerFormula = Math.Cos(DegreeToRadian(currentLatitude)) * Math.Cos(DegreeToRadian(latitudeToCompare)) * Math.Sin(differenceInLong / 2) * Math.Sin(differenceInLong / 2);
double aFormula = (Math.Sin((differenceInLat) / 2) * Math.Sin((differenceInLat) / 2)) + (aInnerFormula);
resultDistance = avgRadiusOfEarth * 2 * Math.Atan2(Math.Sqrt(aFormula), Math.Sqrt(1 - aFormula));
DegreesToRadian is a function I custom created, its is a simple 1 liner of"Math.PI * angle / 180.0
My blog entry - SQL Haversine
Are you looking for
Haversine formula
The haversine formula is an equation
important in navigation, giving
great-circle distances between two
points on a sphere from their
longitudes and latitudes. It is a
special case of a more general formula
in spherical trigonometry, the law of
haversines, relating the sides and
angles of spherical "triangles".
Have a look at this.. has a javascript example as well.
Find Distance
Use the Great Circle Distance Formula.
here is a fiddle with finding locations / near locations to long/lat by given IP:
http://jsfiddle.net/bassta/zrgd9qc3/2/
And here is the function I use to calculate the distance in straight line:
function distance(lat1, lng1, lat2, lng2) {
var radlat1 = Math.PI * lat1 / 180;
var radlat2 = Math.PI * lat2 / 180;
var radlon1 = Math.PI * lng1 / 180;
var radlon2 = Math.PI * lng2 / 180;
var theta = lng1 - lng2;
var radtheta = Math.PI * theta / 180;
var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
dist = Math.acos(dist);
dist = dist * 180 / Math.PI;
dist = dist * 60 * 1.1515;
//Get in in kilometers
dist = dist * 1.609344;
return dist;
}
It returns the distance in Kilometers
If you are measuring distances less than (perhaps) 1 degree lat/long change, are looking for a very high performance approximation, and are willing to accept more inaccuracy than Haversine formula, consider these two alternatives:
(1) "Polar Coordinate Flat-Earth Formula" from Computing Distances:
a = pi/2 - lat1
b = pi/2 - lat2
c = sqrt( a^2 + b^2 - 2 * a * b * cos(lon2 - lon1) )
d = R * c
(2) Pythagorean theorem adjusted for latitude, as seen in Ewan Todd's SO post:
d_ew = (long1 - long0) * cos(average(lat0, lat1))
d_ns = (lat1 - lat0)
d = sqrt(d_ew * d_ew + d_ns * d_ns)
NOTES:
Compared to Ewan's post, I've substituted average(lat0, lat1) for lat0 inside of cos( lat0 ).
#2 is vague on whether values are degrees, radians, or kilometers; you will need some conversion code as well. See my complete code at bottom of this post.
#1 is designed to work well even near the poles, though if you are measuring a distance whose endpoints are on "opposite" sides of the pole (longitudes differ by more than 90 degrees?), Haversine is recommended instead, even for small distances.
I haven't thoroughly measured errors of these approaches, so you should take representative points for your application, and compare results to some high-quality library, to decide if the accuracies are acceptable. For distances less than a few kilometers my gut sense is that these are within 1% of correct measurement.
An alternative way to gain high performance (when applicable):
If you have a large set of static points, within one or two degrees of longitude/latitude, that you will then be calculating distances from a small number of dynamic (moving) points, consider converting your static points ONCE to the containing UTM zone (or to any other local Cartesian coordinate system), and then doing all your math in that Cartesian coordinate system.
Cartesian = flat earth = Pythagorean theorem applies, so distance = sqrt(dx^2 + dy^2).
Then the cost of accurately converting the few moving points to UTM is easily afforded.
CAVEAT for #1 (Polar): May be very wrong for distances less than 0.1 (?) meter. Even with double precision math, the following coordinates, whose true distance is about 0.005 meters, was given as "zero" by my implementation of Polar algorithm:
inputs:
lon1Xdeg 16.6564465477996 double
lat1Ydeg 57.7760262271983 double
lon2Xdeg 16.6564466358281 double
lat2Ydeg 57.776026248554 double
results:
Oblate spheroid formula:
0.00575254911118364 double
Haversine:
0.00573422966122257 double
Polar:
0
this was due to the two factors u and v exactly canceling each other:
u 0.632619944868587 double
v -0.632619944868587 double
In another case, it gave a distance of 0.067129 m when the oblate spheroid answer was 0.002887 m. The problem was that cos(lon2 - lon1) was too close to 1, so cos function returned exactly 1.
Other than measuring sub-meter distances, the max errors (compared to an oblate spheroid formula) I found for the limited small-distance data I've fed in so far:
maxHaversineErrorRatio 0.00350976281908381 double
maxPolarErrorRatio 0.0510789996931342 double
where "1" would represent a 100% error in the answer; e.g. when it returned "0", that was an error of "1" (excluded from above "maxPolar"). So "0.01" would be an error of "1 part in 100" or 1%.
Comparing Polar error with Haversine error over distances less than 2000 meters to see how much worse this simpler formula is. So far, the worst I've seen is 51 parts per 1000 for Polar vs 4 parts per 1000 for Haversine. At about 58 degrees latitude.
Now implemented "Pythagorean with Latitude Adjustment".
It is MUCH more consistent than Polar for distances < 2000 m.
I originally thought the Polar problems were only when < 1 m,
but the result shown immediately below is quite troubling.
As distances approach zero, pythagorean/latitude approaches haversine.
For example this measurement ~ 217 meters:
lon1Xdeg 16.6531667510102 double
lat1Ydeg 57.7751705615804 double
lon2Xdeg 16.6564468739869 double
lat2Ydeg 57.7760263007586 double
oblate 217.201200413731
haversine 216.518428601051
polar 226.128616011973
pythag-cos 216.518428631907
havErrRatio 0.00314349925958048
polErrRatio 0.041102054598393
pycErrRatio 0.00314349911751603
Polar has a much worse error with these inputs; either there is some mistake in my code, or in Cos function I am running on, or I have to recommend not using Polar, even though most Polar measurements were much closer than this.
OTOH, Pythagorean, even with * cos(latitude) adjustment, has error that increases more rapidly than distance (ratio of max_error/distance increases for larger distances), so you need to carefully consider the maximum distance you will measure, and the acceptable error. In addition, it is not advisable to COMPARE two nearly-equal distances using Pythagorean, to decide which is shorter, as the error is different in different DIRECTIONS (evidence not shown).
Worst case measurements, errorRatio = Abs(error) / distance (Sweden; up to 2000 m):
t_maxHaversineErrorRatio 0.00351012021578681 double
t_maxPolarErrorRatio 66.0825360597085 double
t_maxPythagoreanErrorRatio 0.00350976281416454 double
As mentioned before, the extreme polar errors are for sub-meter distances, where it could report zero instead of 6 cm, or report over 0.5 m for a distance of 1 cm (hence the "66 x" worst case shown in t_maxPolarErrorRatio), but there are also some poor results at larger distances. [Needs to be tested again with a Cosine function that is known to be highly accurate.]
Measurements taken in C# code in Xamarin.Android running on a Moto E4.
C# code:
// x=longitude, y= latitude. oblate spheroid formula. TODO: From where?
public static double calculateDistanceDD_AED( double lon1Xdeg, double lat1Ydeg, double lon2Xdeg, double lat2Ydeg )
{
double c_dblEarthRadius = 6378.135; // km
double c_dblFlattening = 1.0 / 298.257223563; // WGS84 inverse
// flattening
// Q: Why "-" for longitudes??
double p1x = -degreesToRadians( lon1Xdeg );
double p1y = degreesToRadians( lat1Ydeg );
double p2x = -degreesToRadians( lon2Xdeg );
double p2y = degreesToRadians( lat2Ydeg );
double F = (p1y + p2y) / 2;
double G = (p1y - p2y) / 2;
double L = (p1x - p2x) / 2;
double sing = Math.Sin( G );
double cosl = Math.Cos( L );
double cosf = Math.Cos( F );
double sinl = Math.Sin( L );
double sinf = Math.Sin( F );
double cosg = Math.Cos( G );
double S = sing * sing * cosl * cosl + cosf * cosf * sinl * sinl;
double C = cosg * cosg * cosl * cosl + sinf * sinf * sinl * sinl;
double W = Math.Atan2( Math.Sqrt( S ), Math.Sqrt( C ) );
if (W == 0.0)
return 0.0;
double R = Math.Sqrt( (S * C) ) / W;
double H1 = (3 * R - 1.0) / (2.0 * C);
double H2 = (3 * R + 1.0) / (2.0 * S);
double D = 2 * W * c_dblEarthRadius;
// Apply flattening factor
D = D * (1.0 + c_dblFlattening * H1 * sinf * sinf * cosg * cosg - c_dblFlattening * H2 * cosf * cosf * sing * sing);
// Transform to meters
D = D * 1000.0;
// tmstest
if (true)
{
// Compare Haversine.
double haversine = HaversineApproxDistanceGeo( lon1Xdeg, lat1Ydeg, lon2Xdeg, lat2Ydeg );
double error = haversine - D;
double absError = Math.Abs( error );
double errorRatio = absError / D;
if (errorRatio > t_maxHaversineErrorRatio)
{
if (errorRatio > t_maxHaversineErrorRatio * 1.1)
Helper.test();
t_maxHaversineErrorRatio = errorRatio;
}
// Compare Polar Coordinate Flat Earth.
double polarDistanceGeo = ApproxDistanceGeo_Polar( lon1Xdeg, lat1Ydeg, lon2Xdeg, lat2Ydeg, D );
double error2 = polarDistanceGeo - D;
double absError2 = Math.Abs( error2 );
double errorRatio2 = absError2 / D;
if (errorRatio2 > t_maxPolarErrorRatio)
{
if (polarDistanceGeo > 0)
{
if (errorRatio2 > t_maxPolarErrorRatio * 1.1)
Helper.test();
t_maxPolarErrorRatio = errorRatio2;
}
else
Helper.dubious();
}
// Compare Pythagorean Theorem with Latitude Adjustment.
double pythagoreanDistanceGeo = ApproxDistanceGeo_PythagoreanCosLatitude( lon1Xdeg, lat1Ydeg, lon2Xdeg, lat2Ydeg, D );
double error3 = pythagoreanDistanceGeo - D;
double absError3 = Math.Abs( error3 );
double errorRatio3 = absError3 / D;
if (errorRatio3 > t_maxPythagoreanErrorRatio)
{
if (D < 2000)
{
if (errorRatio3 > t_maxPythagoreanErrorRatio * 1.05)
Helper.test();
t_maxPythagoreanErrorRatio = errorRatio3;
}
}
}
return D;
}
// As a fraction of the distance.
private static double t_maxHaversineErrorRatio, t_maxPolarErrorRatio, t_maxPythagoreanErrorRatio;
// Average of equatorial and polar radii (meters).
public const double EarthAvgRadius = 6371000;
public const double EarthAvgCircumference = EarthAvgRadius * 2 * PI;
// CAUTION: This is an average of great circles; won't be the actual distance of any longitude or latitude degree.
public const double EarthAvgMeterPerGreatCircleDegree = EarthAvgCircumference / 360;
// Haversine formula (assumes Earth is sphere).
// "deg" = degrees.
// Perhaps based on Haversine Formula in https://cs.nyu.edu/visual/home/proj/tiger/gisfaq.html
public static double HaversineApproxDistanceGeo(double lon1Xdeg, double lat1Ydeg, double lon2Xdeg, double lat2Ydeg)
{
double lon1 = degreesToRadians( lon1Xdeg );
double lat1 = degreesToRadians( lat1Ydeg );
double lon2 = degreesToRadians( lon2Xdeg );
double lat2 = degreesToRadians( lat2Ydeg );
double dlon = lon2 - lon1;
double dlat = lat2 - lat1;
double sinDLat2 = Sin( dlat / 2 );
double sinDLon2 = Sin( dlon / 2 );
double a = sinDLat2 * sinDLat2 + Cos( lat1 ) * Cos( lat2 ) * sinDLon2 * sinDLon2;
double c = 2 * Atan2( Sqrt( a ), Sqrt( 1 - a ) );
double d = EarthAvgRadius * c;
return d;
}
// From https://stackoverflow.com/a/19772119/199364
// Based on Polar Coordinate Flat Earth in https://cs.nyu.edu/visual/home/proj/tiger/gisfaq.html
public static double ApproxDistanceGeo_Polar( double lon1deg, double lat1deg, double lon2deg, double lat2deg, double D = 0 )
{
double approxUnitDistSq = ApproxUnitDistSq_Polar(lon1deg, lat1deg, lon2deg, lat2deg, D);
double c = Sqrt( approxUnitDistSq );
return EarthAvgRadius * c;
}
// Might be useful to avoid taking Sqrt, when comparing to some threshold.
// Threshold would have to be adjusted to match: Power(threshold / EarthAvgRadius, 2)
private static double ApproxUnitDistSq_Polar(double lon1deg, double lat1deg, double lon2deg, double lat2deg, double D = 0 )
{
const double HalfPi = PI / 2; //1.5707963267949;
double lon1 = degreesToRadians(lon1deg);
double lat1 = degreesToRadians(lat1deg);
double lon2 = degreesToRadians(lon2deg);
double lat2 = degreesToRadians(lat2deg);
double a = HalfPi - lat1;
double b = HalfPi - lat2;
double u = a * a + b * b;
double dlon21 = lon2 - lon1;
double cosDeltaLon = Cos( dlon21 );
double v = -2 * a * b * cosDeltaLon;
// TBD: Is "Abs" necessary? That is, is "u + v" ever negative?
// (I think not; "v" looks like a secondary term. Though might be round-off issue near zero when a~=b.)
double approxUnitDistSq = Abs(u + v);
//if (approxUnitDistSq.nearlyEquals(0, 1E-16))
// Helper.dubious();
//else if (D > 0)
//{
// double dba = b - a;
// double unitD = D / EarthAvgRadius;
// double unitDSq = unitD * unitD;
// if (approxUnitDistSq > 2 * unitDSq)
// Helper.dubious();
// else if (approxUnitDistSq * 2 < unitDSq)
// Helper.dubious();
//}
return approxUnitDistSq;
}
// Pythagorean Theorem with Latitude Adjustment - from Ewan Todd - https://stackoverflow.com/a/1664836/199364
// Refined by ToolmakerSteve - https://stackoverflow.com/a/53468745/199364
public static double ApproxDistanceGeo_PythagoreanCosLatitude( double lon1deg, double lat1deg, double lon2deg, double lat2deg, double D = 0 )
{
double approxDegreesSq = ApproxDegreesSq_PythagoreanCosLatitude( lon1deg, lat1deg, lon2deg, lat2deg );
// approximate degrees on the great circle between the points.
double d_degrees = Sqrt( approxDegreesSq );
return d_degrees * EarthAvgMeterPerGreatCircleDegree;
}
public static double ApproxDegreesSq_PythagoreanCosLatitude( double lon1deg, double lat1deg, double lon2deg, double lat2deg )
{
double avgLatDeg = average( lat1deg , lat2deg );
double avgLat = degreesToRadians( avgLatDeg );
double d_ew = (lon2deg - lon1deg) * Cos( avgLat );
double d_ns = (lat2deg - lat1deg);
double approxDegreesSq = d_ew * d_ew + d_ns * d_ns;
return approxDegreesSq;
}
I am done using SQL query
select *, (acos(sin(input_lat* 0.01745329)*sin(lattitude *0.01745329) + cos(input_lat *0.01745329)*cos(lattitude *0.01745329)*cos((input_long -longitude)*0.01745329))* 57.29577951 )* 69.16 As D from table_name
Following is the module (coded in f90) containing three formulas discussed in the previous answers. You can either put this module at the top of your program
(before PROGRAM MAIN) or compile it separately and include the module directory during compilation. The following module contains three formulas. First two are great-circle distances based on the assumption that earth is spherical.
module spherical_dists
contains
subroutine great_circle_distance(lon1,lat1,lon2,lat2,dist)
!https://en.wikipedia.org/wiki/Great-circle_distance
! It takes lon, lats of two points on an assumed spherical earth and
! calculates the distance between them along the great circle connecting the two points
implicit none
real,intent(in)::lon1,lon2,lat1,lat2
real,intent(out)::dist
real,parameter::pi=3.141592,mean_earth_radius=6371.0088
real::lonr1,lonr2,latr1,latr2
real::delangl,dellon
lonr1=lon1*(pi/180.);lonr2=lon2*(pi/180.)
latr1=lat1*(pi/180.);latr2=lat2*(pi/180.)
dellon=lonr2-lonr1
delangl=acos(sin(latr1)*sin(latr2)+cos(latr1)*cos(latr2)*cos(dellon))
dist=delangl*mean_earth_radius
end subroutine
subroutine haversine_formula(lon1,lat1,lon2,lat2,dist)
! https://en.wikipedia.org/wiki/Haversine_formula
! This is similar above but numerically better conditioned for small distances
implicit none
real,intent(in)::lon1,lon2,lat1,lat2
!lon, lats of two points
real,intent(out)::dist
real,parameter::pi=3.141592,mean_earth_radius=6371.0088
real::lonr1,lonr2,latr1,latr2
real::delangl,dellon,dellat,a
! degrees are converted to radians
lonr1=lon1*(pi/180.);lonr2=lon2*(pi/180.)
latr1=lat1*(pi/180.);latr2=lat2*(pi/180.)
dellon=lonr2-lonr1 ! These dels simplify the haversine formula
dellat=latr2-latr1
! The actual haversine formula
a=(sin(dellat/2))**2+cos(latr1)*cos(latr2)*(sin(dellon/2))**2
delangl=2*asin(sqrt(a)) !2*asin(sqrt(a))
dist=delangl*mean_earth_radius
end subroutine
subroutine vincenty_formula(lon1,lat1,lon2,lat2,dist)
!https://en.wikipedia.org/wiki/Vincenty%27s_formulae
!It's a better approximation over previous two, since it considers earth to in oblate spheroid, which better approximates the shape of the earth
implicit none
real,intent(in)::lon1,lon2,lat1,lat2
real,intent(out)::dist
real,parameter::pi=3.141592,mean_earth_radius=6371.0088
real::lonr1,lonr2,latr1,latr2
real::delangl,dellon,nom,denom
lonr1=lon1*(pi/180.);lonr2=lon2*(pi/180.)
latr1=lat1*(pi/180.);latr2=lat2*(pi/180.)
dellon=lonr2-lonr1
nom=sqrt((cos(latr2)*sin(dellon))**2. + (cos(latr1)*sin(latr2)-sin(latr1)*cos(latr2)*cos(dellon))**2.)
denom=sin(latr1)*sin(latr2)+cos(latr1)*cos(latr2)*cos(dellon)
delangl=atan2(nom,denom)
dist=delangl*mean_earth_radius
end subroutine
end module
On this page you can see the whole code and formulas how distances of locations are calculated in Android Location class
android/location/Location.java
EDIT: According the hint from #Richard I put the code of the linked function into my answer, to avoid invalidated link:
private static void computeDistanceAndBearing(double lat1, double lon1,
double lat2, double lon2, BearingDistanceCache results) {
// Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
// using the "Inverse Formula" (section 4)
int MAXITERS = 20;
// Convert lat/long to radians
lat1 *= Math.PI / 180.0;
lat2 *= Math.PI / 180.0;
lon1 *= Math.PI / 180.0;
lon2 *= Math.PI / 180.0;
double a = 6378137.0; // WGS84 major axis
double b = 6356752.3142; // WGS84 semi-major axis
double f = (a - b) / a;
double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b);
double L = lon2 - lon1;
double A = 0.0;
double U1 = Math.atan((1.0 - f) * Math.tan(lat1));
double U2 = Math.atan((1.0 - f) * Math.tan(lat2));
double cosU1 = Math.cos(U1);
double cosU2 = Math.cos(U2);
double sinU1 = Math.sin(U1);
double sinU2 = Math.sin(U2);
double cosU1cosU2 = cosU1 * cosU2;
double sinU1sinU2 = sinU1 * sinU2;
double sigma = 0.0;
double deltaSigma = 0.0;
double cosSqAlpha = 0.0;
double cos2SM = 0.0;
double cosSigma = 0.0;
double sinSigma = 0.0;
double cosLambda = 0.0;
double sinLambda = 0.0;
double lambda = L; // initial guess
for (int iter = 0; iter < MAXITERS; iter++) {
double lambdaOrig = lambda;
cosLambda = Math.cos(lambda);
sinLambda = Math.sin(lambda);
double t1 = cosU2 * sinLambda;
double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
double sinSqSigma = t1 * t1 + t2 * t2; // (14)
sinSigma = Math.sqrt(sinSqSigma);
cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; // (15)
sigma = Math.atan2(sinSigma, cosSigma); // (16)
double sinAlpha = (sinSigma == 0) ? 0.0 :
cosU1cosU2 * sinLambda / sinSigma; // (17)
cosSqAlpha = 1.0 - sinAlpha * sinAlpha;
cos2SM = (cosSqAlpha == 0) ? 0.0 :
cosSigma - 2.0 * sinU1sinU2 / cosSqAlpha; // (18)
double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; // defn
A = 1 + (uSquared / 16384.0) * // (3)
(4096.0 + uSquared *
(-768 + uSquared * (320.0 - 175.0 * uSquared)));
double B = (uSquared / 1024.0) * // (4)
(256.0 + uSquared *
(-128.0 + uSquared * (74.0 - 47.0 * uSquared)));
double C = (f / 16.0) *
cosSqAlpha *
(4.0 + f * (4.0 - 3.0 * cosSqAlpha)); // (10)
double cos2SMSq = cos2SM * cos2SM;
deltaSigma = B * sinSigma * // (6)
(cos2SM + (B / 4.0) *
(cosSigma * (-1.0 + 2.0 * cos2SMSq) -
(B / 6.0) * cos2SM *
(-3.0 + 4.0 * sinSigma * sinSigma) *
(-3.0 + 4.0 * cos2SMSq)));
lambda = L +
(1.0 - C) * f * sinAlpha *
(sigma + C * sinSigma *
(cos2SM + C * cosSigma *
(-1.0 + 2.0 * cos2SM * cos2SM))); // (11)
double delta = (lambda - lambdaOrig) / lambda;
if (Math.abs(delta) < 1.0e-12) {
break;
}
}
float distance = (float) (b * A * (sigma - deltaSigma));
results.mDistance = distance;
float initialBearing = (float) Math.atan2(cosU2 * sinLambda,
cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
initialBearing *= 180.0 / Math.PI;
results.mInitialBearing = initialBearing;
float finalBearing = (float) Math.atan2(cosU1 * sinLambda,
-sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
finalBearing *= 180.0 / Math.PI;
results.mFinalBearing = finalBearing;
results.mLat1 = lat1;
results.mLat2 = lat2;
results.mLon1 = lon1;
results.mLon2 = lon2;
}
just use the distance formula Sqrt( (x2-x1)^2 + (y2-y1)^2 )
Related
Need explanation of ecef to enu algorithm
I found some useful coordinate conversion code at https://gist.github.com/govert/1b373696c9a27ff4c72a However, there is a bit specifically in the EcefToEnu function that I'm not clear on // Converts the Earth-Centered Earth-Fixed (ECEF) coordinates (x, y, z) to // East-North-Up coordinates in a Local Tangent Plane that is centered at the // (WGS-84) Geodetic point (lat0, lon0, h0). public static void EcefToEnu(double x, double y, double z, double lat0, double lon0, double h0, out double xEast, out double yNorth, out double zUp) { // Convert to radians in notation consistent with the paper: var lambda = DegreesToRadians(lat0); var phi = DegreesToRadians(lon0); var s = Sin(lambda); var N = a / Sqrt(1 - e_sq * s * s); var sin_lambda = Sin(lambda); var cos_lambda = Cos(lambda); var cos_phi = Cos(phi); var sin_phi = Sin(phi); double x0 = (h0 + N) * cos_lambda * cos_phi; double y0 = (h0 + N) * cos_lambda * sin_phi; double z0 = (h0 + (1 - e_sq) * N) * sin_lambda; double xd, yd, zd; xd = x - x0; yd = y - y0; zd = z - z0; // This is the matrix multiplication xEast = -sin_phi * xd + cos_phi * yd; yNorth = -cos_phi * sin_lambda * xd - sin_lambda * sin_phi * yd + cos_lambda * zd; zUp = cos_lambda * cos_phi * xd + cos_lambda * sin_phi * yd + sin_lambda * zd; } I get the inputs, the first 4 conversion lines, the 4 sin and cos lines and I get the matrix multiplication - there are numerous examples with that in the algorithm. But what I'm not clear on is the part double x0 = (h0 + N) * cos_lambda * cos_phi; double y0 = (h0 + N) * cos_lambda * sin_phi; double z0 = (h0 + (1 - e_sq) * N) * sin_lambda; double xd, yd, zd; xd = x - x0; yd = y - y0; zd = z - z0; I don't recognize this section from any of the algorithms I've seen. It appears to be some sort of offset, but aside from that, I'm unclear where the formulas came from or what exactly this code is doing. Can someone please enlighten me as to what this bit of code is doing? I just want to understand what I'm looking at.
They are the conversion from geodetic coordinates (lat,long,height) aka (phi,lambda,h0) to ecef cartesians (x0,y0,z0) and then the computation of the ecef vector from (x0,y0,n0) to (x,y,z). For the first part, note that if the ellipsoid were a sphere (e==0) then the first part would be the conversion from spherical polars to cartesians
Not getting the correct orientation of the user with respect to Kinect
I am using Microsoft Kinect in a project. One of the task that I have to accomplish is to find the orientation of the user w.r.t the Kinect sensor (when the user turns, the orientation changes) For this, I am trying to find the angle which the line joining the shoulders makes with the x axis of Kinect. I have come up with the following code, but it gives me very small angle values, even when I turn almost about 40 degrees. double vector_x=skel.SkeletonPositions[NUI_SKELETON_POSITION_SHOULDER_LEFT].x-skel.SkeletonPositions[NUI_SKELETON_POSITION_SHOULDER_RIGHT].x; double vector_y=skel.SkeletonPositions[NUI_SKELETON_POSITION_SHOULDER_LEFT].y-skel.SkeletonPositions[NUI_SKELETON_POSITION_SHOULDER_RIGHT].y; double vector_z=skel.SkeletonPositions[NUI_SKELETON_POSITION_SHOULDER_LEFT].z-skel.SkeletonPositions[NUI_SKELETON_POSITION_SHOULDER_RIGHT].z; double len1=sqrtf(vector_x * vector_x + vector_y * vector_y + vector_z * vector_z); double vector_x1=1.0; double vector_y1=0.0; double vector_z1=0.0; double len2=sqrtf(vector_x1 * vector_x1 + vector_y1 * vector_y1 + vector_z1 * vector_z1); double dot_product = vector_x * vector_x1 + vector_y * vector_y1 + vector_z * vector_z1; double angle = dot_product / (len1 * len2); coor_left=Convert(vector_x)+"\t"+Convert(vector_y)+"\t"+Convert(vector_z)+"\n"; OutputDebugStringA(Convert(acos(angle)).c_str()); When I added the conversion of radians to degrees, double angle1=angle*180.0/3.14; I get values form -33 to -57(when I am facing the Kinect) and then to -33 again. But in reality, it should be negative, then 0 and then positive on the other side. Where am I going wrong?
I solved it myself. I realised that I was finding the angle between incorrect vectors. All I needed to do was to take the projection of the left and the right shoulders on the x-z plane and then reduce the problem to finding the angle between two vectors in a plane. Here is what I did: double CalcAngle(double p1x,double p1y, double p2x,double p2y, double p3x,double p3y, double p4x,double p4y) { // // calculate the angle between the line from p1 to p2 // and the line from p3 to p4 // double x1 = p1x - p2x; double y1 = p1y - p2y; double x2 = p3x - p4x; double y2 = p3y - p4y; // double angle1 , angle2 , angle; // if (x1 != 0.0f) angle1 = atan(y1/x1); else angle1 = 3.14159 / 2.0; // 90 degrees // if (x2 != 0.0f) angle2 = atan(y2/x2); else angle2 = 3.14159 / 2.0; // 90 degrees // angle = fabs(angle2-angle1); angle = angle * 180.0 / 3.14159; // convert to degrees ??? // return angle; } double myangle=CalcAngle(skel.SkeletonPositions[NUI_SKELETON_POSITION_SHOULDER_LEFT].x,skel.SkeletonPositions[NUI_SKELETON_POSITION_SHOULDER_LEFT].z, skel.SkeletonPositions[NUI_SKELETON_POSITION_SHOULDER_RIGHT].x,skel.SkeletonPositions[NUI_SKELETON_POSITION_SHOULDER_RIGHT].z, 0,0,1,0); OutputDebugStringA(Convert(myangle).c_str()); OutputDebugStringA("\n");
Uniformly distributed random point inside a frustum
I have a 3D scene and I want "litter" it with X number of objects placed randomly within the viewing frustum. I tried using 3 randoms: X + Y (viewport 0..1), and then Z distance from camera and then projecting using the camera distance. However this doesn't yield a uniform distribution and there are too many objects close to the camera and too few further away. There is the option of generating a random point within the cube and filtering those points that are outside the view frustum, but I'm generating a shitload of these and I'm worried about performance. How can I generate a random point inside a frustum in a uniformly distributed manner?
This is just nicely formatted code from the final solution in the link in Amir Abiri's answer, since the code in the link has abysmal formatting quality. In java, since that's what I'm using it in, and with some variable name changes (also, tests passed; it works nicely) Note that this can be made nicer in several ways depending on the application (eg. as was mentioned in the linked site from Amir Abiri's answer, some variables could be calculated once and used multiple times so long as the same frustum is being used) public static final double ONE_THIRD = 1d / 3, EPSILON = 0.0000001; public static double getUniformRandZInFrustum(Random random, double nearW, double nearH, double farW, double farH, double depth) { double a = (farW - nearW) * (farH - nearH) / (depth * depth); double b = (nearH * (farW - nearW) + nearW * (farH - nearH)) / depth; if (Math.abs(a) < EPSILON) { if (Math.abs(b) < EPSILON) { //Rectangular prism return (random.nextDouble(depth)); } //Trapezoidal prism double c = nearW * nearH; double area = depth * (c + depth * 0.5 * b); double r = random.nextDouble(area); return (-c + Math.sqrt(c * c + 2 * b * r)) / b; } //General case double c = nearW * nearH; double area = depth * (c + depth * (0.5 * b + depth * ONE_THIRD * a)); double r = random.nextDouble(area); double det = b * b - 4 * a * c; double part1 = b * (b * b - 6 * a * c) - 12 * a * a * r; double part2 = Math.sqrt(part1 * part1 - det * det * det); if (part1 < 0.0) part2 = -part2; double part3 = part1 + part2; if (part3 < 0.0) part3 = -Math.pow(-part3, ONE_THIRD); else part3 = Math.pow(part3, ONE_THIRD); return -(b + det / part3 + part3) / (2 * a); }
Distribute the points in NDC space and back project them with an inverse frustum transformation and w-divide into view space.
I found the solution on gamedev.net in an old forum post: http://www.gamedev.net/topic/254550-geometryrandomnessuniform-filling/page_p_2539021_hl_+random%20+frustum#entry2539021
Calculating new longitude, latitude from old + n meters
I want to create 2 new longitude and 2 new latitudes based on a coordinate and a distance in meters, I want to create a nice bounding box around a certain point. It is for a part of a city and max ±1500 meters. I therefore don't think the curvature of earth has to be taken into account. So I have 50.0452345 (x) and 4.3242234 (y) and I want to know x + 500 meters, x - 500 meters, y - 500 meters, y + 500 meters I found many algorithms but almost all seem to deal with the distance between points.
The number of kilometers per degree of longitude is approximately (pi/180) * r_earth * cos(theta*pi/180) where theta is the latitude in degrees and r_earth is approximately 6378 km. The number of kilometers per degree of latitude is approximately the same at all locations, approx (pi/180) * r_earth = 111 km / degree So you can do: new_latitude = latitude + (dy / r_earth) * (180 / pi); new_longitude = longitude + (dx / r_earth) * (180 / pi) / cos(latitude * pi/180); As long as dx and dy are small compared to the radius of the earth and you don't get too close to the poles.
The accepted answer is perfectly right and works. I made some tweaks and turned into this: double meters = 50; // number of km per degree = ~111km (111.32 in google maps, but range varies // between 110.567km at the equator and 111.699km at the poles) // // 111.32km = 111320.0m (".0" is used to make sure the result of division is // double even if the "meters" variable can't be explicitly declared as double) double coef = meters / 111320.0; double new_lat = my_lat + coef; // pi / 180 ~= 0.01745 double new_long = my_long + coef / Math.cos(my_lat * 0.01745); Hope this helps too.
For latitude do: var earth = 6378.137, //radius of the earth in kilometer pi = Math.PI, m = (1 / ((2 * pi / 360) * earth)) / 1000; //1 meter in degree var new_latitude = latitude + (your_meters * m); For longitude do: var earth = 6378.137, //radius of the earth in kilometer pi = Math.PI, cos = Math.cos, m = (1 / ((2 * pi / 360) * earth)) / 1000; //1 meter in degree var new_longitude = longitude + (your_meters * m) / cos(latitude * (pi / 180)); The variable your_meters can contain a positive or a negative value.
I had to spend about two hours to work out the solution by #nibot , I simply needed a method to create a boundary box given its center point and width/height (or radius) in kilometers: I don't fully understand the solution mathematically/ geographically. I tweaked the solution (by trial and error) to get the four coordinates. Distances in km, given the current position and distance we shift to the new position in the four coordinates: North: private static Position ToNorthPosition(Position center, double northDistance) { double r_earth = 6378; var pi = Math.PI; var new_latitude = center.Lat + (northDistance / r_earth) * (180 / pi); return new Position(new_latitude, center.Long); } East: private static Position ToEastPosition(Position center, double eastDistance) { double r_earth = 6378; var pi = Math.PI; var new_longitude = center.Long + (eastDistance / r_earth) * (180 / pi) / Math.Cos(center.Lat * pi / 180); return new Position(center.Lat, new_longitude); } South: private static Position ToSouthPosition(Position center, double southDistance) { double r_earth = 6378; var pi = Math.PI; var new_latitude = center.Lat - (southDistance / r_earth) * (180 / pi); return new Position(new_latitude, center.Long); } West: private static Position ToWestPosition(Position center, double westDistance) { double r_earth = 6378; var pi = Math.PI; var new_longitude = center.Long - (westDistance / r_earth) * (180 / pi) / Math.Cos(center.Lat * pi / 180); return new Position(center.Lat, new_longitude); }
Have you checked out: How do I find the lat/long that is x km north of a given lat/long ? These calculations are annoying at best, I've done many of them. The haversine formula will be your friend. Some reference: http://www.movable-type.co.uk/scripts/latlong.html
Posting this method for sake of completeness. Use this method "as it is" to: Move any (lat,long) point by given meters in either axis. Python method to move any point by defined meters. def translate_latlong(lat,long,lat_translation_meters,long_translation_meters): ''' method to move any lat,long point by provided meters in lat and long direction. params : lat,long: lattitude and longitude in degrees as decimal values, e.g. 37.43609517497065, -122.17226450150885 lat_translation_meters: movement of point in meters in lattitude direction. positive value: up move, negative value: down move long_translation_meters: movement of point in meters in longitude direction. positive value: left move, negative value: right move ''' earth_radius = 6378.137 #Calculate top, which is lat_translation_meters above m_lat = (1 / ((2 * math.pi / 360) * earth_radius)) / 1000; lat_new = lat + (lat_translation_meters * m_lat) #Calculate right, which is long_translation_meters right m_long = (1 / ((2 * math.pi / 360) * earth_radius)) / 1000; # 1 meter in degree long_new = long + (long_translation_meters * m_long) / math.cos(lat * (math.pi / 180)); return lat_new,long_new
Working Python code to offset coordinates by 10 metres. def add_blur(lat, long): meters = 10 blur_factor = meters * 0.000006279 new_lat = lat + blur_factor new_long = long + blur_factor / math.cos(lat * 0.018) return new_lat, new_long
if you don't have to be very exact then: each 10000 meters is about 0.1 for latitude and longitude. for example I want to load locations 3000 meters around point_A from my database: double newMeter = 3000 * 0.1 / 10000; double lat1 = point_A.latitude - newMeter; double lat2 = point_A.latitude + newMeter; double lon1 = point_A.longitude - newMeter; double lon1 = point_A.longitude + newMeter; Cursor c = mDb.rawQuery("select * from TABLE1 where lat >= " + lat1 + " and lat <= " + lat2 + " and lon >= " + lon1 + " and lon <= " + lon2 + " order by id", null);
public double MeterToDegree(double meters, double latitude) { return meters / (111.32 * 1000 * Math.Cos(latitude * (Math.PI / 180))); }
var meters = 50; var coef = meters * 0.0000089; var new_lat = map.getCenter().lat.apply() + coef; var new_long = map.getCenter().lng.apply() + coef / Math.cos(new_lat * 0.018); map.setCenter({lat:new_lat, lng:new_long});
See from Official Google Maps Documentation (link below) as they solve on easy/simple maps the problems with distance by countries :) I recommended this solution to easy/simply solve issue with boundaries that you can know which area you're solving the problem with boundaries (not recommended globally) Note: Latitude lines run west-east and mark the position south-north of a point. Lines of latitude are called parallels and in total there are 180 degrees of latitude. The distance between each degree of latitude is about 69 miles (110 kilometers). The distance between longitudes narrows the further away from the equator. The distance between longitudes at the equator is the same as latitude, roughly 69 miles (110 kilometers) . At 45 degrees north or south, the distance between is about 49 miles (79 kilometers). The distance between longitudes reaches zero at the poles as the lines of meridian converge at that point. Original source 1 Original source 2 Official Google Maps Documentation: Code Example: Autocomplete Restricted to Multiple Countries See the part of their code how they solve problem with distance center + 10 kilometers by +/- 0.1 degree function initMap(): void { const map = new google.maps.Map( document.getElementById("map") as HTMLElement, { center: { lat: 50.064192, lng: -130.605469 }, zoom: 3, } ); const card = document.getElementById("pac-card") as HTMLElement; map.controls[google.maps.ControlPosition.TOP_RIGHT].push(card); const center = { lat: 50.064192, lng: -130.605469 }; // Create a bounding box with sides ~10km away from the center point const defaultBounds = { north: center.lat + 0.1, south: center.lat - 0.1, east: center.lng + 0.1, west: center.lng - 0.1, }; const input = document.getElementById("pac-input") as HTMLInputElement; const options = { bounds: defaultBounds, componentRestrictions: { country: "us" }, fields: ["address_components", "geometry", "icon", "name"], origin: center, strictBounds: false, types: ["establishment"], };
This is what I did in VBA that seems to be working for me. Calculation is in feet not meters though Public Function CalcLong(OrigLong As Double, OrigLat As Double, DirLong As String, DirLat As String, DistLong As Double, DistLat As Double) Dim FT As Double Dim NewLong, NewLat As Double FT = 1 / ((2 * WorksheetFunction.Pi / 360) * 20902230.971129) If DirLong = "W" Then NewLat = CalcLat(OrigLong, OrigLat, DirLong, DirLat, DistLong, DistLat) NewLong = OrigLong - ((FT * DistLong) / Cos(NewLat * (WorksheetFunction.Pi / 180))) CalcLong = NewLong Else NewLong = OrigLong + ((FT * DistLong) / Math.Cos(CalcLat(OrigLong, OrigLat, DirLong, DirLat, DistLong, DistLat) * (WorksheetFunction.Pi / 180))) CalcLong = NewLong End If End Function Public Function CalcLat(OrigLong As Double, OrigLat As Double, DirLong As String, DirLat As String, DistLong As Double, DistLat As Double) As Double Dim FT As Double Dim NewLat As Double FT = 1 / ((2 * WorksheetFunction.Pi / 360) * 20902230.971129) If DirLat = "S" Then NewLat = (OrigLat - (FT * DistLat)) CalcLat = NewLat Else NewLat = (OrigLat + (FT * DistLat)) CalcLat = NewLat End If End Function
Original poster said: "So I have 50.0452345 (x) and 4.3242234 (y) and I want to know x + 500 meters..." I will assume the units of the x and y values he gave there were in meters (and not degrees Longitude, Latitude). If so then he is stating measurements to 0.1 micrometer, so I will assume he needs similar accuracy for the translated output. I also will assume by "+500 meters" etc. he meant the direction to be due North-South and due East-West. He refers to a reference point: "2 new latitudes based on a coordinate"; but he did not give the Longitude and Latitude, so to explain the procedure concretely I will give the Latitudes and Longitudes for the corners of the 500 meter box he requested around the point [30 degrees Longitude,30 degrees Latitude]. The exact solution on the surface of the GRS80 Ellipsoid is given with the following set of functions (I wrote these for the free-open-source-mac-pc math program called "PARI" which allows any number of digits precision to be setup): \\=======Arc lengths along Latitude and Longitude and the respective scales: dms(u)=[truncate(u),truncate((u-truncate(u))*60),((u-truncate(u))*60-truncate((u-truncate(u))*60))*60]; SpinEarthRadiansPerSec=7.292115e-5;\ GMearth=3986005e8;\ J2earth=108263e-8;\ re=6378137;\ ecc=solve(ecc=.0001,.9999,eccp=ecc/sqrt(1-ecc^2);qecc=(1+3/eccp^2)*atan(eccp)-3/eccp;ecc^2-(3*J2earth+4/15*SpinEarthRadiansPerSec^2*re^3/GMearth*ecc^3/qecc));\ e2=ecc^2;\ b2=1-e2;\ b=sqrt(b2);\ fl=1-b;\ rfl=1/fl;\ U0=GMearth/ecc/re*atan(eccp)+1/3*SpinEarthRadiansPerSec^2*re^2;\ HeightAboveEllipsoid=0;\ reh=re+HeightAboveEllipsoid;\ longscale(lat)=reh*Pi/648000/sqrt(1+b2*(tan(lat))^2); latscale(lat)=reh*b*Pi/648000/(1-e2*(sin(lat))^2)^(3/2); longarc(lat,long1,long2)=longscale(lat)*648000/Pi*(long2-long1); latarc(lat1,lat2)=(intnum(th=lat1,lat2,sqrt(1-e2*(sin(th))^2))+e2/2*sin(2*lat1)/sqrt(1-e2*(sin(lat1))^2)-e2/2*sin(2*lat2)/sqrt(1-e2*(sin(lat2))^2))*reh; \\======= I then plugged the reference point [30,30] into those functions at the PARI command prompt and had PARI solve for the point +/- 500 meters away from it, giving the two new Longitudes and two new Latitudes that the original poster asked for. Here is the input and output showing that: ? dms(solve(x=29,31,longarc(30*Pi/180,30*Pi/180,x*Pi/180)+500)) cpu time = 1 ms, real time = 1 ms. %1172 = [29, 59, 41.3444979398934670450280297216509190843055] ? dms(solve(x=29,31,longarc(30*Pi/180,30*Pi/180,x*Pi/180)-500)) cpu time = 1 ms, real time = 1 ms. %1173 = [30, 0, 18.6555020601065329549719702783490809156945] ? dms(solve(x=29,31,latarc(30*Pi/180,x*Pi/180)+500)) cpu time = 1,357 ms, real time = 1,358 ms. %1174 = [29, 59, 43.7621925447500548285775757329518579545513] ? dms(solve(x=29,31,latarc(30*Pi/180,x*Pi/180)-500)) cpu time = 1,365 ms, real time = 1,368 ms. %1175 = [30, 0, 16.2377963202802863245716034907838199823349] ?
Calculate distance between two latitude-longitude points? (Haversine formula)
How do I calculate the distance between two points specified by latitude and longitude? For clarification, I'd like the distance in kilometers; the points use the WGS84 system and I'd like to understand the relative accuracies of the approaches available.
This link might be helpful to you, as it details the use of the Haversine formula to calculate the distance. Excerpt: This script [in Javascript] calculates great-circle distances between the two points – that is, the shortest distance over the earth’s surface – using the ‘Haversine’ formula. function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) { var R = 6371; // Radius of the earth in km var dLat = deg2rad(lat2-lat1); // deg2rad below var dLon = deg2rad(lon2-lon1); var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon/2) * Math.sin(dLon/2) ; var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); var d = R * c; // Distance in km return d; } function deg2rad(deg) { return deg * (Math.PI/180) }
I needed to calculate a lot of distances between the points for my project, so I went ahead and tried to optimize the code, I have found here. On average in different browsers my new implementation runs 2 times faster than the most upvoted answer. function distance(lat1, lon1, lat2, lon2) { var p = 0.017453292519943295; // Math.PI / 180 var c = Math.cos; var a = 0.5 - c((lat2 - lat1) * p)/2 + c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p))/2; return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km } You can play with my jsPerf and see the results here. Recently I needed to do the same in python, so here is a python implementation: from math import cos, asin, sqrt, pi def distance(lat1, lon1, lat2, lon2): p = pi/180 a = 0.5 - cos((lat2-lat1)*p)/2 + cos(lat1*p) * cos(lat2*p) * (1-cos((lon2-lon1)*p))/2 return 12742 * asin(sqrt(a)) #2*R*asin... And for the sake of completeness: Haversine on Wikipedia.
Here is a C# Implementation: static class DistanceAlgorithm { const double PIx = 3.141592653589793; const double RADIUS = 6378.16; /// <summary> /// Convert degrees to Radians /// </summary> /// <param name="x">Degrees</param> /// <returns>The equivalent in radians</returns> public static double Radians(double x) { return x * PIx / 180; } /// <summary> /// Calculate the distance between two places. /// </summary> /// <param name="lon1"></param> /// <param name="lat1"></param> /// <param name="lon2"></param> /// <param name="lat2"></param> /// <returns></returns> public static double DistanceBetweenPlaces( double lon1, double lat1, double lon2, double lat2) { double dlon = Radians(lon2 - lon1); double dlat = Radians(lat2 - lat1); double a = (Math.Sin(dlat / 2) * Math.Sin(dlat / 2)) + Math.Cos(Radians(lat1)) * Math.Cos(Radians(lat2)) * (Math.Sin(dlon / 2) * Math.Sin(dlon / 2)); double angle = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); return angle * RADIUS; } }
Here is a java implementation of the Haversine formula. public final static double AVERAGE_RADIUS_OF_EARTH_KM = 6371; public int calculateDistanceInKilometer(double userLat, double userLng, double venueLat, double venueLng) { double latDistance = Math.toRadians(userLat - venueLat); double lngDistance = Math.toRadians(userLng - venueLng); double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2) + Math.cos(Math.toRadians(userLat)) * Math.cos(Math.toRadians(venueLat)) * Math.sin(lngDistance / 2) * Math.sin(lngDistance / 2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return (int) (Math.round(AVERAGE_RADIUS_OF_EARTH_KM * c)); } Note that here we are rounding the answer to the nearest km.
Thanks very much for all this. I used the following code in my Objective-C iPhone app: const double PIx = 3.141592653589793; const double RADIO = 6371; // Mean radius of Earth in Km double convertToRadians(double val) { return val * PIx / 180; } -(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 { double dlon = convertToRadians(place2.longitude - place1.longitude); double dlat = convertToRadians(place2.latitude - place1.latitude); double a = ( pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))) * cos(convertToRadians(place2.latitude)) * pow(sin(dlon / 2), 2); double angle = 2 * asin(sqrt(a)); return angle * RADIO; } Latitude and Longitude are in decimal. I didn't use min() for the asin() call as the distances that I'm using are so small that they don't require it. It gave incorrect answers until I passed in the values in Radians - now it's pretty much the same as the values obtained from Apple's Map app :-) Extra update: If you are using iOS4 or later then Apple provide some methods to do this so the same functionality would be achieved with: -(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 { MKMapPoint start, finish; start = MKMapPointForCoordinate(place1); finish = MKMapPointForCoordinate(place2); return MKMetersBetweenMapPoints(start, finish) / 1000; }
This is a simple PHP function that will give a very reasonable approximation (under +/-1% error margin). <?php function distance($lat1, $lon1, $lat2, $lon2) { $pi80 = M_PI / 180; $lat1 *= $pi80; $lon1 *= $pi80; $lat2 *= $pi80; $lon2 *= $pi80; $r = 6372.797; // mean radius of Earth in km $dlat = $lat2 - $lat1; $dlon = $lon2 - $lon1; $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlon / 2) * sin($dlon / 2); $c = 2 * atan2(sqrt($a), sqrt(1 - $a)); $km = $r * $c; //echo '<br/>'.$km; return $km; } ?> As said before; the earth is NOT a sphere. It is like an old, old baseball that Mark McGwire decided to practice with - it is full of dents and bumps. The simpler calculations (like this) treat it like a sphere. Different methods may be more or less precise according to where you are on this irregular ovoid AND how far apart your points are (the closer they are the smaller the absolute error margin). The more precise your expectation, the more complex the math. For more info: wikipedia geographic distance
I post here my working example. List all points in table having distance between a designated point (we use a random point - lat:45.20327, long:23.7806) less than 50 KM, with latitude & longitude, in MySQL (the table fields are coord_lat and coord_long): List all having DISTANCE<50, in Kilometres (considered Earth radius 6371 KM): SELECT denumire, (6371 * acos( cos( radians(45.20327) ) * cos( radians( coord_lat ) ) * cos( radians( 23.7806 ) - radians(coord_long) ) + sin( radians(45.20327) ) * sin( radians(coord_lat) ) )) AS distanta FROM obiective WHERE coord_lat<>'' AND coord_long<>'' HAVING distanta<50 ORDER BY distanta desc The above example was tested in MySQL 5.0.95 and 5.5.16 (Linux).
In the other answers an implementation in r is missing. Calculating the distance between two point is quite straightforward with the distm function from the geosphere package: distm(p1, p2, fun = distHaversine) where: p1 = longitude/latitude for point(s) p2 = longitude/latitude for point(s) # type of distance calculation fun = distCosine / distHaversine / distVincentySphere / distVincentyEllipsoid As the earth is not perfectly spherical, the Vincenty formula for ellipsoids is probably the best way to calculate distances. Thus in the geosphere package you use then: distm(p1, p2, fun = distVincentyEllipsoid) Off course you don't necessarily have to use geosphere package, you can also calculate the distance in base R with a function: hav.dist <- function(long1, lat1, long2, lat2) { R <- 6371 diff.long <- (long2 - long1) diff.lat <- (lat2 - lat1) a <- sin(diff.lat/2)^2 + cos(lat1) * cos(lat2) * sin(diff.long/2)^2 b <- 2 * asin(pmin(1, sqrt(a))) d = R * b return(d) }
The haversine is definitely a good formula for probably most cases, other answers already include it so I am not going to take the space. But it is important to note that no matter what formula is used (yes not just one). Because of the huge range of accuracy possible as well as the computation time required. The choice of formula requires a bit more thought than a simple no brainer answer. This posting from a person at nasa, is the best one I found at discussing the options http://www.cs.nyu.edu/visual/home/proj/tiger/gisfaq.html For example, if you are just sorting rows by distance in a 100 miles radius. The flat earth formula will be much faster than the haversine. HalfPi = 1.5707963; R = 3956; /* the radius gives you the measurement unit*/ a = HalfPi - latoriginrad; b = HalfPi - latdestrad; u = a * a + b * b; v = - 2 * a * b * cos(longdestrad - longoriginrad); c = sqrt(abs(u + v)); return R * c; Notice there is just one cosine and one square root. Vs 9 of them on the Haversine formula.
There could be a simpler solution, and more correct: The perimeter of earth is 40,000Km at the equator, about 37,000 on Greenwich (or any longitude) cycle. Thus: pythagoras = function (lat1, lon1, lat2, lon2) { function sqr(x) {return x * x;} function cosDeg(x) {return Math.cos(x * Math.PI / 180.0);} var earthCyclePerimeter = 40000000.0 * cosDeg((lat1 + lat2) / 2.0); var dx = (lon1 - lon2) * earthCyclePerimeter / 360.0; var dy = 37000000.0 * (lat1 - lat2) / 360.0; return Math.sqrt(sqr(dx) + sqr(dy)); }; I agree that it should be fine-tuned as, I myself said that it's an ellipsoid, so the radius to be multiplied by the cosine varies. But it's a bit more accurate. Compared with Google Maps and it did reduce the error significantly.
pip install haversine Python implementation Origin is the center of the contiguous United States. from haversine import haversine, Unit origin = (39.50, 98.35) paris = (48.8567, 2.3508) haversine(origin, paris, unit=Unit.MILES) To get the answer in kilometers simply set unit=Unit.KILOMETERS (that's the default).
There is some errors in the code provided, I've fixed it below. All the above answers assumes the earth is a sphere. However, a more accurate approximation would be that of an oblate spheroid. a= 6378.137#equitorial radius in km b= 6356.752#polar radius in km def Distance(lat1, lons1, lat2, lons2): lat1=math.radians(lat1) lons1=math.radians(lons1) R1=(((((a**2)*math.cos(lat1))**2)+(((b**2)*math.sin(lat1))**2))/((a*math.cos(lat1))**2+(b*math.sin(lat1))**2))**0.5 #radius of earth at lat1 x1=R1*math.cos(lat1)*math.cos(lons1) y1=R1*math.cos(lat1)*math.sin(lons1) z1=R1*math.sin(lat1) lat2=math.radians(lat2) lons2=math.radians(lons2) R2=(((((a**2)*math.cos(lat2))**2)+(((b**2)*math.sin(lat2))**2))/((a*math.cos(lat2))**2+(b*math.sin(lat2))**2))**0.5 #radius of earth at lat2 x2=R2*math.cos(lat2)*math.cos(lons2) y2=R2*math.cos(lat2)*math.sin(lons2) z2=R2*math.sin(lat2) return ((x1-x2)**2+(y1-y2)**2+(z1-z2)**2)**0.5
I don't like adding yet another answer, but the Google maps API v.3 has spherical geometry (and more). After converting your WGS84 to decimal degrees you can do this: <script src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry" type="text/javascript"></script> distance = google.maps.geometry.spherical.computeDistanceBetween( new google.maps.LatLng(fromLat, fromLng), new google.maps.LatLng(toLat, toLng)); No word about how accurate Google's calculations are or even what model is used (though it does say "spherical" rather than "geoid". By the way, the "straight line" distance will obviously be different from the distance if one travels on the surface of the earth which is what everyone seems to be presuming.
You can use the build in CLLocationDistance to calculate this: CLLocation *location1 = [[CLLocation alloc] initWithLatitude:latitude1 longitude:longitude1]; CLLocation *location2 = [[CLLocation alloc] initWithLatitude:latitude2 longitude:longitude2]; [self distanceInMetersFromLocation:location1 toLocation:location2] - (int)distanceInMetersFromLocation:(CLLocation*)location1 toLocation:(CLLocation*)location2 { CLLocationDistance distanceInMeters = [location1 distanceFromLocation:location2]; return distanceInMeters; } In your case if you want kilometers just divide by 1000.
As pointed out, an accurate calculation should take into account that the earth is not a perfect sphere. Here are some comparisons of the various algorithms offered here: geoDistance(50,5,58,3) Haversine: 899 km Maymenn: 833 km Keerthana: 897 km google.maps.geometry.spherical.computeDistanceBetween(): 900 km geoDistance(50,5,-58,-3) Haversine: 12030 km Maymenn: 11135 km Keerthana: 10310 km google.maps.geometry.spherical.computeDistanceBetween(): 12044 km geoDistance(.05,.005,.058,.003) Haversine: 0.9169 km Maymenn: 0.851723 km Keerthana: 0.917964 km google.maps.geometry.spherical.computeDistanceBetween(): 0.917964 km geoDistance(.05,80,.058,80.3) Haversine: 33.37 km Maymenn: 33.34 km Keerthana: 33.40767 km google.maps.geometry.spherical.computeDistanceBetween(): 33.40770 km Over small distances, Keerthana's algorithm does seem to coincide with that of Google Maps. Google Maps does not seem to follow any simple algorithm, suggesting that it may be the most accurate method here. Anyway, here is a Javascript implementation of Keerthana's algorithm: function geoDistance(lat1, lng1, lat2, lng2){ const a = 6378.137; // equitorial radius in km const b = 6356.752; // polar radius in km var sq = x => (x*x); var sqr = x => Math.sqrt(x); var cos = x => Math.cos(x); var sin = x => Math.sin(x); var radius = lat => sqr((sq(a*a*cos(lat))+sq(b*b*sin(lat)))/(sq(a*cos(lat))+sq(b*sin(lat)))); lat1 = lat1 * Math.PI / 180; lng1 = lng1 * Math.PI / 180; lat2 = lat2 * Math.PI / 180; lng2 = lng2 * Math.PI / 180; var R1 = radius(lat1); var x1 = R1*cos(lat1)*cos(lng1); var y1 = R1*cos(lat1)*sin(lng1); var z1 = R1*sin(lat1); var R2 = radius(lat2); var x2 = R2*cos(lat2)*cos(lng2); var y2 = R2*cos(lat2)*sin(lng2); var z2 = R2*sin(lat2); return sqr(sq(x1-x2)+sq(y1-y2)+sq(z1-z2)); }
Here is a typescript implementation of the Haversine formula static getDistanceFromLatLonInKm(lat1: number, lon1: number, lat2: number, lon2: number): number { var deg2Rad = deg => { return deg * Math.PI / 180; } var r = 6371; // Radius of the earth in km var dLat = deg2Rad(lat2 - lat1); var dLon = deg2Rad(lon2 - lon1); var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(deg2Rad(lat1)) * Math.cos(deg2Rad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); var d = r * c; // Distance in km return d; }
Here is the SQL Implementation to calculate the distance in km, SELECT UserId, ( 3959 * acos( cos( radians( your latitude here ) ) * cos( radians(latitude) ) * cos( radians(longitude) - radians( your longitude here ) ) + sin( radians( your latitude here ) ) * sin( radians(latitude) ) ) ) AS distance FROM user HAVING distance < 5 ORDER BY distance LIMIT 0 , 5; For further details in the implementation by programming langugage, you can just go through the php script given here
This script [in PHP] calculates distances between the two points. public static function getDistanceOfTwoPoints($source, $dest, $unit='K') { $lat1 = $source[0]; $lon1 = $source[1]; $lat2 = $dest[0]; $lon2 = $dest[1]; $theta = $lon1 - $lon2; $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta)); $dist = acos($dist); $dist = rad2deg($dist); $miles = $dist * 60 * 1.1515; $unit = strtoupper($unit); if ($unit == "K") { return ($miles * 1.609344); } else if ($unit == "M") { return ($miles * 1.609344 * 1000); } else if ($unit == "N") { return ($miles * 0.8684); } else { return $miles; } }
here is an example in postgres sql (in km, for miles version, replace 1.609344 by 0.8684 version) CREATE OR REPLACE FUNCTION public.geodistance(alat float, alng float, blat float, blng float) RETURNS float AS $BODY$ DECLARE v_distance float; BEGIN v_distance = asin( sqrt( sin(radians(blat-alat)/2)^2 + ( (sin(radians(blng-alng)/2)^2) * cos(radians(alat)) * cos(radians(blat)) ) ) ) * cast('7926.3352' as float) * cast('1.609344' as float) ; RETURN v_distance; END $BODY$ language plpgsql VOLATILE SECURITY DEFINER; alter function geodistance(alat float, alng float, blat float, blng float) owner to postgres;
Java implementation in according Haversine formula double calculateDistance(double latPoint1, double lngPoint1, double latPoint2, double lngPoint2) { if(latPoint1 == latPoint2 && lngPoint1 == lngPoint2) { return 0d; } final double EARTH_RADIUS = 6371.0; //km value; //converting to radians latPoint1 = Math.toRadians(latPoint1); lngPoint1 = Math.toRadians(lngPoint1); latPoint2 = Math.toRadians(latPoint2); lngPoint2 = Math.toRadians(lngPoint2); double distance = Math.pow(Math.sin((latPoint2 - latPoint1) / 2.0), 2) + Math.cos(latPoint1) * Math.cos(latPoint2) * Math.pow(Math.sin((lngPoint2 - lngPoint1) / 2.0), 2); distance = 2.0 * EARTH_RADIUS * Math.asin(Math.sqrt(distance)); return distance; //km value }
I made a custom function in R to calculate haversine distance(km) between two spatial points using functions available in R base package. custom_hav_dist <- function(lat1, lon1, lat2, lon2) { R <- 6371 Radian_factor <- 0.0174533 lat_1 <- (90-lat1)*Radian_factor lat_2 <- (90-lat2)*Radian_factor diff_long <-(lon1-lon2)*Radian_factor distance_in_km <- 6371*acos((cos(lat_1)*cos(lat_2))+ (sin(lat_1)*sin(lat_2)*cos(diff_long))) rm(lat1, lon1, lat2, lon2) return(distance_in_km) } Sample output custom_hav_dist(50.31,19.08,54.14,19.39) [1] 426.3987 PS: To calculate distances in miles, substitute R in function (6371) with 3958.756 (and for nautical miles, use 3440.065).
To calculate the distance between two points on a sphere you need to do the Great Circle calculation. There are a number of C/C++ libraries to help with map projection at MapTools if you need to reproject your distances to a flat surface. To do this you will need the projection string of the various coordinate systems. You may also find MapWindow a useful tool to visualise the points. Also as its open source its a useful guide to how to use the proj.dll library, which appears to be the core open source projection library.
Here is my java implementation for calculation distance via decimal degrees after some search. I used mean radius of world (from wikipedia) in km. İf you want result miles then use world radius in miles. public static double distanceLatLong2(double lat1, double lng1, double lat2, double lng2) { double earthRadius = 6371.0d; // KM: use mile here if you want mile result double dLat = toRadian(lat2 - lat1); double dLng = toRadian(lng2 - lng1); double a = Math.pow(Math.sin(dLat/2), 2) + Math.cos(toRadian(lat1)) * Math.cos(toRadian(lat2)) * Math.pow(Math.sin(dLng/2), 2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); return earthRadius * c; // returns result kilometers } public static double toRadian(double degrees) { return (degrees * Math.PI) / 180.0d; }
Here's the accepted answer implementation ported to Java in case anyone needs it. package com.project529.garage.util; /** * Mean radius. */ private static double EARTH_RADIUS = 6371; /** * Returns the distance between two sets of latitudes and longitudes in meters. * <p/> * Based from the following JavaScript SO answer: * http://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula, * which is based on https://en.wikipedia.org/wiki/Haversine_formula (error rate: ~0.55%). */ public double getDistanceBetween(double lat1, double lon1, double lat2, double lon2) { double dLat = toRadians(lat2 - lat1); double dLon = toRadians(lon2 - lon1); double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); double d = EARTH_RADIUS * c; return d; } public double toRadians(double degrees) { return degrees * (Math.PI / 180); }
For those looking for an Excel formula based on WGS-84 & GRS-80 standards: =ACOS(COS(RADIANS(90-Lat1))*COS(RADIANS(90-Lat2))+SIN(RADIANS(90-Lat1))*SIN(RADIANS(90-Lat2))*COS(RADIANS(Long1-Long2)))*6371 Source
there is a good example in here to calculate distance with PHP http://www.geodatasource.com/developers/php : function distance($lat1, $lon1, $lat2, $lon2, $unit) { $theta = $lon1 - $lon2; $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta)); $dist = acos($dist); $dist = rad2deg($dist); $miles = $dist * 60 * 1.1515; $unit = strtoupper($unit); if ($unit == "K") { return ($miles * 1.609344); } else if ($unit == "N") { return ($miles * 0.8684); } else { return $miles; } }
Here is the implementation VB.NET, this implementation will give you the result in KM or Miles based on an Enum value you pass. Public Enum DistanceType Miles KiloMeters End Enum Public Structure Position Public Latitude As Double Public Longitude As Double End Structure Public Class Haversine Public Function Distance(Pos1 As Position, Pos2 As Position, DistType As DistanceType) As Double Dim R As Double = If((DistType = DistanceType.Miles), 3960, 6371) Dim dLat As Double = Me.toRadian(Pos2.Latitude - Pos1.Latitude) Dim dLon As Double = Me.toRadian(Pos2.Longitude - Pos1.Longitude) Dim a As Double = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(Me.toRadian(Pos1.Latitude)) * Math.Cos(Me.toRadian(Pos2.Latitude)) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2) Dim c As Double = 2 * Math.Asin(Math.Min(1, Math.Sqrt(a))) Dim result As Double = R * c Return result End Function Private Function toRadian(val As Double) As Double Return (Math.PI / 180) * val End Function End Class
I condensed the computation down by simplifying the formula. Here it is in Ruby: include Math earth_radius_mi = 3959 radians = lambda { |deg| deg * PI / 180 } coord_radians = lambda { |c| { :lat => radians[c[:lat]], :lng => radians[c[:lng]] } } # from/to = { :lat => (latitude_in_degrees), :lng => (longitude_in_degrees) } def haversine_distance(from, to) from, to = coord_radians[from], coord_radians[to] cosines_product = cos(to[:lat]) * cos(from[:lat]) * cos(from[:lng] - to[:lng]) sines_product = sin(to[:lat]) * sin(from[:lat]) return earth_radius_mi * acos(cosines_product + sines_product) end
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2,units) { var R = 6371; // Radius of the earth in km var dLat = deg2rad(lat2-lat1); // deg2rad below var dLon = deg2rad(lon2-lon1); var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon/2) * Math.sin(dLon/2) ; var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); var d = R * c; var miles = d / 1.609344; if ( units == 'km' ) { return d; } else { return miles; }} Chuck's solution, valid for miles also.
In Mysql use the following function pass the parameters as using POINT(LONG,LAT) CREATE FUNCTION `distance`(a POINT, b POINT) RETURNS double DETERMINISTIC BEGIN RETURN GLength( LineString(( PointFromWKB(a)), (PointFromWKB(b)))) * 100000; -- To Make the distance in meters END;