Differenza tra i calcoli della distanza di Vincenty e del grande cerchio?


16

Il pacchetto geopy di Python presenta due tecniche di misurazione della distanza: le formule Great Circle e 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

Qual è la differenza? Quale misurazione della distanza è preferita?

Risposte:


18

Secondo Wikipedia, la formula di Vincenty è più lenta ma più accurata :

Le formule di Vincenty sono due metodi iterativi correlati usati in geodesia per calcolare la distanza tra due punti sulla superficie di uno sferoide, sviluppati da Thaddeus Vincenty (1975a) Si basano sul presupposto che la figura della Terra sia uno sferoide oblato, e quindi sono più precisi di metodi come la distanza del grande cerchio che assumono una Terra sferica.

La differenza di precisione è ~0.17%in una distanza di 428 metri in Israele. Ho fatto un test di velocità rapido e sporco:

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

Codice:

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)

Concludere: la formula di Vincenty è il doppio del tempo di calcolo rispetto al cerchio grande e il suo guadagno di precisione nel punto testato è ~ 0,17%.

Poiché il tempo di calcolo è trascurabile, la formula di Vincenty è preferita per ogni esigenza pratica.

Aggiornamento : seguendo i commenti perspicaci della risposta di whuber e cffk e cffk , concordo sul fatto che il guadagno di precisione debba essere confrontato con l'errore, non con la misurazione. Quindi, la formula di Vincenty è più accurata di alcuni ordini di grandezza, non ~ 0,17%.


3
+1 ben fatto. Per un'analisi generale dell'errore in tutto il mondo, consultare la discussione su gis.stackexchange.com/questions/25494 .
whuber

3
Vincenty calcola le distanze geodetiche ellissoidali molte volte in modo più accurato rispetto alla formula del grande cerchio. Quindi affermare che il guadagno di precisione di Vincenty è solo dello 0,17% è fuorviante. (È equivalente a dire che l'aritmetica a doppia precisione è dello 0,1% più accurata rispetto all'utilizzo di una regola di scorrimento.)
cffk,

14

Se stai usando la geopy, allora le distanze great_circle e vincenty sono ugualmente convenienti da ottenere. In questo caso, dovresti quasi sempre usare quello che ti dà il risultato più accurato, cioè vincenty. Le due considerazioni (come sottolineato) sono la velocità e la precisione.

Vincenty è due volte più lento. Ma probabilmente in un'applicazione reale il tempo di esecuzione aumentato è trascurabile. Anche se la tua applicazione richiede un milione di calcoli di distanza, stiamo parlando solo di una differenza in tempi di un paio di secondi.

Per i punti utilizzati, l'errore in vincenty è 6 μm e l'errore nella grande distanza del cerchio è 0,75 m. Direi quindi che vincenty è 120000 volte più preciso (anziché 0,17% più preciso). Per i punti generali, l'errore nella grande distanza del cerchio può arrivare fino allo 0,5%. Quindi puoi vivere con un errore dello 0,5% nelle distanze? Per un uso occasionale (qual è la distanza da Città del Capo al Cairo?), Probabilmente puoi farlo. Tuttavia, molte applicazioni GIS hanno requisiti di precisione molto più rigorosi. (0,5% è 5 m su 1 km. Questo fa davvero la differenza.)

Quasi tutti i seri lavori di mappatura vengono eseguiti sull'ellissoide di riferimento e ha quindi senso che le distanze debbano essere misurate anche sull'ellissoide. Forse oggi puoi cavartela con grandi distanze. Ma per ogni nuova applicazione, dovrai verificare se questo è ancora accettabile. Meglio usare solo la distanza ellissoidale dall'inizio. Dormirai meglio di notte.

ADDENDUM (maggio 2017)

In risposta alla risposta data da @ craig-hicks. Il metodo vincenty () in geopy ha un difetto potenzialmente fatale: genera un errore per punti quasi antipodali. La documentazione nel codice suggerisce di aumentare il numero di iterazioni. Ma questa non è una soluzione generale perché il metodo iterativo usato da vincenty () è instabile per tali punti (ogni iterazione ti porta oltre dalla soluzione corretta).

Perché caratterizzo il problema come "potenzialmente fatale"? Perché qualsiasi uso della funzione di distanza all'interno di un'altra libreria software deve essere in grado di gestire l'eccezione. Gestirlo restituendo un NaN o la distanza del grande cerchio potrebbe non essere soddisfacente, perché la funzione di distanza risultante non obbedirà alla disuguaglianza del triangolo che ne preclude l'uso, ad esempio, negli alberi di punti panoramici.

La situazione non è completamente desolante. Il mio pacchetto python geographiclib calcola la distanza geodetica con precisione senza errori. La richiesta pull # 144 di geopy geopy cambia la funzione di distanza della geopy per usare il pacchetto geografico se disponibile. Purtroppo questa richiesta pull è stata in un limbo da Augest 2016.

ADDENDUM (maggio 2018)

geopy 1.13.0 ora utilizza il pacchetto geographiclib per calcolare le distanze. Ecco una chiamata di esempio (basata sull'esempio nella domanda originale):

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

3

Mi scuso per aver pubblicato una seconda risposta qui, ma ho colto l'occasione per rispondere alla richiesta di @ craig-hicks per fornire confronti di accuratezza e tempistica per vari algoritmi per il calcolo della distanza geodetica. Questo parafrasa un commento che faccio alla mia richiesta pull # 144 per la geopia che consente l'uso di una delle due implementazioni del mio algoritmo per la geodetica da utilizzare all'interno della geopia, una è un'implementazione nativa di Python, geodetica (geografica) e gli altri usi un'implementazione in C, geodetica (pyproj) .

Ecco alcuni dati di temporizzazione. I tempi sono in microsecondi per chiamata

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

Ecco l'accuratezza dei calcoli geodetici basati sul mio set di test geodetici . Gli errori sono indicati in unità di micron (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

Ho incluso la richiesta pull # 194 di hannosche che risolve un bug errato nella funzione di destinazione. Senza questa correzione, l'errore nel calcolo della destinazione per vincenty è 8,98 metri.

Il 19,2% dei casi di test ha avuto esito negativo con vincenty.distance (iterazioni = 20). Tuttavia, il set di test è inclinato verso casi che potrebbero causare questo fallimento.

Con punti casuali sull'ellissoide WGS84, si garantisce che l'algoritmo Vincenty fallisca 16.6000000 volte (la soluzione corretta è un punto fisso instabile del metodo Vincenty).

Con l'implementazione geografica di Vincenty e iterazioni = 20, il tasso di fallimento è 82,8 per 1000000. Con iterazioni = 200, il tasso di fallimento è 21,2 per 1000000.

Anche se questi tassi sono piccoli, i guasti possono essere abbastanza comuni. Ad esempio, in un set di dati di 1000 punti casuali (si pensi agli aeroporti dei mondi, forse), calcolare la matrice a distanza completa fallirebbe in media 16 volte (con iterazioni = 20).


2

Sembra che il pacchetto geopy.distance offra una funzione "distance ()" che per impostazione predefinita è vincenty (). Consiglierei di usare la distanza () in linea di principio, come è la raccomandazione del pacchetto, nel caso in cui sia mai divergente da vincenty () in futuro (improbabile che sia). Continua a leggere:

Questa nota di documentazione è inclusa nel codice sorgente per la funzione vincenty () specificata:

Nota: questa implementazione della distanza Vincenty non riesce a convergere per alcuni punti validi. In alcuni casi, è possibile ottenere un risultato aumentando il numero di iterazioni ( iterationsargomento della parola chiave, fornito nella classe __init__, con un valore predefinito di 20). Potrebbe essere preferibile usare: class:.great_circle che è leggermente meno preciso, ma produce sempre un risultato.

Il codice sorgente con il commento / nota sopra è disponibile all'indirizzo https://github.com/geopy/geopy/blob/master/geopy/distance.py Scorri verso il basso fino alla definizione per vincenty ()

Tuttavia, la funzione di distanza predefinita utilizzata da quel pacchetto quando caliing distance () è la funzione vincenty (), il che implica che la mancata convergenza non è catastrofica e viene restituita una risposta ragionevole - soprattutto non viene generata un'eccezione.

Aggiornamento: come notato da "cffk", la funzione vincenty () genera esplicitamente un'eccezione ValueError quando l'algoritmo non converge, sebbene non sia documentato nella descrizione della funzione. Quindi, la documentazione è difettosa.


No, il metodo vincenty () può generare un'eccezione. Si afferma spesso che questo non ha importanza perché influisce solo sul calcolo delle distanze tra punti quasi antipodali. Tuttavia, tali insuccessi significano che la disuguaglianza del triangolo fallisce e quindi la distanza Vincenty non può essere utilizzata per implementare una ricerca del vicino più vicino usando un albero di punti di vantaggio (che consentirebbe di determinare, ad esempio, la posizione dell'aeroporto più vicino in modo efficiente). Per ovviare a questo problema, è possibile utilizzare questa richiesta pull geopy github.com/geopy/geopy/pull/144 che utilizza GeographicLib per le distanze.
Cffk,

@cffk - Non riesco a discernere con certezza dal tuo commento o link, ma immagino che "richiesta di pull in geopy" potrebbe essere una tabella di ricerca - vero? La discussione può essere divisa in due: il caso in cui la tabella di ricerca non è disponibile (scaricato) e il caso in cui è disponibile.
Craig Hicks,

@cffk - Nel caso in cui non sia disponibile: in primo luogo, la documentazione è errata principalmente perché non include una descrizione dell'eccezione pianificata (aumenta ValueError ("La formula Vincenty non è riuscita a convergere!")), ma anche perché non descrive l'instabilità che si verifica alla misurazione di punti quasi antipodali. Vorrei raccomandare di aggiungere una funzione vincenty_noexcpt alla classe Vincenty che intercetta internamente l'eccezione e restituisce invece un grande valore del cerchio, e rendere l'impostazione predefinita: distance = vincenty_noexcep.
Craig Hicks,

@cffk - Nel caso in cui sia disponibile la tabella di ricerca: consiglierei molti test e tempistiche perché i metodi di ricerca spesso vanno fuori dalla cache e quindi sono costosi. Sostituire il metodo vincenty con il metodo "pull" come impostazione predefinita potrebbe significare che chiunque scarichi il pacchetto "pull" nella directory python cambierà tutte le chiamate esistenti in vincenty in chiamate da pull - ciò potrebbe essere problematico se l'utente / i davvero volevo provare attentamente ed esplicitamente il metodo "pull".
Craig Hicks,

@ craig-hicks - No, la "richiesta pull" sostituisce un algoritmo migliore (da parte mia!) per misurare le distanze, vedi doi.org/10.1007/s00190-012-0578-z Questo è più preciso di Vincenty, restituisce sempre un risultato e richiede circa lo stesso tempo. Non sono un manutentore della geografia e questa richiesta pull è stata inattiva dallo scorso agosto. Se avessi i miei druther, questo sarebbe sostituito in geopy (e vincenty () chiamerebbe il nuovo algoritmo invece di quello di Vincenty) e sarebbe la fine della discussione.
Cffk,

1

Sia che utilizzi vincenty o haversine o la legge sferica dei coseni, c'è saggezza nel prendere coscienza di eventuali problemi potenziali con il codice che stai pianificando di usare, cose a cui fare attenzione e mitigare, e come si affrontano le questioni vincenty vs haversine vs sloc differirà man mano che si diventa consapevoli dei problemi in agguato di ciascuno, che possono o meno essere conosciuti popolarmente. Il programmatore esperto lo sa. I principianti non possono. Spero di risparmiare alcuni di loro frustrazione quando uno snippet di un forum fa qualcosa di inaspettato, in alcuni casi. Se si utilizzerà seriamente una versione di uno di questi, vincenty, haversine, sloc, allora SE, SO, Reddit, Quora, ecc., Potrebbero aver fornito un aiuto limitato in alcuni codici iniziali di una soluzione, ma ciò non significa che la loro soluzione o la "risposta" accettata è priva di problemi. Se un progetto è abbastanza importante, merita un'adeguata quantità ragionevole di ricerca. Leggi il manuale, leggi i documenti e, se esiste una revisione del codice di quel codice, leggilo. Copiare e incollare uno snippet o un gist che è stato votato centinaia o più volte non significa che la sua sicurezza sia completa e garantita.

La risposta intrigante pubblicata da cffk solleva il punto di essere a conoscenza di agguati di edgecase, in soluzioni confezionate, che possono produrre eccezioni o altre difficoltà . Le affermazioni specifiche fatte in quel post sono al di là del mio budget di tempo da perseguire al momento, ma tolgo da esso che ci sono davvero problemi in agguato in alcuni pacchetti, inclusa almeno un'implementazione vincenty, riguardo alla quale almeno una persona ha proposto di migliorare in un modo o nell'altro, al fine di ridurre al minimo o eliminare il rischio di incontrare tali difficoltà. Non approfondirò ulteriormente l'argomento relativo a vincenty (essendo troppo troppo ignorante), ma passerò invece a haversine, almeno in parte sull'argomento con l'OP.

La formula haversine pubblicata pubblicamente, sia in pitone che in un'altra lingua, perché molto probabilmente utilizzerà le specifiche IEEE 754 in virgola mobile sulla maggior parte di tutti i sistemi Intel e Intel, e processori ARM, powerPC, ecc. essere suscettibile anche a rari ma reali e ripetibili errori di eccezione molto vicini o ad una distanza dell'arco di 180 gradi, punti antipodali, a causa di approssimazioni in virgola mobile e arrotondamenti. Alcuni principianti potrebbero non essere ancora stati morsi da questa situazione. Poiché questa specifica fp si avvicina e arrotonda, ciò non significa che qualsiasi codice che chiama fp64 potrebbe causare errori di eccezione, no. Ma un po 'di codice, alcune formule potrebbero non avere edgecase così evidenti in cui le approssimazioni e gli arrotondamenti di IEEE 754 fp64 potrebbero far deviare leggermente un valore dal dominio di un metodo matematico che dovrebbe valutare in modo impeccabile tale valore. Un esempio ... sqrt (). Se un valore negativo viene inserito in un sqrt (), come sqrt (-0.00000000000000000122739), si verificherà un errore di eccezione. Nella formula haversine, il modo in cui progredisce verso una soluzione, ci sono due metodi sqrt () nell'atan2 (). Ila che viene calcolato e quindi utilizzato in sqrt (), può, nei punti antipodali del globo, leggermente vagare sotto 0,0 o sopra 1,0, molto leggermente a causa delle approssimazioni e degli arrotondamenti di fp64, raramente, ma ripetibilmente. La ripetibilità affidabile e coerente, in questo contesto, rende questo rischio eccezionale, una custodia per proteggere, mitigare, piuttosto che un colpo di fortuna casuale isolato. Ecco un esempio di un breve frammento di python3 di haversine, senza la protezione necessaria:

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

Molto vicino o nei punti antipodali, un calcolato nella prima riga della formula può vagare negativamente, raramente, ma ripetibilmente con le stesse coordinate lat lon. Per proteggere / correggere quei casi rari, si può semplicemente aggiungere, dopo l' una di calcolo, come si vede qui sotto:

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

Ovviamente non ho mostrato l'intera funzione qui, ma un breve frammento come spesso pubblicato. Ma questo mostra la protezione per sqrt (), testando la a e normalizzandola se necessario, risparmiando anche la necessità di provare tutto tranne. La nota = '' up top serve per impedire allo stage bytecode di protestare sul fatto che la nota viene utilizzata prima che gli venga assegnato un valore, se viene restituito con il risultato della funzione.

Con questa semplice modifica, l'aggiunta dei due a test, le funzioni sqrt () saranno felici e il codice ora ha una nota aggiuntiva che può essere restituita al codice chiamante, per avvisare che un risultato è stato leggermente normalizzato e perché. Ad alcuni potrebbe interessare, altri potrebbero non farlo, ma è lì, a prevenire un errore di eccezione, che altrimenti si può verificare. Un tentativo tranne il blocco può catturare l'eccezione, ma non risolverlo, a meno che non sia scritto esplicitamente per farlo. Sembra più facile codice linea di correzione (s) immediatamente dopo l' una riga di calcolo. Un input accuratamente pulito non dovrebbe quindi richiedere un tentativo, tranne un blocco qui.

Riepilogo, se si utilizza haversine, codificato esplicitamente anziché utilizzare un pacchetto o una libreria, indipendentemente dalla lingua prescelta, sarebbe una buona idea testare e normalizzare un ritorno nell'intervallo necessario di 0,0 <= a <= 1.0 in ordine per proteggere la riga successiva con i suoi calcoli c . Ma la maggior parte degli snippet di codice haversine non lo mostra e non menziona il rischio.

Esperienza: durante test approfonditi in tutto il mondo, con incrementi di 0,001 gradi, ho riempito un disco rigido con combinazioni lat lon che hanno causato un'eccezione, un'eccezione affidabile ripetibile e coerente, durante un mese di test collaterale anche l'affidabilità del raffreddamento della CPU fan e la mia pazienza. Sì, da allora ho cancellato la maggior parte di quei registri, poiché il loro scopo era principalmente quello di dimostrare il punto (se il gioco di parole è permesso). Ma ho alcuni registri più brevi di "valori lat lon problem", tenuti a scopo di test.

Precisione: a e l'intero risultato di haversine perderanno un po 'di precisione normalizzandolo di nuovo un po' nel dominio? Non molto, forse non più che le approssimazioni e gli arrotondamenti di fp64 stavano già introducendo, ciò ha causato quella leggera deviazione dal dominio. Se hai già trovato che haversine è accettabile rispetto a vincenty - più semplice, più veloce, più facile da personalizzare, risolvere i problemi e mantenere, allora haversine potrebbe essere una buona soluzione per il tuo progetto.

Ho usato haversine su uno skysphere proiettato in alto per misurare le distanze angolari tra gli oggetti nel cielo, visto da una posizione sulla terra, mappando azimut e alt a skysphere lat lon coordinate equivalenti, nessun elipsoide da considerare, dal momento che il la skysphere teorica proiettata è una sfera perfetta, quando si tratta di misurare la distanza angolare osservando gli angoli tra due oggetti da una posizione sulla superficie terrestre. Si adatta perfettamente alle mie esigenze. Quindi, haversine è ancora molto utile, e molto preciso, in alcune applicazioni (ben entro i miei scopi) ... ma se lo usi, sulla terra per GIS o navigazione, o in osservazioni e misurazioni di oggetti del cielo, proteggi nel caso di punti antipodali o punti antipodali molto vicini, mediante test ae spingendolo di nuovo nel suo dominio necessario quando necessario.

L'haversine non protetto è su Internet, e ho visto solo un vecchio post usenet che mostrava una certa protezione, penso da qualcuno della JPL, e che potrebbe essere stato prima del 1985, prima della IEEE 754 in virgola mobile. Altre due pagine menzionavano possibili problemi in prossimità di punti antipodali, ma non descrivevano tali problemi o come si potesse mitigarli. Quindi c'è preoccupazione per i neofiti (come me) che potrebbero non sempre comprendere le buone pratiche abbastanza bene per approfondire la ricerca e testare le edgecase, di alcuni codici che hanno copiato e incollato in un progetto di fiducia. L'intrigante post di cffk è stato rinfrescante in quanto era pubblico con questi tipi di problemi, che non sono spesso menzionati, raramente codificati pubblicamente per la protezione in frammenti, e raramente discussi in questo modo, rispetto alla quantità di versioni non protette e non discusse che vengono pubblicate.

A partire da 20190923, la pagina wiki per la formula di haversine menziona effettivamente il problema possibile nei punti antipodali, a causa di problemi in virgola mobile nei dispositivi di elaborazione ... incoraggiante ...

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

(perché quella pagina wiki non ha, in questo momento, un'ancora html per la sezione a cui vorrei collegarmi direttamente, quindi, dopo che la pagina è stata caricata, fai una ricerca su quella pagina del browser per "Quando usi queste formule" e vedere il problema di haversine con i punti antipodali menzionati, più ufficialmente.)

E anche questo altro sito ne parla brevemente:

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

Se uno trova su quella pagina per "includere la protezione contro gli errori di arrotondamento", c'è questo ...

Se atan2 non è disponibile, c potrebbe essere calcolato da 2 ⋅ asin (min (1, √a)) (inclusa la protezione contro gli errori di arrotondamento).

Ora c'è un raro caso in cui sono menzionati errori di arrotondamento e protezione mostrata per la versione asin (), ma non menzionata o mostrata per la versione atan2 (). Ma almeno viene menzionato il rischio di errori di arrotondamento.

imho, qualsiasi applicazione 24/7/365 che utilizza haversine, ha bisogno di questa protezione vicino ai punti antipodali come un dettaglio importante e semplice.

Non so quali pacchetti di haversine includano o non includano questa protezione, ma se sei nuovo in tutto questo e utilizzerai le versioni 'snippet' pubblicamente pubblicate, ora sai che ha bisogno di protezione e tale protezione è molto semplice da implementare, vale a dire, se non si utilizza vincenty e non si utilizza un pacchetto confezionato senza un facile accesso per modificare il codice del pacchetto.

IOW, sia che si utilizzi vincenty o haversine o sloc, si dovrebbe essere consapevoli di eventuali problemi con il codice, le cose a cui prestare attenzione e mitigare e come si affrontano i problemi vincenty vs haversine vs sloc differiranno quando si diventa consapevoli di ognuno in agguato problemi / edgecase, che possono o meno essere conosciuti popolarmente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.