Haversine-Formel in Python (Peilung und Abstand zwischen zwei GPS-Punkten)


119

Problem

Ich würde gerne wissen, wie man die Entfernung und Peilung zwischen 2 GPS-Punkten ermittelt . Ich habe über die Haversinformel recherchiert. Jemand sagte mir, dass ich das Lager auch mit denselben Daten finden könnte.

Bearbeiten

Alles funktioniert gut, aber das Lager funktioniert noch nicht ganz richtig. Das Lager ist negativ, sollte aber zwischen 0 und 360 Grad liegen. Die eingestellten Daten sollten das horizontale Lager bilden 96.02166666666666 und sind:

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

Hier ist mein neuer Code:

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

Die Implementierung von Python Haversine finden Sie unter codecodex.com/wiki/… . Für Kurzstreckenberechnungen gibt es jedoch sehr einfache Möglichkeiten. Was ist nun Ihre maximale Entfernung erwartet? Können Sie Ihre Koordinaten in einem lokalen kartesischen Koordinatensystem abrufen?
Essen


1
@ James Dyson: Bei Entfernungen wie 15 km zählt der Erstellungskreis nichts. Mein Vorschlag: Finde zuerst die Lösung mit euklidischen Abständen heraus! Dadurch erhalten Sie eine funktionierende Lösung. Wenn Ihre Entfernungen später viel länger sind, passen Sie Ihre Anwendung an. Danke
essen Sie

1
@ James Dyson: Wenn Ihr obiger Kommentar auf mich (und auf meinen früheren Vorschlag) gerichtet war, ist die Antwort sicher (und auch ziemlich "trivial"). Ich kann vielleicht einen Beispielcode angeben, aber er verwendet keine Trigonometrie, sondern Geometrie (daher bin ich mir nicht sicher, ob er Ihnen überhaupt hilft. Kennen Sie das Konzept des Vektors überhaupt? In Ihren Fallpositionen und Richtungen könnten mit Vektoren am einfachsten gehandhabt werden.
essen

1
atan2(sqrt(a), sqrt(1-a))ist das gleiche wieasin(sqrt(a))
user102008

Antworten:


241

Hier ist eine Python-Version:

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
Könnte die Funktion math.radians () verwenden, anstatt mit pi / 180 zu multiplizieren - der gleiche Effekt, aber etwas selbstdokumentierender.
Hugh Bothwell

4
Sie können, aber wenn Sie sagen, import mathdann müssen Sie angeben math.pi, math.sinetc. Mit erhalten from math import *Sie direkten Zugriff auf alle Modulinhalte. Überprüfen Sie "Namespaces" in einem Python-Tutorial (wie docs.python.org/tutorial/modules.html )
Michael Dunn

2
Wie kommt es, dass Sie atan2 (sqrt (a), sqrt (1-a)) anstelle von asin (sqrt (a)) verwenden? Ist atan2 in diesem Fall genauer?
Eyal

4
Wenn der mittlere Erdradius als 6371 km definiert ist, entspricht dies 3959 Meilen und nicht 3956 Meilen. Unter Globale Durchschnittsradien finden Sie verschiedene Möglichkeiten zur Berechnung dieser Werte.
Ekhumoro

3
Was ist das für eine Rückkehr? Die Peilung oder die Entfernung?
Aesculus

11

Die meisten dieser Antworten "runden" den Radius der Erde. Wenn Sie diese mit anderen Entfernungsrechnern (z. B. Geopy) vergleichen, sind diese Funktionen deaktiviert.

Das funktioniert gut:

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
Dieser ist viel genauer als die obigen Beispiele!
Alex van Es

Dies betrifft nicht die Variation von R. 6356.752 km an den Polen zu 6378.137 km am Äquator
Uhr

3
Ist dieser Fehler für Ihre Anwendung wirklich wichtig? cs.nyu.edu/visual/home/proj/tiger/gisfaq.html
Tejas Kale

8

Es gibt auch eine vektorisierte Implementierung , die die Verwendung von 4 Numpy-Arrays anstelle von Skalarwerten für Koordinaten ermöglicht:

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

4

Die Lagerberechnung ist falsch, Sie müssen die Eingänge auf atan2 tauschen.

    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

Dadurch erhalten Sie das richtige Lager.


Ich habe tatsächlich Schwierigkeiten zu verstehen, wie diese Gleichungen abgeleitet wurden, während ich eine Zeitung lese. Sie haben mir einen Hinweis gegeben: haversine formulaIch habe das zum ersten Mal gehört, danke.
Arilwan

4

Sie können Folgendes versuchen:

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

Wie kann dies in der ORM-Abfrage eines Django verwendet werden?
Gocht

3

Hier ist eine numpy vektorisierte Implementierung der Haversine-Formel von @Michael Dunn, die eine 10- bis 50-fache Verbesserung gegenüber großen Vektoren ergibt.

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

Sie können das negative Lagerproblem lösen, indem Sie 360 ​​° hinzufügen. Leider kann dies bei positiven Lagern zu Lagern führen, die größer als 360 ° sind. Dies ist ein guter Kandidat für den Modulo-Operator. Alles in allem sollten Sie also die Zeile hinzufügen

Bearing = (Bearing + 360) % 360

am Ende Ihrer Methode.


1
Ich denke, es ist nur: Lager = Lager% 360
Holger Bille

1

Das Y in atan2 ist standardmäßig der erste Parameter. Hier ist die Dokumentation . Sie müssen Ihre Eingänge umschalten, um den richtigen Peilwinkel zu erhalten.

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

Hier sind zwei Funktionen zur Berechnung von Entfernung und Peilung, die auf dem Code in früheren Nachrichten und https://gist.github.com/jeromer/2005586 basieren (der Übersichtlichkeit halber wurde für beide Funktionen ein Tupeltyp für geografische Punkte im Lat-Lon-Format hinzugefügt ). Ich habe beide Funktionen getestet und sie scheinen richtig zu funktionieren.

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

Diese Methode liefert andere Ergebnisse als alle anderen oben genannten Methoden!
Basilisk
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.