问题描述
如何计算纬度和经度指定的两点之间的距离?
为了澄清,我想要距离公里;这些点使用WGS84系统,我想了解可用方法的相对准确性。
最佳解决方案
这个link可能对您有帮助,因为它详细说明了使用Haversine formula计算距离。
摘抄:
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) }
次佳解决方案
我需要计算我的项目点数之间的距离,所以我继续尝试优化代码,我在这里找到。平均来说,在不同浏览器中,我的新实现运行速度比最受欢迎的答案快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,看看results here。
最近我需要做同样的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; }
第四种方案
这是一个Java实现的Haversine公式。
描述 java.lang.Math.toRadians(double angdeg) 转换为度大致相等的角度,以弧度为单位的角度。从角度到弧度的转换通常是不精确的。 声明 以下是声明java.lang.Math.toRadians()方法 public static double toRadians(double angdeg) 参数 angdeg -- an angle, in degrees 返回值 此方法返回的的角度angdeg弧度测量。 异常 NA
import java.lang.*; public class MathDemo { public static void main(String[] args) { // get two double numbers numbers double x = 45; double y = -180; // convert them in radians x = Math.toRadians(x); y = Math.toRadians(y); // print the hyperbolic tangent of these doubles System.out.println("Math.tanh(" + x + ")=" + Math.tanh(x)); System.out.println("Math.tanh(" + y + ")=" + Math.tanh(y)); } }
让我们来编译和运行上面的程序,这将产生以下结果:
Math.tanh(0.7853981633974483)=0.6557942026326724
Math.tanh(-3.141592653589793)=-0.99627207622075
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提供一些方法来执行此操作,因此可以通过以下方式实现相同的功能:
-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 { MKMapPoint start, finish; start = MKMapPointForCoordinate(place1); finish = MKMapPointForCoordinate(place2); return MKMetersBetweenMapPoints(start, finish) / 1000; }
第六种方案
我在这里张贴我的工作实例。
列出表中具有小于50KM的指定点(我们使用随机点 – 纬度:45.20327,长:23.7806)之间的距离的所有点,纬度&经度在MySQL(表格栏位是coord_lat和coord_long):
列出所有DISTANCE< 50,公里(被认为是地球半径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
以上示例在MySQL 5.0.95和5.5.16(Linux)中进行了测试。
第七种方案
这是一个简单的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; } ?>
如前所述:地球不是一个球体。它就像一个老旧的棒球,标记mcguire决定练习 – 它充满了凹痕和颠簸。更简单的计算(像这样)将其视为一个球体。
不同的方法可能根据您在这种不规则卵形上的位置或多或少精确,并且您的点距离相差甚远(绝对误差越小越小)。你的期望越准确,数学越复杂。
更多信息:wikipedia geographic distance
第八种方案
您可以使用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。
第九种方案
在其他答案中,缺少r中的一个实现。
使用geosphere封装的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 formula for ellipsoids可能是计算距离的最佳方式。因此,在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) }
第十种方案
它取决于你想要的准确程度,以及所定义的datum的长度和长度。非常,非常近,你做一个小的球形触发,但纠正事实,地球不是一个球体,使公式更复杂。
参考文献
注:本文内容整合自google/baidu/bing辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:gxnotes#qq.com(#替换为@)。