緯度/経度に基づいて2点間の距離を取得する


156

私はこの式を実装してみました:http ://andrew.hedges.name/experiments/haversine/アプレットは私がテストしている2つのポイントに役立ちます:

ここに画像の説明を入力してください

それでも私のコードは機能していません。

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です。どうして?

回答:


206

編集:ちょうどメモとして、2つのポイント間の距離をすばやく簡単に見つける方法が必要な場合は、カートの回答で説明されているアプローチを使用することを強くお勧めします Haversineを再実装するのではなく、以下のます-根拠については彼の投稿を参照してください。

この回答は、OPが遭遇した特定のバグへの回答にのみ焦点を当てています。


Pythonでは、すべてのトリガー関数が がラジアンを使用するためです度ではなくです。

数値を手動でラジアンに変換するかradians、mathモジュールの関数を使用できます。

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 2013年

11
賢明な言葉として、この式はすべての学位が正であることを要求します。radians(abs(52.123))トリックをする必要があります...
リチャード・ダン

1
すべての角度(角度?)が正であると確信していますか?これは間違っていると思います。lat1、lon1 = 10、10(度)およびlat2、lon2 = -10、-10(度)かどうかを検討します。度の周りにabs()を追加すると、距離はゼロになり、これは正しくありません。おそらくdlonやdlatの絶対値を取るつもりだったかもしれませんが、aの計算でdlon、dlatの値を見ると、正弦は偶関数であり、余弦二乗は偶関数なので、私はしませんdlatまたはdlonのいずれかの絶対値を取ることの利点を参照してください。
Dave LeCompte

238

更新:2018年4月: GeoPyバージョン1.13以降、Vincentyの距離は廃止されました。代わりにgeopy.distance.distance()を使用してください。


上記の答えは、地球が球であると仮定して、最大約0.5%の誤差をもたらすHaversine式に基づいています(によるとhelp(geopy.distance))。ビンセンティ距離は、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つを選択することもできます)。


1
ありがとう。ニューポートとクリーブランドの代わりに、私が問題に提供した座標で回答を更新してください。それは、将来の読者により良い理解を与えるでしょう。
gwaramadze

1
ニューポートとクリーブランドの任意の場所は、PyPIリストのgeopyドキュメントの例に由来します:pypi.python.org/pypi/geopy
Jason Parham

私はこれにクルトかいま見の答えを変更する必要がありました:資本金が必要:print geopy.distance.VincentyDistance(coords_1, coords_2).km 279.352901604
ジム・

4
おそらくgeopy.distance.distance(…)現在最も良い(=最も正確な)距離式のエイリアスであるコードで使用する必要があります。(現時点ではVincentyです。)
2018年

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

彼らは2つのベクトルのすべてのポイント間の距離のパフォーマンスを最適化すると主張しています

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

ポイントの1つの所定の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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.