web-dev-qa-db-fra.com

Obtenir la distance entre deux points en fonction de la latitude / longitude

J'ai essayé d'implémenter cette formule: http://andrew.hedges.name/experiments/haversine/ L'aplet fait du bien aux deux points que je teste:

enter image description here

Pourtant, mon code ne fonctionne pas.

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

La distance qu'il retourne est 5447.05546147 . Pourquoi?

110
gwaramadze

Modifiez: Remarque: si vous avez simplement besoin d’un moyen simple et rapide de déterminer la distance entre deux points, je vous recommande vivement d’utiliser l’approche décrite dans réponse de Kurt ci-dessous au lieu de ré-implémenter Haversine - voir son post pour les raisons.

Cette réponse se concentre uniquement sur le bogue spécifique rencontré par l'OP.


C'est parce qu'en Python, toutes les fonctions trigonométriques tilisent des radians , pas des degrés.

Vous pouvez convertir les nombres manuellement en radians ou utiliser la fonction radians du module mathématique:

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

La distance renvoie maintenant la valeur correcte de 278.545589351 km.

163
Michael0x2a

Mise à jour: 04/2018: Notez que la distance de Vincenty est obsolète depuis la version de GeoPy 1.1 - vous devez utiliser geopy.distance.distance. () au lieu!


Les réponses ci-dessus sont basées sur le formule de Haversine , qui suppose que la Terre est une sphère, ce qui entraîne des erreurs pouvant atteindre environ 0,5% (selon help(geopy.distance)). Vincenty distance utilise des modèles ellipsoïdaux plus précis, tels que WGS-84 , et est implémenté dans geopy . Par exemple,

import geopy.distance

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

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

imprimera la distance de 279.352901604 kilomètres en utilisant l’ellipsoïde par défaut WGS-84. (Vous pouvez également choisir .miles ou l'une des autres unités de distance).

163
Kurt Peek

Pour les personnes (comme moi) venant ici via un moteur de recherche et cherchant une solution qui fonctionne immédiatement, je recommande l'installation de mpu . Installez-le via pip install mpu --user et utilisez-le comme ceci pour obtenir le distance haversine :

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.

Un autre package est gpxpy .

Si vous ne voulez pas de dépendances, vous pouvez utiliser:

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 __== '__main__':
    import doctest
    doctest.testmod()
69
Martin Thoma

Je suis arrivé à une solution beaucoup plus simple et robuste qui utilise le paquet geodesic de geopy car vous l'utiliserez très probablement dans votre projet de toute façon, aucune installation de paquet supplémentaire n'est donc nécessaire.

Voici ma solution:

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

2
Ramy Mohamed
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)
0
Sudhirln92