Python'da Haversine Formülü (İki GPS noktası arasında Kerteriz ve Mesafe)


119

Sorun

2 GPS noktası arasındaki mesafeyi ve yönü nasıl öğreneceğimi bilmek istiyorum . Haversine formülünü araştırdım. Biri bana aynı verileri kullanarak yatağı da bulabileceğimi söyledi.

Düzenle

Her şey yolunda gidiyor, ancak yatak henüz tam olarak çalışmıyor. Yatak negatif çıktı verir ancak 0 - 360 derece arasında olmalıdır. Ayarlanan veriler yatay yönü yapmalıdır 96.02166666666666 ve şu şekildedir :

Start point: 53.32055555555556 , -1.7297222222222221   
Bearing:  96.02166666666666  
Distance: 2 km  
Destination point: 53.31861111111111, -1.6997222222222223  
Final bearing: 96.04555555555555

İşte yeni kodum:

from math import *

Aaltitude = 2000
Oppsite  = 20000

lat1 = 53.32055555555556
lat2 = 53.31861111111111
lon1 = -1.7297222222222221
lon2 = -1.6997222222222223

lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])

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))
Base = 6371 * c


Bearing =atan2(cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(lon2-lon1), sin(lon2-lon1)*cos(lat2)) 

Bearing = degrees(Bearing)
print ""
print ""
print "--------------------"
print "Horizontal Distance:"
print Base
print "--------------------"
print "Bearing:"
print Bearing
print "--------------------"


Base2 = Base * 1000
distance = Base * 2 + Oppsite * 2 / 2
Caltitude = Oppsite - Aaltitude

a = Oppsite/Base
b = atan(a)
c = degrees(b)

distance = distance / 1000

print "The degree of vertical angle is:"
print c
print "--------------------"
print "The distance between the Balloon GPS and the Antenna GPS is:"
print distance
print "--------------------"

Python haversinüs uygulama bulunabilir codecodex.com/wiki/... . Ancak kısa mesafe hesaplamaları için çok basit yollar mevcuttur. Şimdi, beklenen maksimum mesafeniz nedir? Koordinatlarınızı bazı yerel kartezyen koordinat sistemlerinde alabiliyor musunuz?
yiyin


1
@James Dyson: 15km gibi mesafelerde, yaratıcı daire hiçbir şeyi saymaz. Önerim: Önce öklid mesafeleri ile çözümü bulun! Bu size çalışan bir çözüm sağlayacaktır ve daha sonra mesafeleriniz çok daha uzun olacaksa, uygulamanızı ayarlayın. Teşekkürler
yiyin

1
@James Dyson: Eğer yukarıdaki yorumunuz beni hedef almışsa (ve önceki önerime), cevap kesinlikle (ve oldukça 'önemsiz'). Bazı örnek kodlar verebilirim, ancak trigonometri yerine geometri kullanmayacak (bu yüzden size yardımcı olup olmayacağından emin değilim. Vektör kavramına aşina mısınız? Vaka konumlarınızda ve yönler, vektörlerle en basit şekilde ele alınabilir).
yemek

1
atan2(sqrt(a), sqrt(1-a))ile aynıasin(sqrt(a))
user102008

Yanıtlar:


241

İşte bir Python sürümü:

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

def haversine(lon1, lat1, lon2, lat2):
    """
    Calculate the great circle distance between two points 
    on the earth (specified in decimal degrees)
    """
    # convert decimal degrees to radians 
    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])

    # haversine formula 
    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a)) 
    r = 6371 # Radius of earth in kilometers. Use 3956 for miles
    return c * r

10
Pi / 180 ile çarpmak yerine math.radians () işlevi kullanılabilir - aynı etki, ancak biraz daha kendini belgeleme.
Hugh Bothwell

4
Yapabilirsiniz, ancak söylerseniz import mathbelirtmek zorundasınız math.pi, math.sinvb. İle from math import *tüm modül içeriğine doğrudan erişebilirsiniz. Bir python öğreticisindeki "ad alanlarına" göz atın ( docs.python.org/tutorial/modules.html gibi )
Michael Dunn

2
Nasıl oluyor da sadece asin (sqrt (a)) yerine atan2 (sqrt (a), sqrt (1-a)) kullanıyorsunuz? Bu durumda atan2 daha mı doğru?
Eyal

4
Ortalama Dünya yarıçapı 6371 km olarak tanımlanırsa, bu 3956 mile değil 3959 mile eşdeğerdir. Bu değerleri hesaplamanın çeşitli yolları için Küresel ortalama yarıçaplara bakın .
ekhumoro

3
bu ne dönüyor? Kerteriz mi, mesafe mi?
AesculusMaximus

11

Bu yanıtların çoğu dünyanın yarıçapını "yuvarlamaktır". Bunları diğer mesafe hesaplayıcılarla (jeopi gibi) karşılaştırırsanız, bu işlevler kapanacaktır.

Bu iyi çalışıyor:

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

def haversine(lat1, lon1, lat2, lon2):

      R = 3959.87433 # this is in miles.  For Earth radius in kilometers use 6372.8 km

      dLat = radians(lat2 - lat1)
      dLon = radians(lon2 - lon1)
      lat1 = radians(lat1)
      lat2 = radians(lat2)

      a = sin(dLat/2)**2 + cos(lat1)*cos(lat2)*sin(dLon/2)**2
      c = 2*asin(sqrt(a))

      return R * c

# Usage
lon1 = -103.548851
lat1 = 32.0004311
lon2 = -103.6041946
lat2 = 33.374939

print(haversine(lat1, lon1, lat2, lon2))

2
Bu, yukarıdaki örneklerden çok daha doğrudur!
Alex van Es

Bu, kutuplarda 6356.752 km'den ekvatorda 6378.137 km'ye R'nin varyasyonunu ele almıyor
ldmtwo

3
Bu hata başvurunuz için gerçekten önemli mi? cs.nyu.edu/visual/home/proj/tiger/gisfaq.html
Tejas Kale

8

Koordinatlar için skaler değerler yerine 4 numpy dizisinin kullanılmasına izin veren vektörleştirilmiş bir uygulama da vardır :

def distance(s_lat, s_lng, e_lat, e_lng):

   # approximate radius of earth in km
   R = 6373.0

   s_lat = s_lat*np.pi/180.0                      
   s_lng = np.deg2rad(s_lng)     
   e_lat = np.deg2rad(e_lat)                       
   e_lng = np.deg2rad(e_lng)  

   d = np.sin((e_lat - s_lat)/2)**2 + np.cos(s_lat)*np.cos(e_lat) * np.sin((e_lng - s_lng)/2)**2

   return 2 * R * np.arcsin(np.sqrt(d))

5

Yön hesaplaması yanlış, girişleri atan2'ye değiştirmeniz gerekiyor.

    bearing = atan2(sin(long2-long1)*cos(lat2), cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(long2-long1))
    bearing = degrees(bearing)
    bearing = (bearing + 360) % 360

Bu size doğru yönü verecektir.


Aslında bir makale okurken bu denklemlerin nasıl türetildiğini anlamaya çalışıyorum. Bana bir işaret verdin: haversine formulabunu ilk kez duyduğumda, teşekkür ederim.
arilwan

4

Aşağıdakileri deneyebilirsiniz:

from haversine import haversine
haversine((45.7597, 4.8422),(48.8567, 2.3508), unit='mi')
243.71209416020253

Bu, Django'nun ORM sorgusunda nasıl kullanılabilir?
Gocht

3

İşte @ Michael Dunn tarafından verilen Haversine Formülünün, büyük vektörlere göre 10-50 kat iyileştirme sağlayan bir uyuşmuş vektörleştirilmiş uygulaması.

from numpy import radians, cos, sin, arcsin, sqrt

def haversine(lon1, lat1, lon2, lat2):
    """
    Calculate the great circle distance between two points 
    on the earth (specified in decimal degrees)
    """

    #Convert decimal degrees to Radians:
    lon1 = np.radians(lon1.values)
    lat1 = np.radians(lat1.values)
    lon2 = np.radians(lon2.values)
    lat2 = np.radians(lat2.values)

    #Implementing Haversine Formula: 
    dlon = np.subtract(lon2, lon1)
    dlat = np.subtract(lat2, lat1)

    a = np.add(np.power(np.sin(np.divide(dlat, 2)), 2),  
                          np.multiply(np.cos(lat1), 
                                      np.multiply(np.cos(lat2), 
                                                  np.power(np.sin(np.divide(dlon, 2)), 2))))
    c = np.multiply(2, np.arcsin(np.sqrt(a)))
    r = 6371

    return c*r

2

Negatif yatak problemini 360 ° ekleyerek çözebilirsiniz. Ne yazık ki bu, pozitif rulmanlar için 360 ° 'den daha büyük rulmanlarla sonuçlanabilir. Bu, modulo operatörü için iyi bir adaydır, bu nedenle sonuçta satırı eklemelisiniz

Bearing = (Bearing + 360) % 360

yönteminizin sonunda.


1
Sanırım sadece: Bearing = Bearing% 360
Holger Bille

1

Atan2'deki Y varsayılan olarak ilk parametredir. İşte belgeler . Doğru yön açısı elde etmek için girişlerinizi değiştirmeniz gerekecektir.

bearing = atan2(sin(lon2-lon1)*cos(lat2), cos(lat1)*sin(lat2)in(lat1)*cos(lat2)*cos(lon2-lon1))
bearing = degrees(bearing)
bearing = (bearing + 360) % 360


0

Burada, önceki iletilerdeki koda dayanan mesafe ve kerterizi hesaplamak için iki işlev ve https://gist.github.com/jeromer/2005586 (netlik için her iki işlev için enlem, lon biçiminde coğrafi noktalar için tuple türü eklendi) ). Her iki işlevi de test ettim ve doğru çalışıyor gibi görünüyorlar.

#coding:UTF-8
from math import radians, cos, sin, asin, sqrt, atan2, degrees

def haversine(pointA, pointB):

    if (type(pointA) != tuple) or (type(pointB) != tuple):
        raise TypeError("Only tuples are supported as arguments")

    lat1 = pointA[0]
    lon1 = pointA[1]

    lat2 = pointB[0]
    lon2 = pointB[1]

    # convert decimal degrees to radians 
    lat1, lon1, lat2, lon2 = map(radians, [lat1, lon1, lat2, lon2]) 

    # haversine formula 
    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a)) 
    r = 6371 # Radius of earth in kilometers. Use 3956 for miles
    return c * r


def initial_bearing(pointA, pointB):

    if (type(pointA) != tuple) or (type(pointB) != tuple):
        raise TypeError("Only tuples are supported as arguments")

    lat1 = radians(pointA[0])
    lat2 = radians(pointB[0])

    diffLong = radians(pointB[1] - pointA[1])

    x = sin(diffLong) * cos(lat2)
    y = cos(lat1) * sin(lat2) - (sin(lat1)
            * cos(lat2) * cos(diffLong))

    initial_bearing = atan2(x, y)

    # Now we have the initial bearing but math.atan2 return values
    # from -180° to + 180° which is not what we want for a compass bearing
    # The solution is to normalize the initial bearing as shown below
    initial_bearing = degrees(initial_bearing)
    compass_bearing = (initial_bearing + 360) % 360

    return compass_bearing

pA = (46.2038,6.1530)
pB = (46.449, 30.690)

print haversine(pA, pB)

print initial_bearing(pA, pB)

bu yöntem, yukarıdaki tüm diğer yöntemlerden farklı sonuçlar verir!
basilisk
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.