当前位置:  开发笔记 > 编程语言 > 正文

计算两个纬度 - 经度点之间的距离?(Haversine配方)

如何解决《计算两个纬度-经度点之间的距离?(Haversine配方)》经验,为你挑选了17个好方法。

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

为了澄清,我想要以公里为单位的距离; 这些要点使用WGS84系统,我想了解可用方法的相对准确性.



1> Chuck..:

此链接可能对您有所帮助,因为它详细说明了使用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)
}


这个计算/方法是否说明地球是一个球体(不是一个完美的球体)?原始问题询问WGS84地球仪上各点之间的距离.不确定使用一个完美的球体会产生多少错误,但我怀疑它可能相当多,这取决于地球上各点的位置,因此值得记住.
有没有理由使用`Math.atan2(Math.sqrt(a),Math.sqrt(1-a))`而不是`Math.asin(Math.sqrt(h))`,这将是直接实现维基百科文章使用的公式?它更有效和/或更稳定吗?
Haversine公式没有说明地球是一个椭球体,所以你会因为这个事实而引入一些错误.它不能保证正确,优于0.5%.但是,这可能是也可能不是可接受的错误级别.
@UsmanMutawakil嗯,你得到的38英里是路上的距离.该算法计算地球表面的直线距离.谷歌地图有一个距离工具(左下角,"实验室"),它使用相同的距离工具.
@Forte_201092:因为这不是必要的 - 因为`(sin(x))²`等于`(sin(-x))²
以下是PHP中相同代码的实现,https://gist.github.com/gajus/5487925.
这个公式似乎适用于较大的距离,但如果我们试图测量相对较小的距离(<3英里)呢?我用这个公式进行了几次测试,误差在30-50%之间.例如,使用var testCoords1 = {'lat':40.7483126,'long': - 73.3314238}; var testCoords2 = {'lat':40.7385402,'long': - 73.3205462}; 它显示大约1.65英里(在GMaps上使用"测量距离"),但该函数返回0.88英里(我在函数中将km转换为mi).
将逻辑包装成一个函数,因此它应该可以在JS中使用,现在实际上在NodeJS中使用它...
所有这些测量都是最佳近似值.由于没有球体消融,我们可以忽略大圆距离计算中固有的误差.当您测量距离小于几百公里的点时,误差很小.当距离足够大以产生显着误差时,误差在实际应用中被其他因素淹没,例如交通状况或水流和气流.

2> Salvador Dal..:

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


@AngularM并且如果您将采取一些道路而不是直线,谷歌很可能计算距离.
@KhalilKhalaf你是在开玩笑还是想在这里玩?km代表公里.你认为R代表什么(特别是如果我们谈论一个shpere)?如果你已经看到km,那么猜猜答案是什么单位.你在这里寻找什么样的文件:那里有4行.
@Ouadie,它会提高速度吗?很可能没有,但是对于那些在旧浏览器中进行复制的人来说,我最终会遇到很多"你的东西不起作用"
好吧,但是什么叫`// 2*R; R = 6371 km`代表什么?而目前的方法提供公里或英里的答案?需要更好的文档.谢谢
谷歌计算行驶距离,计算"随着乌鸦飞行"
看来您找到了您从未想过的像我这样的人。我正在寻找的文档是另外一条评论,指出输出以km / m为单位,并且要对其进行更改,我们必须删除12742并放入6371,或者将12742乘以0.6237,或者清楚地表明cz不是。对我来说仍然不清楚。不好意思
@KhalilKhalaf:地球的直径为12,742 km(或直径/ 2 =半径= R = 6,371).因此,为了获得里程,您可以将Python函数的最后一行更改为"返回7918*asin(sqrt(a))"*或*您可以将km输出乘以0.6237.

3> jaircazarin-..:

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

}


您正在使用赤道半径,但您应该使用平均半径,即6371 km
不应该是`double dlon = Radians(lon2 - lon1);`和`double dlat = Radians(lat2 - lat1);`

4> 小智..:

这是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.



5> Stephen Wats..:

非常感谢所有这一切.我在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;
}



6> tony gil..:

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

'.$km;
    return $km;
}
?>

如前所述; 地球不是球体.这就像Mark McGwire决定练习的旧式棒球一样 - 它充满了凹痕和颠簸.更简单的计算(像这样)将其视为一个球体.

根据你在这个不规则卵形上的位置,不同的方法可能或多或少精确到你的点有多远(它们越接近绝对误差范围).您的期望越精确,数学就越复杂.

有关更多信息:维基百科地理距离


这很完美!我刚添加了$ distance_miles = $ km*0.621371; 这就是我所需要的近似英里距离!谢谢托尼.

7> conualfy..:

我在这里发布我的工作示例.

列出表格中的所有点,其中指定点之间的距离(我们使用随机点 - 纬度: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)中进行了测试.



8> Jaap..:

在其他答案中,缺少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)
}



9> Arturo Herna..:

对于大多数情况来说,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公式.



10> Andre Cytryn..:

您可以使用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.



11> 小智..:

我不想添加另一个答案,但Google maps API v.3具有球形几何(以及更多).将WGS84转换为十进制度后,您可以执行以下操作:

  

distance = google.maps.geometry.spherical.computeDistanceBetween(
    new google.maps.LatLng(fromLat, fromLng), 
    new google.maps.LatLng(toLat, toLng));

没有关于谷歌的计算是多么准确甚至使用什么模型的说法(虽然它确实说"球形"而不是"大地水准面".顺便说一下,如果一个人在旅行中,"直线"距离显然会与距离不同.每个人似乎都在假设的地球表面.



12> invoketheshe..:

Python的实现Origin是连续的美国的中心。

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

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



13> Meymann..:

可能有一个更简单的解决方案,更正确:赤道上的地球周长为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));
};

我同意它应该被微调,因为我自己说它是一个椭球,所以乘以余弦的半径会有所不同.但它更准确一点.与谷歌地图相比,它确实显着减少了错误.



14> Keerthana Go..:

所有上述答案都假设地球是一个球体.然而,更精确的近似将是扁球体的近似.

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



15> Sel..:

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



16> Chong Lip Ph..:

如前所述,准确的计算应考虑到地球不是一个完美的球体。以下是此处提供的各种算法的一些比较:

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



17> Kiran Maniya..:

这是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脚本

推荐阅读
和谐啄木鸟
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有