如何计算纬度和经度指定的两点之间的距离?
为了澄清,我想要以公里为单位的距离; 这些要点使用WGS84系统,我想了解可用方法的相对准确性.
此链接可能对您有所帮助,因为它详细说明了使用Haversine公式计算距离.
摘抄:
这个脚本[在Javascript中]使用'Haversine'公式计算两点之间的大圆距离 - 即地球表面上的最短距离.
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)
}
我需要为我的项目计算点之间的很多距离,所以我继续尝试优化代码,我在这里找到了.平均而言,在不同的浏览器中,我的新实现运行速度比最受欢迎的答案快2倍.
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
}
您可以使用我的jsPerf并在此处查看结果.
最近我需要在python中做同样的事情,所以这里有一个python实现:
from math import cos, asin, sqrt
def distance(lat1, lon1, lat2, lon2):
p = 0.017453292519943295 #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...
并且为了完整起见:在wiki上的Haversine.
这是一个C#实现:
static class DistanceAlgorithm
{
const double PIx = 3.141592653589793;
const double RADIUS = 6378.16;
///
/// Convert degrees to Radians
///
/// Degrees
/// The equivalent in radians
public static double Radians(double x)
{
return x * PIx / 180;
}
///
/// Calculate the distance between two places.
///
///
///
///
///
///
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;
}
}
这是Haversine公式的java实现.
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));
}
请注意,这里我们将答案四舍五入到最近的km.
非常感谢所有这一切.我在Objective-C iPhone应用程序中使用了以下代码:
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;
}
纬度和经度以十进制表示.我没有使用min()进行asin()调用,因为我使用的距离太小而且不需要它.
它给出了错误的答案,直到我传入Radians中的值 - 现在它与从Apple的Map应用程序获得的值几乎相同:-)
额外更新:
如果您使用的是iOS4或更高版本,则Apple会提供一些方法来实现此目的,因此可以通过以下方式实现相同的功能:
-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {
MKMapPoint start, finish;
start = MKMapPointForCoordinate(place1);
finish = MKMapPointForCoordinate(place2);
return MKMetersBetweenMapPoints(start, finish) / 1000;
}
这是一个简单的PHP函数,它将给出一个非常合理的近似值(低于+/- 1%的误差范围).
'.$km;
return $km;
}
?>
如前所述; 地球不是球体.这就像Mark McGwire决定练习的旧式棒球一样 - 它充满了凹痕和颠簸.更简单的计算(像这样)将其视为一个球体.
根据你在这个不规则卵形上的位置,不同的方法可能或多或少精确到你的点有多远(它们越接近绝对误差范围).您的期望越精确,数学就越复杂.
有关更多信息:维基百科地理距离
我在这里发布我的工作示例.
列出表格中的所有点,其中指定点之间的距离(我们使用随机点 - 纬度:45.20327,长度:23.7806)小于50公里,纬度和经度,在MySQL中(表格字段为coord_lat和coord_long):
列出所有DISTANCE <50,以公里为单位(被认为是地球半径6371公里):
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
以上示例在MySQL 5.0.95和5.5.16(Linux)中进行了测试.
在其他答案中,缺少r中的实现.
distm
使用geosphere
包中的函数计算两点之间的距离非常简单:
distm(p1, p2, fun = distHaversine)
哪里:
p1 = longitude/latitude for point(s) p2 = longitude/latitude for point(s) # type of distance calculation fun = distCosine / distHaversine / distVincentySphere / distVincentyEllipsoid
由于地球不是完美的球形,因此椭圆体的Vincenty公式可能是计算距离的最佳方法.因此,在geosphere
您使用的包中:
distm(p1, p2, fun = distVincentyEllipsoid)
当然你不一定要使用geosphere
包,你也可以R
用函数计算基数的距离:
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) }
对于大多数情况来说,hasrsine肯定是一个很好的公式,其他答案已经包括它,所以我不打算占用空间.但重要的是要注意,无论使用什么配方(是的,不仅仅是一个).由于可能的精确范围以及所需的计算时间.公式的选择需要更多的思考,而不是一个简单的没有脑子的答案.
来自美国国家航空航天局的人发布的这篇文章是我在讨论这些选项时发现的最好的
http://www.cs.nyu.edu/visual/home/proj/tiger/gisfaq.html
例如,如果您只是在100英里范围内按距离排序行.扁平地球配方比快速配方快得多.
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;
请注意,只有一个余弦和一个平方根.其中9个是Haversine公式.
您可以使用CLLocationDistance中的构建来计算:
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; }
在你的情况下,如果你想要公里除以1000.
我不想添加另一个答案,但Google maps API v.3具有球形几何(以及更多).将WGS84转换为十进制度后,您可以执行以下操作:
distance = google.maps.geometry.spherical.computeDistanceBetween( new google.maps.LatLng(fromLat, fromLng), new google.maps.LatLng(toLat, toLng));
没有关于谷歌的计算是多么准确甚至使用什么模型的说法(虽然它确实说"球形"而不是"大地水准面".顺便说一下,如果一个人在旅行中,"直线"距离显然会与距离不同.每个人似乎都在假设的地球表面.
Python的实现Origin是连续的美国的中心。
from haversine import haversine origin = (39.50, 98.35) paris = (48.8567, 2.3508) haversine(origin, paris, miles=True)
要获得以千米为单位的答案,只需将miles = false设置为。
可能有一个更简单的解决方案,更正确:赤道上的地球周长为40,000公里,格林威治(或任何经度)周期约为37,000公里.从而:
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)); };
我同意它应该被微调,因为我自己说它是一个椭球,所以乘以余弦的半径会有所不同.但它更准确一点.与谷歌地图相比,它确实显着减少了错误.
所有上述答案都假设地球是一个球体.然而,更精确的近似将是扁球体的近似.
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=R*math.cos(lat1)*math.cos(lons1) y1=R*math.cos(lat1)*math.sin(lons1) z1=R*math.sin(lat1) lat2=math.radians(lat2) lons2=math.radians(lons2) R1=(((((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=R*math.cos(lat2)*math.cos(lons2) y2=R*math.cos(lat2)*math.sin(lons2) z2=R*math.sin(lat2) return ((x1-x2)**2+(y1-y2)**2+(z1-z2)**2)**0.5
这是Haversine公式的打字稿实现
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; }
如前所述,准确的计算应考虑到地球不是一个完美的球体。以下是此处提供的各种算法的一些比较:
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
在很小的距离上,Kerthana的算法似乎确实与Google Maps的算法一致。Google Maps似乎没有遵循任何简单的算法,这表明这可能是最准确的方法。
无论如何,这是Keerthana算法的Javascript实现:
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)); }
这是SQL实现,用于以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;
有关通过编程语言实现的更多详细信息,您可以浏览此处给出的php脚本