根据纬度/经度获取两点之间的距离


156

我尝试实现此公式:http ://andrew.hedges.name/experiments/haversine/ aplet可以很好地满足我测试的两点要求:

在此处输入图片说明

但是我的代码无法正常工作。

from math import sin, cos, sqrt, atan2

R = 6373.0

lat1 = 52.2296756
lon1 = 21.0122287
lat2 = 52.406374
lon2 = 16.9251681

dlon = lon2 - lon1
dlat = lat2 - lat1
a = (sin(dlat/2))**2 + cos(lat1) * cos(lat2) * (sin(dlon/2))**2
c = 2 * atan2(sqrt(a), sqrt(1-a))
distance = R * c

print "Result", distance
print "Should be", 278.546

它返回的距离是5447.05546147。为什么?

Answers:


206

编辑:谨记一下,如果您只需要一种快速简便的方法来找到两点之间的距离,我强烈建议您使用下面库尔特答案中描述的方法,而不是重新实现Haversine-有关原理,请参阅他的帖子。

该答案仅专注于回答OP遇到的特定错误。


这是因为在Python中,所有trig函数都使用弧度而不是度。

您可以将数字手动转换为弧度,也可以使用radiansmath模块中的函数:

from math import sin, cos, sqrt, atan2, radians

# approximate radius of earth in km
R = 6373.0

lat1 = radians(52.2296756)
lon1 = radians(21.0122287)
lat2 = radians(52.406374)
lon2 = radians(16.9251681)

dlon = lon2 - lon1
dlat = lat2 - lat1

a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
c = 2 * atan2(sqrt(a), sqrt(1 - a))

distance = R * c

print("Result:", distance)
print("Should be:", 278.546, "km")

现在,距离返回正确的278.545589351km 值。


13
在任何编程语言和微积分中都是如此。使用度数是例外,仅用于人类语音。
bluesmoon

11
明智地讲,此公式要求所有度均为正。radians(abs(52.123))应该做到这一点...
理查德·邓恩

1
您确定所有角度(角度?)都为正吗?我认为这是错误的。考虑lat1,lon1 = 10,10(度)和lat2,lon2 = -10,-10(度)。通过在度数周围添加abs(),距离将为零,这是不正确的。也许您打算获取dlon和/或dlat的绝对值,但是如果您查看dlon,则在计算a时,dlat值的正弦是偶函数,而余弦平方是偶函数,所以我不知道看到采用dlat或dlon绝对值的任何好处。
Dave LeCompte

238

更新:04/2018:请注意,自GeoPy版本1.13起,不建议使用Vincenty距离-您应该改用geopy.distance.distance()!


上面的答案基于Haversine公式,该公式假定地球是一个球体,其误差最高可达0.5%(根据help(geopy.distance))。Vincenty距离使用更精确的椭圆模型(例如WGS-84),并在geopy中实现。例如,

import geopy.distance

coords_1 = (52.2296756, 21.0122287)
coords_2 = (52.406374, 16.9251681)

print geopy.distance.vincenty(coords_1, coords_2).km

279.352901604使用默认的椭球WGS-84 打印公里的距离。(您也可以选择.miles或选择其他几种距离单位之一)。


1
谢谢。您能用我提供的座标代替纽波特和克利夫兰来更新您的答案吗?它将为将来的读者提供更好的理解。
gwaramadze

1
纽波特和克利夫兰的任意位置均来自PyPI列表中的示例geopy文档:pypi.python.org/pypi/geopy
Jason Parham

我必须修改Kurt Peek的答案:大写要求:print geopy.distance.VincentyDistance(coords_1, coords_2).km 279.352901604
Jim Jim

4
您可能应该geopy.distance.distance(…)在代码中使用它作为当前最佳(=最准确)距离公式的别名。(目前为Vincenty。)
出生

10
在geopy-1.18.1输出中使用geopy.distance.vincenty:Vincenty已过时,将在geopy 2.0中删除。使用geopy.distance.geodesic(或默认值geopy.distance.distance)代替,它更准确并且始终收敛。
juanmah

88

对于喜欢通过搜索引擎来到这里的人(例如我),他们只是想寻找一个开箱即用的解决方案,建议安装mpu。通过进行安装,pip install mpu --user并像这样使用以获得正弦距离

import mpu

# Point one
lat1 = 52.2296756
lon1 = 21.0122287

# Point two
lat2 = 52.406374
lon2 = 16.9251681

# What you were looking for
dist = mpu.haversine_distance((lat1, lon1), (lat2, lon2))
print(dist)  # gives 278.45817507541943.

另一个包是gpxpy

如果您不想依赖,可以使用:

import math


def distance(origin, destination):
    """
    Calculate the Haversine distance.

    Parameters
    ----------
    origin : tuple of float
        (lat, long)
    destination : tuple of float
        (lat, long)

    Returns
    -------
    distance_in_km : float

    Examples
    --------
    >>> origin = (48.1372, 11.5756)  # Munich
    >>> destination = (52.5186, 13.4083)  # Berlin
    >>> round(distance(origin, destination), 1)
    504.2
    """
    lat1, lon1 = origin
    lat2, lon2 = destination
    radius = 6371  # km

    dlat = math.radians(lat2 - lat1)
    dlon = math.radians(lon2 - lon1)
    a = (math.sin(dlat / 2) * math.sin(dlat / 2) +
         math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) *
         math.sin(dlon / 2) * math.sin(dlon / 2))
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
    d = radius * c

    return d


if __name__ == '__main__':
    import doctest
    doctest.testmod()

另一个替代软件包是 [haversine][1]

from haversine import haversine, Unit

lyon = (45.7597, 4.8422) # (lat, lon)
paris = (48.8567, 2.3508)

haversine(lyon, paris)
>> 392.2172595594006  # in kilometers

haversine(lyon, paris, unit=Unit.MILES)
>> 243.71201856934454  # in miles

# you can also use the string abbreviation for units:
haversine(lyon, paris, unit='mi')
>> 243.71201856934454  # in miles

haversine(lyon, paris, unit=Unit.NAUTICAL_MILES)
>> 211.78037755311516  # in nautical miles

他们声称对两个向量中所有点之间的距离进行了性能优化

from haversine import haversine_vector, Unit

lyon = (45.7597, 4.8422) # (lat, lon)
paris = (48.8567, 2.3508)
new_york = (40.7033962, -74.2351462)

haversine_vector([lyon, lyon], [paris, new_york], Unit.KILOMETERS)

>> array([ 392.21725956, 6163.43638211])

有没有一种方法可以改变给定的Highet点之一?
yovel cohen

您可以简单地将高差添加到距离中。不过,我不会这样做。
马丁·托马

16

我提供了一个更简单,更强大的解决方案,该解决方案可以geodesicgeopy软件包中使用,因为无论如何您很有可能在项目中使用它,因此不需要额外的软件包安装。

这是我的解决方案:

from geopy.distance import geodesic


origin = (30.172705, 31.526725)  # (latitude, longitude) don't confuse
dist = (30.288281, 31.732326)

print(geodesic(origin, dist).meters)  # 23576.805481751613
print(geodesic(origin, dist).kilometers)  # 23.576805481751613
print(geodesic(origin, dist).miles)  # 14.64994773134371

几何


5
import numpy as np


def Haversine(lat1,lon1,lat2,lon2, **kwarg):
    """
    This uses the ‘haversine’ formula to calculate the great-circle distance between two points – that is, 
    the shortest distance over the earth’s surface – giving an ‘as-the-crow-flies’ distance between the points 
    (ignoring any hills they fly over, of course!).
    Haversine
    formula:    a = sin²(Δφ/2) + cos φ1 ⋅ cos φ2 ⋅ sin²(Δλ/2)
    c = 2 ⋅ atan2( √a, √(1−a) )
    d = R ⋅ c
    where   φ is latitude, λ is longitude, R is earth’s radius (mean radius = 6,371km);
    note that angles need to be in radians to pass to trig functions!
    """
    R = 6371.0088
    lat1,lon1,lat2,lon2 = map(np.radians, [lat1,lon1,lat2,lon2])

    dlat = lat2 - lat1
    dlon = lon2 - lon1
    a = np.sin(dlat/2)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2) **2
    c = 2 * np.arctan2(a**0.5, (1-a)**0.5)
    d = R * c
    return round(d,4)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.