Diferença entre Vincenty e cálculos de distância de grande círculo?


16

O pacote geopy do Python apresenta duas técnicas de medição de distância: as fórmulas de 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 é a diferença? Qual medida de distância é preferida?

Respostas:


18

Segundo a Wikipedia, a fórmula de Vincenty é mais lenta, porém mais precisa :

As fórmulas de Vincenty são dois métodos iterativos relacionados usados ​​na geodésia para calcular a distância entre dois pontos na superfície de um esferóide, desenvolvida por Thaddeus Vincenty (1975a). Eles são baseados no pressuposto de que a figura da Terra é um esferóide oblato e, portanto, são mais precisos do que métodos como a distância do grande círculo que assume uma Terra esférica.

A diferença de precisão está ~0.17%em uma distância de 428 metros em Israel. Fiz um teste de velocidade rápido e sujo:

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

Código:

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)

Para concluir: a fórmula de Vincenty é o dobro do tempo de cálculo em comparação com o círculo grande, e seu ganho de precisão no ponto testado é de ~ 0,17%.

Como o tempo de cálculo é insignificante, a fórmula de Vincenty é preferida para todas as necessidades práticas.

Atualização : Após os comentários perspicazes das respostas da whuber e cffk e cffk , concordo que o ganho de precisão deve ser comparado com o erro, não com a medição. Portanto, a fórmula de Vincenty é algumas ordens de magnitude mais precisas, e não ~ 0,17%.


3
+1 Muito bem. Para uma análise geral do erro em todo o mundo, consulte o tópico em gis.stackexchange.com/questions/25494 .
whuber

3
Vincenty calcula distâncias geodésicas elipsoidais muitas vezes com mais precisão do que a fórmula do grande círculo. Dizer que o ganho de precisão do Vincenty é de apenas 0,17% é enganador. (É equivalente a dizer que a dupla aritmética de precisão é 0,1% mais preciso do que usando uma régua de cálculo.)
cffk

14

Se você estiver usando geopy, as distâncias great_circle e vincenty são igualmente convenientes de obter. Nesse caso, você quase sempre deve usar aquele que fornece o resultado mais preciso, ou seja, vincenty. As duas considerações (como você aponta) são velocidade e precisão.

Vincenty é duas vezes mais lento. Mas provavelmente em um aplicativo real, o aumento do tempo de execução é insignificante. Mesmo se seu aplicativo exigir um milhão de cálculos de distância, estamos falando apenas de uma diferença em tempos de alguns segundos.

Para os pontos que você usa, o erro de vincenty é de 6 μm e o erro na distância do grande círculo é de 0,75 m. Eu diria que vincenty é 120000 vezes mais preciso (em vez de 0,17% mais preciso). Para pontos gerais, o erro na grande distância do círculo pode chegar a 0,5%. Então você pode viver com um erro de 0,5% nas distâncias? Para uso casual (qual é a distância da Cidade do Cabo ao Cairo?), Provavelmente você pode. No entanto, muitos aplicativos GIS têm requisitos de precisão muito mais rigorosos. (0,5% é de 5m em 1km. Isso realmente faz a diferença.)

Quase todo trabalho sério de mapeamento é realizado no elipsóide de referência e, portanto, faz sentido que as distâncias também sejam medidas no elipsóide. Talvez você possa se afastar com grandes distâncias hoje. Mas para cada novo aplicativo, você precisará verificar se isso ainda é aceitável. Melhor é usar a distância elipsoidal desde o início. Você vai dormir melhor à noite.

ADENDA (maio de 2017)

Em resposta à resposta dada por @ craig-hicks. O método vincenty () em geopy tem uma falha potencialmente fatal: gera um erro para pontos quase antipodais. A documentação no código sugere aumentar o número de iterações. Mas essa não é uma solução geral, porque o método iterativo usado por vincenty () é instável para esses pontos (cada iteração leva você mais longe da solução correta).

Por que caracterizo o problema como "potencialmente fatal"? Como qualquer uso da função de distância em outra biblioteca de software precisa ser capaz de lidar com a exceção. Manejá-lo retornando um NaN ou a distância do grande círculo pode não ser satisfatório, porque a função de distância resultante não obedecerá à desigualdade do triângulo que impede seu uso, por exemplo, em árvores de pontos de vantagem.

A situação não é completamente sombria. Meu pacote python geographiclib calcula a distância geodésica com precisão, sem falhas. A solicitação de retirada de geopy nº 144 altera a função de distância da geopy para usar o pacote geographiclib, se estiver disponível. Infelizmente, esta solicitação de recebimento está no limbo desde agosto de 2016.

ADENDO (maio de 2018)

O geopy 1.13.0 agora usa o pacote geographiclib para calcular distâncias. Aqui está um exemplo de chamada (com base no exemplo da pergunta original):

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

Peço desculpas por postar uma segunda resposta aqui, mas aproveito a oportunidade para responder à solicitação do @ craig-hicks para fornecer comparações de precisão e tempo de vários algoritmos para calcular a distância geodésica. Isso parafraseia um comentário que eu faço ao meu pedido pull # 144 para geopy, que permite o uso de uma das duas implementações do meu algoritmo para geodésica ser usada dentro de geopy, uma é uma implementação nativa de python, geodésica (geographiclib) e as outras utilizações uma implementação em C, geodésica (pyproj) .

Aqui estão alguns dados de tempo. Os tempos estão em microssegundos por chamada

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

Aqui está a precisão dos cálculos geodésicos com base no meu conjunto de testes geodésicos . Os erros são dados em unidades de mícrons (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

Incluí a solicitação pull de hannosche # 194, que corrige um bug incorreto na função de destino. Sem essa correção, o erro no cálculo de destino para vincenty é 8,98 metros.

19,2% dos casos de teste falharam com vincenty.distance (iterações = 20). No entanto, o conjunto de testes é inclinado para casos que causariam essa falha.

Com pontos aleatórios no elipsóide WGS84, é garantido que o algoritmo Vincenty falhe 16,6 em 1000000 vezes (a solução correta é um ponto fixo instável do método Vincenty).

Com a implementação geográfica do Vincenty e as iterações = 20, a taxa de falhas é de 82,8 por 1000000. Com as iterações = 200, a taxa de falhas é de 21,2 por 1000000.

Embora essas taxas sejam pequenas, falhas podem ser bastante comuns. Por exemplo, em um conjunto de dados de 1000 pontos aleatórios (pense nos aeroportos do mundo, talvez), o cálculo da matriz de distância total falharia em média 16 vezes (com iterações = 20).


2

Parece que o pacote geopy.distance oferece uma função "distance ()" cujo padrão é vincenty (). Eu recomendaria o uso de distance () em princípio, pois é a recomendação do pacote, caso haja divergência de vincenty () no futuro (por mais improvável que seja). Continue lendo:

Esta nota de documentação está incluída no código fonte da função vincenty () que você especificou:

Nota: Esta implementação da distância Vincenty não converge para alguns pontos válidos. Em alguns casos, um resultado pode ser obtido aumentando o número de iterações ( iterationsargumento de palavra-chave, fornecido na classe __init__, com um padrão de 20). Pode ser preferível usar: class:, .great_circleque é marginalmente menos preciso, mas sempre produz um resultado.

O código fonte com o comentário / nota acima pode ser encontrado em https://github.com/geopy/geopy/blob/master/geopy/distance.py Role para baixo até a definição de vincenty ()

No entanto, a função de distância padrão usada por esse pacote ao calcular a distância () é a função vincenty (), o que implica que a falha na convergência não é catastrófica e uma resposta razoável é retornada - o mais importante é que uma exceção não é gerada.

Atualização: Conforme observado por "cffk", a função vincenty () lança explicitamente uma exceção ValueError quando o algoritmo não converge - embora não esteja documentado na descrição da função. Portanto, a documentação é incorreta.


Não, o método vincenty () pode gerar uma exceção. Costuma-se afirmar que isso não importa, porque afeta apenas o cálculo de distâncias entre pontos quase antipodais. No entanto, essas falhas significam que a desigualdade do triângulo falha e, portanto, a distância Vincenty não pode ser usada para implementar uma pesquisa de vizinhos mais próximos usando uma árvore de pontos de vantagem (o que permitiria determinar, por exemplo, a localização do aeroporto mais próximo com eficiência). Para contornar esse problema, você pode usar esta solicitação de extração geográfica, github.com/geopy/geopy/pull/144, que usa GeographicLib para distâncias.
Cffk 30/04

@cffk - Não consigo discernir com certeza pelo seu comentário ou link, mas acho que "solicitação de extração geográfica" pode ser uma tabela de pesquisa - não é? A discussão pode ser dividida em duas: o caso em que a tabela de pesquisa não está disponível (baixada) e o caso em que está disponível.
Craig Hicks

@cffk - No caso em que não está disponível: Em primeiro lugar, a documentação é defeituosa, principalmente porque não inclui uma descrição da exceção planejada (raise ValueError ("A fórmula Vincenty falhou ao convergir!")), mas também porque não descreve a instabilidade como ocorrendo na medição de pontos quase antipodais. Eu recomendaria adicionar uma função vincenty_noexcpt à classe Vincenty, que captura internamente a exceção e retorna um grande valor de círculo, e a configuração padrão: distance = vincenty_noexcep.
Craig Hicks

@ cffk - No caso em que a tabela de pesquisa está disponível: eu recomendaria muitos testes e tempos, porque os métodos de pesquisa geralmente saem do cache e, portanto, são muito dispendiosos. Substituir o método vincenty pelo método "pull", como padrão, significa que qualquer pessoa que esteja baixando o pacote "pull" no diretório python mudará todas as chamadas existentes para vincenty em chamadas para pull - isso pode ser problemático se o usuário realmente apenas Queria tentar cuidadosa e explicitamente o método "pull".
Craig Hicks

@ craig-hicks - Não, a "solicitação pull" substitui um algoritmo melhor (por mim!) para medir distâncias, consulte doi.org/10.1007/s00190-012-0578-z Isso é mais preciso que o Vincenty, sempre retorna um resultado e leva aproximadamente o mesmo tempo. Não sou mantenedor de geopy e essa solicitação de recebimento está inativa desde agosto do ano passado. Se eu tivesse meus defensores, isso seria substituído por geopy (e vincenty () chamaria o novo algoritmo em vez de vincenty) e esse seria o fim da discussão.
Cffk 2/17

1

Seja usando vincenty ou haversine ou a lei esférica dos cossenos, há sabedoria em tomar conhecimento de possíveis problemas com o código que você planeja usar, coisas a observar e mitigar e como lidar com questões vincenty vs haversine vs sloc diferirá à medida que se tornar ciente dos problemas / edgecases ocultos de cada um, que podem ou não ser conhecidos popularmente. O programador experiente sabe disso. Iniciantes não podem. Espero poupar alguns deles de frustração quando um trecho de um fórum faz algo inesperado, em certos casos. Se alguém usar seriamente alguma versão de qualquer um desses, vincenty, haversine, sloc, SE, SO, Reddit, Quora, etc., poderá ter fornecido ajuda limitada em alguma codificação inicial de uma solução, mas isso não significa que sua solução ou 'resposta' aceita está livre de problemas. Se um projeto é importante o suficiente, ele merece uma quantidade razoável e razoável de pesquisa. Leia o manual, leia os documentos e, se houver uma revisão desse código, leia-o. Copiar e colar um snippet ou essência que foi votado cem ou mais vezes não significa que sua segurança seja abrangente e garantida.

A resposta intrigante postada pelo cffk levanta o ponto de estar ciente de que as coisas estão à espreita, em soluções empacotadas, que podem produzir exceções ou outras dificuldades . As reivindicações específicas feitas nesse post estão além do meu orçamento de tempo a ser perseguido no momento, mas retiro que há realmente problemas ocultos em determinados pacotes, incluindo pelo menos uma implementação vincenty, sobre a qual pelo menos uma pessoa propôs melhorar de uma maneira ou de outra, a fim de minimizar ou eliminar o risco de encontrar essas dificuldades. Não acrescentarei mais a esse tópico referente à vincenty (sendo muito ignorante), mas, em vez disso, me voltarei para o haversine, pelo menos em parte no tópico com o OP.

A fórmula haversine popularmente publicada, seja em python ou em outra linguagem, porque provavelmente usará a especificação de ponto flutuante IEEE 754 na maioria dos sistemas intel e afins da atualidade, além de processadores ARM, powerPC, etc. também ser suscetível a erros de exceção raros, reais e repetíveis, muito próximos ou a uma distância de arco de 180 graus, pontos antipodais, devido a aproximações e arredondamentos de pontos flutuantes. Alguns iniciantes ainda podem não ter sido mordidos por essa situação. Como essa especificação fp se aproxima e arredonda, isso não significa que qualquer código que chama fp64 possa causar erros de exceção, não. Mas algum código, algumas fórmulas podem não ter resultados tão óbvios em que as aproximações e os arredondamentos do IEEE 754 fp64 podem fazer com que um valor se desvie um pouco do domínio de um método matemático que se espera que avalie esse valor na perfeição. Um exemplo ... sqrt (). Se um valor negativo chegar a um sqrt (), como sqrt (-0.00000000000000000122739), haverá um erro de exceção. Na fórmula haversine, a maneira pela qual ela progride em direção a uma solução, existem dois métodos sqrt () no atan2 (). oum que é calculado e depois usado no sqrt (), pode, nos pontos antipodais do globo, desviar-se ligeiramente abaixo de 0,0 ou acima de 1,0, muito levemente devido às aproximações e arredondamentos de fp64, raramente, mas repetidamente. A repetibilidade confiável e consistente, nesse contexto, torna esse risco de exceção, uma base de dados para proteger, mitigar, em vez de um acaso aleatório isolado. Aqui está um exemplo de um pequeno trecho python3 de haversine, sem a proteção necessária:

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

Muito próximo ou em pontos antípodas, um cálculo calculado na primeira linha da fórmula pode se desviar negativo, raramente, mas repetidamente com as mesmas coordenadas posteriores. Para proteger / corrigir essas ocorrências raras, pode-se simplesmente acrescentar, após a um cálculo, como visto abaixo:

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

É claro que eu não mostrei a função inteira aqui, mas um pequeno trecho, como é frequentemente publicado. Mas este mostra a proteção para o sqrt (), testando a um , e normalizando-se necessário, também poupando a necessidade de colocar a coisa toda em uma tentativa exceto. A nota = '' na parte superior é para impedir que o estágio de bytecode proteste que a nota está sendo usada antes de receber um valor, se for retornada com o resultado da função.

Com esta mudança simples, de adicionar os dois de testes, o sqrt () funções serão felizes, e o código agora tem um adicional de nota que pode ser retornado para o código de chamada, de alerta que resultado foi ligeiramente normalizada, e por quê. Alguns podem se importar, outros podem não, mas está lá, impedindo um erro de exceção, que 'pode' ocorrer de outra forma. Um bloco try try pode capturar a exceção, mas não corrigi-la, a menos que seja explicitamente escrito para isso. Parece mais fácil de codificar a linha de correção (s) imediatamente após a uma linha de cálculo. A entrada completamente limpa não deve, portanto, exigir uma tentativa, exceto o bloco aqui.

Resumo, se estiver usando haversine, codificado explicitamente em vez de usar um pacote ou biblioteca, não importa o idioma de sua escolha, seria uma boa idéia testar e normalizar uma volta ao intervalo necessário de 0,0 <= a <= 1,0, para para proteger a próxima linha com seus cálculos c . Mas a maioria dos trechos de código haversine não o mostra e não menciona o risco.

Experiência: durante testes detalhados em todo o mundo, em incrementos de 0,001 graus, preenchi um disco rígido com combinações de latitude que causaram uma exceção, uma exceção repetível consistente e confiável, durante um mês também testando em conjunto a confiabilidade do resfriamento da CPU fã e minha paciência. Sim, eu apaguei a maioria desses logs, pois o objetivo deles era principalmente provar o argumento (se o trocadilho for permitido). Mas eu tenho alguns registros mais curtos de "valores de problemas futuros", mantidos para fins de teste.

Precisão: Será um e todo o resultado haversine perder alguma precisão, normalizando-se que pouco para trás pequeno em domínio? Não muito, talvez não mais do que as aproximações e arredondamentos do fp64 já estavam sendo introduzidos, o que causou essa ligeira mudança de domínio. Se você já achou que a haversine é aceitável acima de muitos - mais simples, mais rápido, mais fácil de personalizar, solucionar problemas e manter, a haversine pode ser uma boa solução para o seu projeto.

Eu usei haversine em uma skysphere projetada acima para medir distâncias angulares entre objetos no céu, como visto de uma posição na terra, mapeando azimute e alt para skysphere lat lon coordenadas equivalentes, sem elipsóide a considerar, desde que o A skysphere teórica projetada é uma esfera perfeita, quando se trata de medir a distância angular, olhar ângulos entre dois objetos de uma posição na superfície da Terra. Atende perfeitamente às minhas necessidades. Portanto, o haversine ainda é muito útil e preciso em certas aplicações (bem dentro dos meus propósitos) ... mas se você o usar, seja na Terra para SIG ou navegação, ou nas observações e medições de objetos no céu, proteja no caso de pontos antipodais ou pontos antipodais muito próximos, testando ume empurrando-o de volta ao seu domínio necessário, quando necessário.

O haversine desprotegido está em toda a Internet, e eu só vi um post antigo da usenet que mostrou alguma proteção, acho de alguém da JPL, e que pode ter sido antes de 1985, antes da especificação IEEE 754 de ponto flutuante. Duas outras páginas mencionaram possíveis problemas próximos a pontos antipodais, mas não os descreveram, ou como alguém poderia mitigá-los. Portanto, existe uma preocupação com os novatos (como eu), que nem sempre entendem as boas práticas o suficiente para continuar pesquisando e testando alguns códigos que eles copiaram e colaram em um projeto de confiança. A publicação intrigante do cffk foi refrescante, pois era pública com esses tipos de problemas, que não são mencionados com frequência, raramente codificados publicamente para proteção em trechos e raramente discutidos dessa maneira, em comparação com a quantidade de versões desprotegidas e não discutidas publicadas.

A partir de 20190923, a página wiki da fórmula haversine realmente menciona o problema possível em pontos antipodais, devido a problemas de ponto flutuante em dispositivos de computação ... encorajadores ...

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

(como essa página da wiki não possui, no momento, uma âncora html para a seção à qual eu vincularia diretamente, portanto, após o carregamento da página, faça uma pesquisa nessa página do navegador por 'Ao usar essas fórmulas' e você veja o problema do haversine com os pontos antipodais mencionados, mais oficialmente.)

E este outro site também possui uma breve menção a ele:

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

Se alguém encontrar nessa página informações sobre 'incluindo proteção contra erros de arredondamento', existe isso ...

Se atan2 não estiver disponível, c poderá ser calculado a partir de 2 ⋅ asin (min (1, √a)) (incluindo proteção contra erros de arredondamento).

Agora, há um caso raro em que erros de arredondamento são mencionados e a proteção é mostrada para a versão asin (), ainda não mencionada ou mostrada para a versão atan2 (). Mas pelo menos o risco de erros de arredondamento é mencionado.

imho, qualquer aplicativo 24/7/365 usando haversine, precisa dessa proteção perto dos pontos antipodais como um detalhe importante e simples.

Não sei quais pacotes haversine incluem ou não essa proteção, mas se você é novo em tudo isso e usará a (s) versão (ões) de snippet (s) popularmente publicada (s), agora sabe que precisa de proteção e essa proteção é muito simples de implementar, ou seja, se você não estiver usando o vincenty e não estiver usando um haversine empacotado sem acesso fácil para modificar o código do pacote.

IOW, seja usando vincenty, haversine ou sloc, é preciso ficar ciente de quaisquer problemas com o código, coisas a serem observadas e mitigadas, e como lidar com questões vincenty vs haversine vs sloc será diferente à medida que se tornar consciente de cada um. questões ocultas / edgecases, que podem ou não ser conhecidas popularmente.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.