Enlem / boylama göre iki nokta arasındaki mesafeyi elde etme


175

Bu formülü uygulamayı denedim: http://andrew.hedges.name/experiments/haversine/ Aplet, test ettiğim iki nokta için iyidir:

görüntü açıklamasını buraya girin

Yine de kodum çalışmıyor.

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

Döndüğü mesafe 5447.05546147'dir . Neden?


@gwarmadze kabul edilen cevabı değiştirir misiniz lütfen?
Boris

Yanıtlar:


224

Düzenleme: Bir not olarak, iki nokta arasındaki mesafeyi bulmanın hızlı ve kolay bir yoluna ihtiyacınız varsa, Haversine'i yeniden uygulamak yerine aşağıdaki Kurt'un yanıtında açıklanan yaklaşımı kullanmanızı şiddetle tavsiye ederim - gerekçesini görmek için gönderisine bakın.

Bu cevap, sadece OP'nin karşılaştığı belirli hatayı yanıtlamaya odaklanıyor.


Bunun nedeni Python'da tüm trigonometrik fonksiyonların derece değil radyan kullanmasıdır .

Sayıları manuel olarak radyana dönüştürebilir veya radiansmatematik modülündeki işlevi kullanabilirsiniz :

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

Mesafe şimdi doğru 278.545589351km değerini döndürüyor .


13
bu, herhangi bir programlama dilinde ve ayrıca diferansiyel hesaplamada geçerlidir. derece kullanmak istisnadır ve sadece insan konuşmasında kullanılır.
bluesmoon

11
Bilge bir söz, bu formül tüm derecelerin pozitif olmasını gerektirir. radians(abs(52.123))hile yapmalı ...
Richard Dunn

1
Tüm derecelerin (açıların) pozitif olduğundan emin misiniz? Bence bu yanlış. Lat1, lon1 = 10, 10 (derece) ve lat2, lon2 = -10, -10 (derece) olduğunu düşünün. Derecelerin etrafına bir abs () eklediğinizde mesafe sıfır olur ve bu da yanlıştır. Belki de dlon ve / veya dlat'ın mutlak değerini almak istediniz, ancak dlon'a bakarsanız, a'nın hesaplamasındaki dlat değerleri, sinüs çift bir fonksiyondur ve kosinüs kare bir çift fonksiyondur, bu yüzden yapmam Mutlak bir dlat veya dlon değeri almanın herhangi bir yararı olduğunu görün.
Dave LeCompte

258

Güncelleme: 04/2018: Not VINCENTY mesafe beri kaldırılmış olması GeoPy sürüm 1.13 - Eğer () yerine geopy.distance.distance kullanmalısınız!


Yukarıdaki cevaplar , dünyanın bir küre olduğunu varsayan Haversine formülüne dayanmaktadır ve bu da (göre help(geopy.distance)) yaklaşık% 0,5'e varan hatalarla sonuçlanmaktadır . Vincenty mesafesi , WGS-84 gibi daha doğru elipsoidal modeller kullanır ve geopy'de uygulanır . Örneğin,

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.352901604kilometre mesafesini varsayılan elipsoid WGS-84 kullanarak yazdıracaktır . (Ayrıca .milesbirkaç başka mesafe biriminden birini veya birini de seçebilirsiniz ).


1
Teşekkürler. Lütfen cevabınızı Newport ve Cleveland yerine söz konusu verdiğim koordinatlarla günceller misiniz? Gelecekteki okuyuculara daha iyi bir anlayış verecektir.
gwaramadze

1
Newport ve Cleveland'ın rastgele konumları, PyPI listesindeki örnek coğrafi kopya belgelerinden gelir: pypi.python.org/pypi/geopy
Jason Parham,

Kurt Peek'in buna cevabını değiştirmek zorunda kaldım: Büyük harf kullanımı gerekli:print geopy.distance.VincentyDistance(coords_1, coords_2).km 279.352901604
Jim

5
Muhtemelen geopy.distance.distance(…)şu anda en iyi (= en doğru) uzaklık formülünün bir takma adı olan kodda kullanmalısınız . (Vincenty şu anda.)
mbirth

14
Geopy-1.18.1 çıktılarında geopy.distance.vincenty kullanma: Vincenty kullanımdan kaldırıldı ve geopy 2.0'da kaldırılacak. Kullanım geopy.distance.geodesic(veya varsayılan geopy.distance.distance) daha doğru ve her zaman yakınsak olduğunu bunun yerine,.
juanmah

91

Buraya arama motoruyla gelen ve kutudan çıktığı gibi çalışan bir çözüm arayan insanlar (benim gibi) için kurmanızı tavsiye ederim mpu. Haversine mesafesinipip install mpu --user elde etmek için onu kurun ve şu şekilde kullanın :

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.

Alternatif bir paket gpxpy.

Bağımlılık istemiyorsanız şunları kullanabilirsiniz:

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

Diğer alternatif paket ise [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

İki vektördeki tüm noktalar arasındaki mesafeler için performans optimizasyonuna sahip olduklarını iddia ediyorlar

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

Noktalardan birinin verilen Highet değerini değiştirmenin bir yolu var mı?
yovel cohen

Yükseklik farkını mesafeye kolayca ekleyebilirsiniz. Yine de bunu yapmazdım.
Martin Thoma

"Lyon, Paris, 392.2172595594006 km", vay canına, son rakam bir hidrojen atomunun boyutu bile değil. Çok doğru!
dakika

17

Ben kullanan bir çok daha basit ve sağlam bir çözüm geldi geodesicdan geopysen zaten bu yüzden hiçbir ekstra paket kurulumu gerekli projenizde kullanmaktan büyük olasılıkla olacak kadar paket.

İşte benim çözümüm:

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

geopy


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

Merhaba, verileri doğrudan şablondan almak için hesaplama yapmanın bir yolu olduğunu düşünüyor musunuz?
Louis

5

Koordinatlara göre mesafeyi hesaplamanın birden fazla yolu vardır, yani enlem ve boylam

Kurun ve içe aktarın

from geopy import distance
from math import sin, cos, sqrt, atan2, radians
from sklearn.neighbors import DistanceMetric
import osrm
import numpy as np

Koordinatları tanımlayın

lat1, lon1, lat2, lon2, R = 20.9467,72.9520, 21.1702, 72.8311, 6373.0
coordinates_from = [lat1, lon1]
coordinates_to = [lat2, lon2]

Haversine kullanma

dlon = radians(lon2) - radians(lon1)
dlat = radians(lat2) - radians(lat1)
    
a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
c = 2 * atan2(sqrt(a), sqrt(1 - a))
    
distance_haversine_formula = R * c
print('distance using haversine formula: ', distance_haversine_formula)

Haversine ile sklearn kullanımı

dist = DistanceMetric.get_metric('haversine')
    
X = [[radians(lat1), radians(lon1)], [radians(lat2), radians(lon2)]]
distance_sklearn = R * dist.pairwise(X)
print('distance using sklearn: ', np.array(distance_sklearn).item(1))

OSRM'yi kullanma

osrm_client = osrm.Client(host='http://router.project-osrm.org')
coordinates_osrm = [[lon1, lat1], [lon2, lat2]] # note that order is lon, lat
    
osrm_response = osrm_client.route(coordinates=coordinates_osrm, overview=osrm.overview.full)
dist_osrm = osrm_response.get('routes')[0].get('distance')/1000 # in km
print('distance using OSRM: ', dist_osrm)

Geopy kullanma

distance_geopy = distance.distance(coordinates_from, coordinates_to).km
print('distance using geopy: ', distance_geopy)
    
distance_geopy_great_circle = distance.great_circle(coordinates_from, coordinates_to).km 
print('distance using geopy great circle: ', distance_geopy_great_circle)

Çıktı

distance using haversine formula:  26.07547017310917
distance using sklearn:  27.847882224769783
distance using OSRM:  33.091699999999996
distance using geopy:  27.7528030550408
distance using geopy great circle:  27.839182219511834
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.