计算两个纬度经度点之间的距离? (半乳糖配方)

如何计算经纬度两点之间的距离?

为了澄清,我想以千米为单位的距离; 点使用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) } 

我需要为我的项目计算点之间的很多距离,所以我继续尝试优化代码,我已经在这里find了。 平均而言,在不同的浏览器中,我的新实现的运行速度比最上面的答案快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... 

为了完整起见:维基上的Haversine 。

这是一个C#实现:

 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; } 

这里是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)); } 

请注意,这里我们正在将最接近公里的答案四舍五入。

非常感谢所有这一切。 我在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提供了一些方法来实现这一function,以便实现相同的function:

 -(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 { MKMapPoint start, finish; start = MKMapPointForCoordinate(place1); finish = MKMapPointForCoordinate(place2); return MKMetersBetweenMapPoints(start, finish) / 1000; } 

这是一个简单的PHP函数,将给出一个非常合理的近似值(在+/- 1%的误差范围内)。

 <?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; } ?> 

如前所述; 地球不是一个球体。 Mark McGwire就像一个古老的老棒球,决定练习一下 – 它充满了凹痕和颠簸。 简单的计算(像这样)把它当作一个球体来对待。

根据你在这个不规则卵形的位置,不同的方法可能或多或less是精确的。你的点的距离有多远(它们越接近绝对误差)。 你的期望越精确,math就越复杂。

欲了解更多信息: 维基百科的地理距离

我在这里发表我的工作示例。

在MySQL中(表格字段为coord_lat和coord_long),列出距离指定点(我们使用一个随机点 – lat:45.20327,长度:23.7806)小于50 KM,具有经度和纬度的点之间的所有点。

列出所有具有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)中testing过。

在另一个答案中, r中的实现丢失了。

distm软件包的distm函数计算两点之间的距离非常简单:

 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 c <- 2 * asin(min(1,sqrt(a))) d = R * c return(d) } 

你可以使用CLLocationDistance中的构build来计算:

 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。

它取决于你想要的精度以及经纬度的数据 。 非常非常近似地,你做一个小小的球形三angular形,但是纠正地球不是球体的事实使公式更加复杂。

半胱氨酸对于大多数情况来说绝对是一个很好的公式,其他的答案已经包括了,所以我不打算占据这个空间。 但重要的是要注意,不pipe使用什么公式(是的不只是一个)。 由于可能的精确度范围以及所需的计算时间。 公式的select需要更多的思考,而不是一个简单的,没有脑子的答案。

在美国国家航空航天局这个post里,是我在讨论这个选项时发现的最好的一个

http://www.cs.nyu.edu/visual/home/proj/tiger/gisfaq.html

例如,如果您只是按照100英里半径的距离对行进行sorting, 这个扁平的地球公式将会比半胱氨酸快得多。

 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; 

注意只有一个余弦和一个平方根。 与Haversine公式中的9个。

我不喜欢再添加一个答案,但Google maps API v.3具有球形几何(以及更多)。 将WGS84转换成十进制度后,可以这样做:

 <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)); 

没有关于Google的计算精度究竟如何,甚至是使用什么模型的说法(尽pipe它的确是说“球形”而不是“大地水准面”,顺便说一下,如果在“每个人似乎都在假设地球表面。

要计算球体上两点之间的距离,您需要进行大圆圈计算 。

如果需要重新投影到平面的距离,有许多C / C ++库可以帮助MapTools进行地图投影。 为此,您将需要各种坐标系统的投影string。

你也可以findMapWindow一个有用的工具来可视化的点。 另外作为其开放源代码的一个有用的指导如何使用proj.dll库,这似乎是核心的开源投影库。

这里是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; } 

可以有一个更简单的解决scheme,更正确的说法是:赤道地区的周边地区为4万公里,格林威治(或任何经度)地区的周边地区约为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)); }; 

我同意它应该被微调,我自己说这是一个椭球,所以半径乘以余弦变化。 但它更准确一些。 与Google地图相比,它显着减less了错误。

这个脚本[在PHP]计算两点之间的距离。

 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; } } 

我通过简化公式简化了计算。

这里是在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 

这里是接受的答案实施移植到Java以防万一需要它。

 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); } 

有错误 – 没有方法'toRad'

所以修改了上面的程序来调用toRad方法 –

 toRad(lat2-lat1) Math.cos(toRad(lat1)) 

并添加了方法 –

 //degrees to radians function toRad(degree) { rad = degree* Math.PI/ 180; return rad; } 

在这里有一个很好的例子来计算与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; } } 

这里是VB.NET的实现,这个实现将根据你传递的Enum值给你KM或Miles的结果。

 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 

这是我的java实现计算距离通过十进制度在一些search后。 我用千米的世界平均半径(来自维基百科)。 如果你想要结果里程,然后使用英里的世界半径。

 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; } 
 function getDistanceFromLatLonInKm(position1, position2) { "use strict"; var deg2rad = function (deg) { return deg * (Math.PI / 180); }, R = 6371, dLat = deg2rad(position2.lat - position1.lat), dLng = deg2rad(position2.lng - position1.lng), a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(deg2rad(position1.lat)) * Math.cos(deg2rad(position1.lat)) * Math.sin(dLng / 2) * Math.sin(dLng / 2), c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; } console.log(getDistanceFromLatLonInKm( {lat: 48.7931459, lng: 1.9483572}, {lat: 48.827167, lng: 2.2459745} )); 

Python implimentation起源是连续美国的中心。

 from haversine import haversine origin = (39.50, 98.35) paris = (48.8567, 2.3508) haversine(origin, paris, miles=True) 

要以千米为单位获得答案,只需设置miles = false即可。

以上所有答案都假设地球是一个球体。 然而,更准确的近似值将是扁球体。

 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 

这是另一个转换成Ruby代码:

 include Math #Note: from/to = [lat, long] def get_distance_in_km(from, to) radians = lambda { |deg| deg * Math.PI / 180 } radius = 6371 # Radius of the earth in kilometer dLat = radians[to[0]-from[0]] dLon = radians[to[1]-from[1]] cosines_product = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(radians[from[0]]) * Math.cos(radians[to[1]]) * Math.sin(dLon/2) * Math.sin(dLon/2) c = 2 * Math.atan2(Math.sqrt(cosines_product), Math.sqrt(1-cosines_product)) return radius * c # Distance in kilometer end 

这里是一个简单的JavaScript函数,可能从这个链接有用..以某种方式相关,但我们使用谷歌地球JavaScript插件,而不是地图

 function getApproximateDistanceUnits(point1, point2) { var xs = 0; var ys = 0; xs = point2.getX() - point1.getX(); xs = xs * xs; ys = point2.getY() - point1.getY(); ys = ys * ys; return Math.sqrt(xs + ys); } 

单位是不是在距离,但在一个比例相对于你的坐标。 还有其他相关的计算,您可以在这里replacegetApproximateDistanceUnits函数链接

然后我使用这个函数来查看纬度经度是否在半径之内

 function isMapPlacemarkInRadius(point1, point2, radi) { if (point1 && point2) { return getApproximateDistanceUnits(point1, point2) <= radi; } else { return 0; } } 

点可定义为

  $$.getPoint = function(lati, longi) { var location = { x: 0, y: 0, getX: function() { return location.x; }, getY: function() { return location.y; } }; location.x = lati; location.y = longi; return location; }; 

那么你可以做你的事情,看看一个点是否在一个半径的区域说:

  //put it on the map if within the range of a specified radi assuming 100,000,000 units var iconpoint = Map.getPoint(pp.latitude, pp.longitude); var centerpoint = Map.getPoint(Settings.CenterLatitude, Settings.CenterLongitude); //approx ~200 units to show only half of the globe from the default center radius if (isMapPlacemarkInRadius(centerpoint, iconpoint, 120)) { addPlacemark(pp.latitude, pp.longitude, pp.name); } else { otherSidePlacemarks.push({ latitude: pp.latitude, longitude: pp.longitude, name: pp.name }); } 
 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; }} 

查克的解决scheme,也有效英里。

在Mysql中使用以下函数传递参数,如使用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; 
 //JAVA public Double getDistanceBetweenTwoPoints(Double latitude1, Double longitude1, Double latitude2, Double longitude2) { final int RADIUS_EARTH = 6371; double dLat = getRad(latitude2 - latitude1); double dLong = getRad(longitude2 - longitude1); double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(getRad(latitude1)) * Math.cos(getRad(latitude2)) * Math.sin(dLong / 2) * Math.sin(dLong / 2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return (RADIUS_EARTH * c) * 1000; } private Double getRad(Double x) { return x * Math.PI / 180; }