Différence entre Vincenty et les calculs de distance du grand cercle?

16

Le package de géopopie de Python propose deux techniques de mesure de distance: les formules Great Circle et Vincenty .

>>> from geopy.distance import great_circle
>>> from geopy.distance import vincenty
>>> p1 = (31.8300167,35.0662833) # (lat, lon) - https://goo.gl/maps/TQwDd
>>> p2 = (31.8300000,35.0708167) # (lat, lon) - https://goo.gl/maps/lHrrg
>>> vincenty(p1, p2).meters
429.16765838976664
>>> great_circle(p3, p4).meters
428.4088367903001

Quelle est la différence? Quelle mesure de distance est préférée?

Adam Matan
la source

Réponses:

18

Selon Wikipedia, la formule de Vincenty est plus lente mais plus précise :

Les formules de Vincenty sont deux méthodes itératives connexes utilisées en géodésie pour calculer la distance entre deux points à la surface d'un sphéroïde, développées par Thaddeus Vincenty (1975a) .Elles sont basées sur l'hypothèse que la figure de la Terre est un sphéroïde oblat, et donc sont plus précises que des méthodes telles que la distance du grand cercle qui supposent une Terre sphérique.

La différence de précision se situe ~0.17%à une distance de 428 mètres en Israël. J'ai fait un test de vitesse rapide et sale:

<class 'geopy.distance.vincenty'>       : Total 0:00:04.125913, (0:00:00.000041 per calculation)
<class 'geopy.distance.great_circle'>   : Total 0:00:02.467479, (0:00:00.000024 per calculation)

Code:

import datetime
from geopy.distance import great_circle
from geopy.distance import vincenty
p1 = (31.8300167,35.0662833)
p2 = (31.83,35.0708167)

NUM_TESTS = 100000
for strategy in vincenty, great_circle:
    before = datetime.datetime.now()
    for i in range(NUM_TESTS):
        d=strategy(p1, p2).meters
    after = datetime.datetime.now()
    duration = after-before
    print "%-40s: Total %s, (%s per calculation)" % (strategy, duration, duration/NUM_TESTS)

Pour conclure: la formule de Vincenty double le temps de calcul par rapport au grand cercle, et son gain de précision au point testé est de ~ 0,17%.

Le temps de calcul étant négligeable, la formule de Vincenty est préférée pour chaque besoin pratique.

Mise à jour : Suite aux commentaires perspicaces de whuber et de la réponse de cffk et de cffk , je suis d'accord que le gain de précision doit être comparé à l'erreur, pas à la mesure. Par conséquent, la formule de Vincenty est de quelques ordres de grandeur plus précise, et non de ~ 0,17%.

Adam Matan
la source
3
+1 Bien joué. Pour une analyse générale de l'erreur à travers le monde, veuillez consulter le fil à gis.stackexchange.com/questions/25494 .
whuber
3
Vincenty calcule les distances géodésiques ellipsoïdales beaucoup plus précisément que la formule du grand cercle. Donc, dire que le gain de précision de Vincenty n'est que de 0,17% est trompeur. (Cela revient à dire que l'arithmétique à double précision est 0,1% plus précise que l'utilisation d'une règle à
calcul
14

Si vous utilisez geopy, les distances great_circle et vincenty sont également pratiques à obtenir. Dans ce cas, vous devez presque toujours utiliser celui qui vous donne le résultat le plus précis, c'est-à-dire vincenty. Les deux considérations (comme vous le signalez) sont la vitesse et la précision.

Vincenty est deux fois plus lent. Mais probablement dans une application réelle, la durée de fonctionnement accrue est négligeable. Même si votre application demandait un million de calculs de distance, nous ne parlons que d'une différence de temps de quelques secondes.

Pour les points que vous utilisez, l'erreur dans vincenty est de 6 μm et l'erreur dans la distance du grand cercle est de 0,75 m. Je dirais alors que vincenty est 120000 fois plus précis (plutôt que 0,17% plus précis). Pour les points généraux, l'erreur dans la distance du grand cercle peut atteindre 0,5%. Pouvez-vous donc vivre avec une erreur de 0,5% dans les distances? Pour une utilisation occasionnelle (quelle est la distance du Cap au Caire?), Vous pouvez probablement le faire. Cependant, de nombreuses applications SIG ont des exigences de précision beaucoup plus strictes. (0,5% représente 5 m sur 1 km. Cela fait vraiment une différence.)

Presque tous les travaux de cartographie sérieux sont effectués sur l'ellipsoïde de référence et il est donc logique que les distances soient également mesurées sur l'ellipsoïde. Peut-être que vous pouvez vous en sortir avec des distances de grands cercles aujourd'hui. Mais pour chaque nouvelle demande, vous devrez vérifier si cela est toujours acceptable. Il vaut mieux utiliser la distance ellipsoïdale dès le départ. Vous dormirez mieux la nuit.

ADDENDA (mai 2017)

En réponse à la réponse donnée par @ craig-hicks. La méthode vincenty () dans geopy a un défaut potentiellement fatal: elle renvoie une erreur pour les points presque antipodaux. La documentation du code suggère d'augmenter le nombre d'itérations. Mais ce n'est pas une solution générale car la méthode itérative utilisée par vincenty () est instable pour de tels points (chaque itération vous éloigne de la bonne solution).

Pourquoi est-ce que je caractérise le problème comme "potentiellement mortel"? Parce que toute utilisation de la fonction de distance dans une autre bibliothèque de logiciels doit pouvoir gérer l'exception. Le manipuler en renvoyant un NaN ou la distance du grand cercle peut ne pas être satisfaisant, car la fonction de distance résultante n'obéira pas à l'inégalité du triangle qui empêche son utilisation, par exemple, dans les arbres à points d'observation.

La situation n'est pas complètement sombre. Mon package python Geographiclib calcule la distance géodésique avec précision sans aucune défaillance. La requête d'extraction de géopie # 144 modifie la fonction de distance du géopy pour utiliser le paquet GeographicLib s'il est disponible. Malheureusement, cette demande de traction est dans les limbes depuis août 2016.

ADDENDA (mai 2018)

geopy 1.13.0 utilise désormais le paquet Geographiclib pour calculer les distances. Voici un exemple d'appel (basé sur l'exemple de la question d'origine):

>>> from geopy.distance import great_circle
>>> from geopy.distance import geodesic
>>> p1 = (31.8300167,35.0662833) # (lat, lon) - https://goo.gl/maps/TQwDd
>>> p2 = (31.8300000,35.0708167) # (lat, lon) - https://goo.gl/maps/lHrrg
>>> geodesic(p1, p2).meters
429.1676644986777
>>> great_circle(p1, p2).meters
428.28877358686776
cffk
la source
3

Je m'excuse d'avoir posté une deuxième réponse ici, mais j'en profite pour répondre à la demande de @ craig-hicks de fournir des comparaisons de précision et de synchronisation pour différents algorithmes de calcul de la distance géodésique. Cela paraphrase un commentaire que je fais à ma demande de tirage # 144 pour geopy qui permet l'utilisation de l'une des deux implémentations de mon algorithme pour les géodésiques à utiliser dans geopy, l'une est une implémentation native de python, la géodésique (Geographiclib) et les autres utilisations une implémentation en C, géodésique (pyproj) .

Voici quelques données de synchronisation. Les temps sont en microsecs par appel

method                          dist    dest
geopy great_circle              20.4    17.1
geopy vincenty                  40.3    30.4
geopy geodesic(pyproj)          37.1    31.1
geopy geodesic(geographiclib)  302.9   124.1

Voici la précision des calculs géodésiques basés sur mon ensemble de test géodésique . Les erreurs sont données en unités de microns (1e-6 m)

method                        distance destination
geopy vincenty                 205.629  141.945
geopy geodesic(pyproj)           0.007    0.013
geopy geodesic(geographiclib)    0.011    0.010

J'ai inclus la requête pull n ° 194 de hannosche qui corrige un mauvais bug dans la fonction de destination. Sans ce correctif, l'erreur dans le calcul de la destination pour vincenty est de 8,98 mètres.

19,2% des cas de tests ont échoué avec vincenty.distance (itérations = 20). Cependant, l'ensemble de test est biaisé vers les cas qui pourraient provoquer cet échec.

Avec des points aléatoires sur l'ellipsoïde WGS84, l'algorithme Vincenty est garanti d'échouer 16,6 fois sur 1000000 (la bonne solution est un point fixe instable de la méthode Vincenty).

Avec l'implémentation geopy de Vincenty et des itérations = 20, le taux d'échec est de 82,8 pour 1000000. Avec des itérations = 200, le taux d'échec est de 21,2 pour 1000000.

Même si ces taux sont faibles, les échecs peuvent être assez courants. Par exemple, dans un ensemble de données de 1000 points aléatoires (pensez aux aéroports du monde, peut-être), le calcul de la matrice de distance complète échouerait en moyenne 16 fois (avec des itérations = 20).

cffk
la source
2

Il semble que le paquet geopy.distance offre une fonction "distance ()" qui est par défaut vincenty (). Je recommanderais d'utiliser distance () en principe, car c'est la recommandation du package, au cas où il s'écarterait de vincenty () à l'avenir (peu probable que ce soit). Continuer la lecture:

Cette note de documentation est incluse dans le code source de la fonction vincenty () que vous avez spécifiée:

Remarque: Cette implémentation de Vincenty distance ne parvient pas à converger pour certains points valides. Dans certains cas, un résultat peut être obtenu en augmentant le nombre d'itérations ( iterationsargument mot-clé, donné dans la classe __init__, avec une valeur par défaut de 20). Il peut être préférable d'utiliser: class:, .great_circlequi est légèrement moins précis, mais produit toujours un résultat.

Le code source avec le commentaire / note ci-dessus peut être trouvé à https://github.com/geopy/geopy/blob/master/geopy/distance.py Faites défiler jusqu'à la définition de vincenty ()

Néanmoins, la fonction de distance par défaut utilisée par ce package lorsque caliing distance () est la fonction vincenty (), ce qui implique que l'échec de la convergence n'est pas catastrophique et qu'une réponse raisonnable est renvoyée - le plus important est qu'aucune exception n'est générée.

Mise à jour: Comme indiqué par "cffk", la fonction vincenty () lève explicitement une exception ValueError lorsque l'algorithme ne converge pas - bien qu'elle ne soit pas documentée dans la description de la fonction. Par conséquent, la documentation est boguée.

Craig Hicks
la source
Non, la méthode vincenty () peut générer une exception. On prétend souvent que cela n'a pas d'importance car cela n'affecte que le calcul des distances entre des points presque antipodaux. Cependant, ces échecs signifient que l'inégalité du triangle échoue et que la distance de Vincenty ne peut pas être utilisée pour implémenter une recherche du plus proche voisin à l'aide d'un arbre de points de vue (ce qui vous permettrait de déterminer, par exemple, l'emplacement de l'aéroport le plus proche de manière efficace). Pour contourner ce problème, vous pouvez utiliser cette demande d'extraction geopy github.com/geopy/geopy/pull/144 qui utilise GeographicLib pour les distances.
cffk
@cffk - Je ne peux pas discerner avec certitude à partir de votre commentaire ou lien, mais je suppose que "demande de tirage geopy" pourrait être une table de recherche - est-ce? La discussion peut être divisée en deux: le cas où la table de recherche n'est pas disponible (téléchargée) et le cas où elle est disponible.
Craig Hicks
@cffk - Dans le cas où il n'est pas disponible: Premièrement, la documentation est boguée principalement parce qu'elle ne comprend pas de description de l'exception prévue (augmenter ValueError ("La formule Vincenty n'a pas réussi à converger!")), mais aussi parce que il ne décrit pas l'instabilité comme se produisant à la mesure de points presque antipodaux. Je recommanderais d'ajouter une fonction vincenty_noexcpt à la classe Vincenty qui intercepte en interne l'exception et renvoie à la place une grande valeur de cercle, et en faisant le paramètre par défaut: distance = vincenty_noexcep.
Craig Hicks
@cffk - Dans le cas où la table de recherche est disponible: je conseillerais beaucoup de tests et de synchronisation car les méthodes de recherche sortent souvent du cache et sont donc coûteuses en temps. Le remplacement de la méthode vincenty par la méthode "pull" comme valeur par défaut pourrait signifier que quiconque télécharge le package "pull" dans le répertoire python changera tous les appels existants en vincenty en appels à tirer - cela pourrait être problématique si l'utilisateur (s) vraiment juste voulait essayer soigneusement et explicitement la méthode "pull".
Craig Hicks,
@ craig-hicks - Non, la "pull request" substitue un meilleur algorithme (par moi!) pour mesurer les distances, voir doi.org/10.1007/s00190-012-0578-z Ceci est plus précis que Vincenty, renvoie toujours un résultat et prend environ le même temps. Je ne suis pas un mainteneur de geopy et cette demande de pull est en sommeil depuis août dernier. Si j'avais mes druthers, cela serait remplacé par geopy (et vincenty () appellerait le nouvel algorithme au lieu de Vincenty) et ce serait la fin de la discussion.
cffk
1

Que vous utilisiez vincenty ou haversine ou la loi sphérique des cosinus, il est judicieux de prendre conscience des problèmes potentiels avec le code que vous prévoyez d'utiliser, des choses à surveiller et à atténuer, et de la façon dont on traite les problèmes vincenty vs haversine vs sloc différera à mesure que l'on se rend compte des problèmes / casse cachés de chacun, qui peuvent ou non être connus du grand public. Le programmeur chevronné le sait. Les débutants ne le peuvent pas. J'espère épargner à certains d'entre eux la frustration lorsqu'un extrait d'un forum fait quelque chose d'inattendu, dans certains cas. Si l'on va sérieusement utiliser une version de l'un d'entre eux, vincenty, haversine, sloc, alors SE, SO, Reddit, Quora, etc., peuvent avoir fourni une aide limitée dans le codage initial d'une solution, mais cela ne signifie pas que leur solution ou «réponse» acceptée est exempte de problèmes. Si un projet est suffisamment important, il mérite une quantité raisonnable de recherche appropriée. Lisez le manuel, lisez les documents, et s'il existe une révision du code de ce code, lisez-le. Copier et coller un extrait ou un résumé qui a été voté une centaine de fois ou plus ne signifie pas que sa sécurité est complète et assurée.

La réponse intrigante publiée par cffk soulève le point d'être conscient des cas cachés, dans les solutions packagées, qui peuvent produire des exceptions ou d'autres difficultés . Les allégations spécifiques formulées dans ce poste dépassent mon budget-temps pour le moment, mais j'en retiens qu'il y a effectivement des problèmes cachés dans certains paquets, y compris au moins une mise en œuvre de Vincent, au sujet desquels au moins une personne a proposé d'améliorer d'une manière ou d'une autre, afin de minimiser ou d'éliminer le risque de rencontrer ces difficultés. Je n'apporterai rien de plus à ce sujet concernant Vincent (en l'ignorant beaucoup trop), mais je me tournerai plutôt vers l'héversine, au moins en partie sur le sujet avec le PO.

La formule haversine publiquement publiée, que ce soit en python ou dans un autre langage, car elle utilisera très probablement la spécification en virgule flottante IEEE 754 sur la plupart des systèmes Intel et similaires aujourd'hui, et les processeurs ARM, PowerPC, etc. être également sensible à des erreurs d'exception rares mais réelles et répétables très proches ou à une distance d'arc de 180 degrés, des points antipodaux, en raison d'approximations de virgule flottante et d'arrondi. Certains débutants n'ont peut-être pas encore été mordus par cette situation. Parce que cette spécification fp se rapproche et arrondit, cela ne signifie pas que tout code qui appelle fp64 peut provoquer des erreurs d'exception, non. Mais du code, certaines formules peuvent ne pas avoir des contours si évidents où les approximations et les arrondis de IEEE 754 fp64 peuvent faire en sorte qu'une valeur s'écarte légèrement du domaine d'une méthode mathématique qui devrait évaluer sans faille une telle valeur. Un exemple ... sqrt (). Si une valeur négative trouve son chemin dans un sqrt (), tel que sqrt (-0.00000000000000000122739), il y aura une erreur d'exception. Dans la formule haversine, la manière dont elle progresse vers une solution, il y a deux méthodes sqrt () dans atan2 (). leun qui est calculé puis utilisé dans le sqrt (), peut, aux points antipodes du globe, légèrement s'égarer en dessous de 0,0 ou au-dessus de 1,0, très légèrement à cause des approximations fp64 et de l'arrondi, rarement, mais de façon répétable. Dans ce contexte, une répétabilité fiable et constante en fait un risque d'exception, un casse-tête à protéger, à atténuer, plutôt qu'un hasard aléatoire isolé. Voici un exemple d'un court extrait python3 de haversine, sans la protection nécessaire:

import math as m

a = m.sin(dlat / 2)**2 + m.cos(lat1) * m.cos(lat2) * m.sin(dlon / 2)**2
c = 2 * m.atan2(m.sqrt(a), m.sqrt(1 - a))
distance = Radius * c

Très près ou aux points antipodes, un calculé dans la première ligne de la formule peut s'égarer négativement, rarement, mais de façon répétée avec ces mêmes coordonnées lat lon. Pour protéger / corriger ces rares cas, on peut simplement ajouter, après un calcul, comme on le voit ci - dessous:

import math as m

note = ''

a = m.sin(dlat / 2)**2 + m.cos(lat1) * m.cos(lat2) * m.sin(dlon / 2)**2
if a < 0.0: a = 0.0 ; note = '*'
if a > 1.0: a = 1.0 ; note = '**'
c = 2 * m.atan2(m.sqrt(a), m.sqrt(1 - a))
distance = Radius * c

# note = '*'  # a went below 0.0 and was normalized back to 0.0
# note = '**' # a went above 1.0 and was normalized back to max of 1.0

Bien sûr, je n'ai pas montré l'intégralité de la fonction ici, mais un court extrait comme il est si souvent affiché. Mais celui-ci montre la protection de sqrt (), en testant le a et en le normalisant si nécessaire, ce qui évite également de mettre le tout dans un essai, sauf. La note = '' en haut sert à empêcher la phase de bytecode de protester contre l'utilisation de la note avant de lui affecter une valeur, si elle est renvoyée avec le résultat de la fonction.

Avec ce simple changement, d'ajouter les deux un essais, les fonctions sqrt () seront heureux, et le code a maintenant une somme supplémentaire de note qui peut être retourné au code d' appel, à l' alerte qu'un résultat a été légèrement normalisé, et pourquoi. Certains peuvent s'en soucier, d'autres non, mais c'est là, empêchant une erreur d'exception, qui «peut» autrement se produire. Un bloc try except peut intercepter l'exception, mais pas la corriger, sauf si cela est explicitement écrit pour le faire. Il semble plus facile à coder la ligne de correction (s) immédiatement après la une ligne de calcul. Une entrée soigneusement nettoyée ne devrait alors pas du tout nécessiter d'essayer sauf bloquer ici.

Résumé, si vous utilisez haversine, codé explicitement plutôt que d'utiliser un package ou une bibliothèque, peu importe la langue de votre choix, ce serait une bonne idée de tester et de normaliser un retour dans la plage nécessaire de 0,0 <= a <= 1,0 afin pour protéger la ligne suivante avec ses calculs c . Mais la majorité des extraits de code haversine ne le montrent pas et ne mentionnent pas le risque.

Expérience: lors de tests approfondis dans le monde entier, par incréments de 0,001 degré, j'ai rempli un disque dur avec des combinaisons lat lon qui ont provoqué une exception, une exception fiable et répétable cohérente, pendant un mois de test collatéral de la fiabilité du refroidissement du CPU fan, et ma patience. Oui, j'ai depuis supprimé la plupart de ces journaux, car leur but était principalement de prouver le point (si le jeu de mots est autorisé). Mais j'ai quelques journaux plus courts de «valeurs lat lon de problème», conservés à des fins de test.

Précision: est-ce que a et l'ensemble du résultat haversine perdront une certaine précision en le normalisant un peu dans le domaine? Pas grand-chose, peut-être pas plus que les approximations et arrondis fp64 déjà introduits, qui ont causé cette légère dérive hors du domaine. Si vous avez déjà trouvé haversine acceptable par rapport à vincenty - plus simple, plus rapide, plus facile à personnaliser, à dépanner et à entretenir, alors haversine peut être une bonne solution pour votre projet.

J'ai utilisé haversine sur une skysphère projetée au-dessus pour mesurer les distances angulaires entre les objets dans le ciel, vu depuis une position sur la terre, cartographier l'azimut et alt en coordonnées équivalentes lat lons à la skysphère, aucun élipsoïde à considérer du tout, puisque le la skysphère théorique projetée est une sphère parfaite, lorsqu'il s'agit de mesurer des angles de vue de distance angulaire entre deux objets à partir d'une position à la surface de la terre. Cela correspond parfaitement à mes besoins. Donc, haversine est toujours très utile et très précis dans certaines applications (bien dans mes buts) ... mais si vous l'utilisez, que ce soit sur terre pour le SIG ou la navigation, ou dans les observations et les mesures d'objets célestes, protégez dans le cas de points antipodes ou très proches de points antipodes, en testant unet le repoussant dans son domaine nécessaire en cas de besoin.

Le haversine non protégé est partout sur Internet, et je n'ai vu qu'un seul ancien poste Usenet qui a montré une certaine protection, je pense de la part d'un membre du JPL, et qui pourrait avoir été avant 1985, avant la spécification en virgule flottante IEEE 754. Deux autres pages ont mentionné des problèmes possibles près des points antipodaux, mais n'ont pas décrit ces problèmes, ni comment on pourrait les atténuer. Il y a donc lieu de s'inquiéter pour les débutants (comme moi) qui ne comprennent pas toujours assez bien les bonnes pratiques pour approfondir la recherche et tester les cas de certains codes qu'ils ont copiés et collés dans un projet en toute confiance. Le post intrigant de cffk était rafraîchissant en ce qu'il était public avec ces types de problèmes, qui ne sont pas souvent mentionnés, rarement codés publiquement pour la protection dans des extraits, et rarement discutés de cette manière, par rapport à la quantité de versions non protégées et non discutées qui sont publiées.

Depuis 20190923, la page wiki pour la formule haversine mentionne en effet le problème possible aux points antipodaux, en raison de problèmes de virgule flottante dans les appareils informatiques ... encourageant ...

https://en.wikipedia.org/wiki/Haversine_formula

(parce que cette page wiki n'a pas, pour le moment, d'ancrage html pour la section à laquelle je lierais directement, donc, après le chargement de la page, faites une recherche sur cette page du navigateur pour 'Quand utiliser ces formules' et vous voir plus officiellement le problème de l'haversine avec les points antipodes.)

Et cet autre site en a également une très brève mention:

https://www.movable-type.co.uk/scripts/latlong.html

Si l'on fait une recherche sur cette page pour «y compris la protection contre les erreurs d'arrondi», il y a ceci ...

Si atan2 n'est pas disponible, c pourrait être calculé à partir de 2 ⋅ asin (min (1, √a)) (y compris la protection contre les erreurs d'arrondi).

Maintenant, il existe un cas rare où des erreurs d'arrondi sont mentionnées et la protection affichée pour la version asin (), mais non mentionnée ou affichée pour la version atan2 (). Mais au moins, le risque d'erreurs d'arrondi est mentionné.

imho, toute application 24/7/365 utilisant haversine, a besoin de cette protection près des points antipodes comme un détail important et simple.

Je ne sais pas quels packages haversine incluent ou n'incluent pas cette protection, mais si vous êtes nouveau dans tout cela et que vous allez utiliser la ou les versions d'extraits publiées, vous savez maintenant qu'il a besoin de protection, et cette protection est très simple à mettre en œuvre, c'est-à-dire si vous n'utilisez pas vincenty, et n'utilisez pas un haversine packagé sans accès facile pour modifier le code du package.

IOW, que vous utilisiez vincenty ou haversine ou sloc, vous devez être conscient de tout problème avec le code, des choses à surveiller et à atténuer, et comment les problèmes vincenty vs haversine vs sloc différeront à mesure que l'on se rend compte de chacun problèmes cachés / edgecases, qui peuvent être connus ou non.

Toujours entrain d'apprendre
la source