Calcular distância entre dois pontos de latitude-longitude? (Fórmula de Haversine)

907

Como calculo a distância entre dois pontos especificados por latitude e longitude?

Para esclarecimento, eu gostaria da distância em quilômetros; os pontos usam o sistema WGS84 e eu gostaria de entender a precisão relativa das abordagens disponíveis.

Robin Minto
fonte
Para maior precisão - consulte stackoverflow.com/questions/1420045/…
Lior Kogan
3
Observe que você não pode aplicar uma fórmula Haversine em um elipsóide de revolução como o WGS 84. Você só pode aplicar esse método em uma esfera com um raio.
Mike T
3
A maioria das respostas aqui usa trigonometria esférica simples, portanto os resultados são bastante brutos comparados às distâncias elipsóides WGS84 usadas no sistema GPS. Algumas das respostas se referem à fórmula de Vincenty para elipsóides, mas esse algoritmo foi projetado para uso nas calculadoras de mesa da década de 1960 e apresenta problemas de estabilidade e precisão; agora temos melhor hardware e software. Consulte GeographicLib para obter uma biblioteca de alta qualidade com implementações em vários idiomas.
PM 2Ring
@MikeT - é verdade, embora muitas das respostas aqui pareçam úteis em pequenas distâncias : se você tirar lat / long do WGS 84 e aplicar o Haversine como se fossem pontos em uma esfera, não obtenha respostas cujos erros são devidos apenas a o fator de achatamento da Terra, talvez até 1% de uma fórmula mais precisa? Com a ressalva de que estas são pequenas distâncias, digamos dentro de uma única cidade.
Home
1
Para estas plataformas: Mono / .NET 4.5 / .NET Core / Windows Phone 8.x / Plataforma Universal do Windows / Xamarin iOS / Xamarin Android, consulte stackoverflow.com/a/54296314/2736742
A. Morel

Respostas:

1148

Esse link pode ser útil para você, pois detalha o uso da fórmula de Haversine para calcular a distância.

Excerto:

Esse script [em Javascript] calcula as distâncias de grandes círculos entre os dois pontos - ou seja, a menor distância sobre a superfície da Terra - usando a fórmula 'Haversine'.

function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  var dLon = deg2rad(lon2-lon1); 
  var a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * 
    Math.sin(dLon/2) * Math.sin(dLon/2)
    ; 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  var d = R * c; // Distance in km
  return d;
}

function deg2rad(deg) {
  return deg * (Math.PI/180)
}
Mandril
fonte
51
Esse cálculo / método explica que a Terra é um esferóide (não uma esfera perfeita)? A pergunta original pedia a distância entre os pontos em um globo WGS84. Não tenho certeza de quanto erro aparece usando uma esfera perfeita, mas suspeito que pode ser bastante dependendo de onde os pontos estão no globo, portanto vale a pena lembrar a distinção.
Redcalx
15
A fórmula de Haversine não explica que a Terra seja um esferóide, então você receberá algum erro devido a esse fato. Não é possível garantir a correção correta para melhor que 0,5%. Esse pode ou não ser um nível aceitável de erro.
Brandon
24
Existe alguma razão para usar em Math.atan2(Math.sqrt(a), Math.sqrt(1-a))vez de Math.asin(Math.sqrt(h)), qual seria a implementação direta da fórmula que o artigo da Wikipedia usa? É mais eficiente e / ou mais numericamente estável?
Musiphil 20/12/12
16
@UsmanMutawakil Bem, as 38 milhas que você recebe são distâncias na estrada. Este algoritmo calcula uma distância em linha reta na superfície da Terra. O Google Maps possui uma ferramenta de distância (canto inferior esquerdo, "Labs") que faz o mesmo, use-a para comparar.
Pascal
4
@ Forte_201092: Porque isso não é necessário - como (sin(x))²iguais(sin(-x))²
Jean Hominal
360

Eu precisava calcular muitas distâncias entre os pontos do meu projeto, então fui em frente e tentei otimizar o código, que encontrei aqui. Em média, em diferentes navegadores, minha nova implementação é executada duas vezes mais rápido que a resposta mais votada.

function distance(lat1, lon1, lat2, lon2) {
  var p = 0.017453292519943295;    // Math.PI / 180
  var c = Math.cos;
  var a = 0.5 - c((lat2 - lat1) * p)/2 + 
          c(lat1 * p) * c(lat2 * p) * 
          (1 - c((lon2 - lon1) * p))/2;

  return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
}

Você pode brincar com meu jsPerf e ver os resultados aqui .

Recentemente, eu precisei fazer o mesmo em python, então aqui está uma implementação em python :

from math import cos, asin, sqrt, pi

def distance(lat1, lon1, lat2, lon2):
    p = pi/180
    a = 0.5 - cos((lat2-lat1)*p)/2 + cos(lat1*p) * cos(lat2*p) * (1-cos((lon2-lon1)*p))/2
    return 12742 * asin(sqrt(a)) #2*R*asin...

E por uma questão de completude: Haversine no wiki.

Salvador Dalí
fonte
13
@AngularM e é muito provável que o Google calcule a distância se você seguir algumas estradas e não uma linha reta.
Salvador Dali
3
Google calcula a distância condução, este calcula "em linha recta"
Hobbyist
4
@Ouadie e isso vai melhorar a velocidade? Muito provavelmente não, mas vou acabar com um monte de 'suas coisas não trabalho para as pessoas que CopyPaste-lo em navegadores antigos
Salvador Dali
4
bem, sim, mas o que // 2 * R; R = 6371 kmsignifica? e o método atual fornece resposta em km ou milhas? precisa de uma documentação melhor. Graças
Khalil Khalaf
20
@KhalilKhalaf você está brincando ou tentando trollar aqui? km significa quilômetros. O que você acha que R representa (especialmente se falamos de um shpere)? Adivinhe em que unidades a resposta será se você já vê o km. Que tipo de documentação você está procurando aqui: existem literalmente 4 linhas lá.
Salvador Dali
69

Aqui está uma implementação de C #:

static class DistanceAlgorithm
{
    const double PIx = 3.141592653589793;
    const double RADIUS = 6378.16;

    /// <summary>
    /// Convert degrees to Radians
    /// </summary>
    /// <param name="x">Degrees</param>
    /// <returns>The equivalent in radians</returns>
    public static double Radians(double x)
    {
        return x * PIx / 180;
    }

    /// <summary>
    /// Calculate the distance between two places.
    /// </summary>
    /// <param name="lon1"></param>
    /// <param name="lat1"></param>
    /// <param name="lon2"></param>
    /// <param name="lat2"></param>
    /// <returns></returns>
    public static double DistanceBetweenPlaces(
        double lon1,
        double lat1,
        double lon2,
        double lat2)
    {
        double dlon = Radians(lon2 - lon1);
        double dlat = Radians(lat2 - lat1);

        double a = (Math.Sin(dlat / 2) * Math.Sin(dlat / 2)) + Math.Cos(Radians(lat1)) * Math.Cos(Radians(lat2)) * (Math.Sin(dlon / 2) * Math.Sin(dlon / 2));
        double angle = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
        return angle * RADIUS;
    }

}
jaircazarin-old-account
fonte
14
Você está usando o raio equatorial, mas você deve estar usando o raio médio, que é 6371 km
Philippe Leybaert
7
Não deveria ser isso double dlon = Radians(lon2 - lon1);e #double dlat = Radians(lat2 - lat1);
Chris Marisic
Eu concordo com Chris Marisic. Usei o código original e os cálculos estavam errados. Eu adicionei a chamada para converter os deltas em radianos e funciona corretamente agora. Enviei uma edição e estou aguardando a revisão por pares.
Bryan Bedard
Enviei outra edição porque lat1 e lat2 também precisam ser convertidos em radianos. Eu também revisou a fórmula para a atribuição a um para coincidir com a fórmula e código encontrado aqui: movable-type.co.uk/scripts/latlong.html
Bryan Bedard
o RADIUSvalor precisa ser 6371 como nas outras respostas?
Chris Hayes
66

Aqui está uma implementação em java da fórmula Haversine.

public final static double AVERAGE_RADIUS_OF_EARTH_KM = 6371;
public int calculateDistanceInKilometer(double userLat, double userLng,
  double venueLat, double venueLng) {

    double latDistance = Math.toRadians(userLat - venueLat);
    double lngDistance = Math.toRadians(userLng - venueLng);

    double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
      + Math.cos(Math.toRadians(userLat)) * Math.cos(Math.toRadians(venueLat))
      * Math.sin(lngDistance / 2) * Math.sin(lngDistance / 2);

    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

    return (int) (Math.round(AVERAGE_RADIUS_OF_EARTH_KM * c));
}

Observe que aqui estamos arredondando a resposta para o km mais próximo.

whostolebenfrog
fonte
2
Se quiséssemos calcular a distância entre dois pontos em metros, qual seria a maneira mais precisa? Para usar 6371000como o raio da terra? (o raio médio da terra é 6371000 metros) ou converte quilômetros em metros de sua função?
Micro
se você deseja milhas, multiplique o resultado por0.621371
lasec0203 09/09/19
42

Muito obrigado por tudo isso. Usei o seguinte código no meu aplicativo Objective-C para iPhone:

const double PIx = 3.141592653589793;
const double RADIO = 6371; // Mean radius of Earth in Km

double convertToRadians(double val) {

   return val * PIx / 180;
}

-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {

        double dlon = convertToRadians(place2.longitude - place1.longitude);
        double dlat = convertToRadians(place2.latitude - place1.latitude);

        double a = ( pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))) * cos(convertToRadians(place2.latitude)) * pow(sin(dlon / 2), 2);
        double angle = 2 * asin(sqrt(a));

        return angle * RADIO;
}

Latitude e Longitude estão em decimal. Eu não usei min () para a chamada asin (), pois as distâncias que estou usando são tão pequenas que não exigem.

Ele deu respostas incorretas até eu passar os valores em Radianos - agora é praticamente o mesmo que os valores obtidos no aplicativo Map da Apple :-)

Atualização extra:

Se você estiver usando o iOS4 ou posterior, a Apple fornecerá alguns métodos para fazer isso, para que a mesma funcionalidade seja alcançada com:

-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {

    MKMapPoint  start, finish;


    start = MKMapPointForCoordinate(place1);
    finish = MKMapPointForCoordinate(place2);

    return MKMetersBetweenMapPoints(start, finish) / 1000;
}
Stephen Watson
fonte
1
O SDK do iOS tem sua própria implementação: developer.apple.com/library/ios/documentation/CoreLocation/… :
tuler 27/03/16
Eu acho que os parênteses pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))estão incorretos. Remova-os e o resultado corresponderá ao que recebo quando uso outras implementações nesta página ou implemente a fórmula Haversine da Wikipedia do zero.
Zanedp 17/01/19
Usando as coordenadas (40.7127837, -74.0059413) para Nova York e (34.052234, -118.243685) para LA, com ()essa quantia em torno dessa quantia, recebo 3869,75. Sem eles, recebo 3935.75, que é praticamente o que uma pesquisa na web aparece.
zanedp 17/01/19
40

Esta é uma função PHP simples que fornecerá uma aproximação bastante razoável (com margem de erro de +/- 1%).

<?php
function distance($lat1, $lon1, $lat2, $lon2) {

    $pi80 = M_PI / 180;
    $lat1 *= $pi80;
    $lon1 *= $pi80;
    $lat2 *= $pi80;
    $lon2 *= $pi80;

    $r = 6372.797; // mean radius of Earth in km
    $dlat = $lat2 - $lat1;
    $dlon = $lon2 - $lon1;
    $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlon / 2) * sin($dlon / 2);
    $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
    $km = $r * $c;

    //echo '<br/>'.$km;
    return $km;
}
?>

Como dito antes; a terra não é uma esfera. É como um beisebol velho e velho que Mark McGwire decidiu praticar - está cheio de amolgadelas. Os cálculos mais simples (como este) tratam-no como uma esfera.

Métodos diferentes podem ser mais ou menos precisos, de acordo com o local onde você está neste ovóide irregular E a que distância estão seus pontos (quanto mais próximos eles estiverem, menor será a margem de erro absoluta). Quanto mais precisa for sua expectativa, mais complexa será a matemática.

Para mais informações: distância geográfica da wikipedia

Tony Gil
fonte
4
Isso funciona perfeitamente! Acabei de adicionar $ distance_miles = $ km * 0,621371; e isso é tudo o que eu precisava para uma distância aproximada em milhas! Obrigado Tony.
31

Eu posto aqui meu exemplo de trabalho.

Liste todos os pontos da tabela com distância entre um ponto designado (usamos um ponto aleatório - lat: 45.20327, long: 23.7806) menor que 50 KM, com latitude e longitude, no MySQL (os campos da tabela são coord_lat e coord_long):

Liste todos com DISTÂNCIA <50, em Quilômetros (considerado raio da Terra 6371 KM):

SELECT denumire, (6371 * acos( cos( radians(45.20327) ) * cos( radians( coord_lat ) ) * cos( radians( 23.7806 ) - radians(coord_long) ) + sin( radians(45.20327) ) * sin( radians(coord_lat) ) )) AS distanta 
FROM obiective 
WHERE coord_lat<>'' 
    AND coord_long<>'' 
HAVING distanta<50 
ORDER BY distanta desc

O exemplo acima foi testado no MySQL 5.0.95 e 5.5.16 (Linux).

conualfy
fonte
Eu acho que uma boa abordagem pode estar pré-filtrando os resultados usando uma aproximação, então a fórmula pesada é aplicada apenas em alguns casos. Especialmente útil se você tiver outras condições. Estou usando isso para o aprox inicial: stackoverflow.com/questions/1253499/…
Pato
28

Nas outras respostas, uma implementação em está desaparecido.

Calcular a distância entre dois pontos é bastante direto com a distmfunção do geospherepacote:

distm(p1, p2, fun = distHaversine)

Onde:

p1 = longitude/latitude for point(s)
p2 = longitude/latitude for point(s)
# type of distance calculation
fun = distCosine / distHaversine / distVincentySphere / distVincentyEllipsoid 

Como a Terra não é perfeitamente esférica, a fórmula Vincenty para elipsóides é provavelmente a melhor maneira de calcular distâncias. Assim, no geospherepacote você usa então:

distm(p1, p2, fun = distVincentyEllipsoid)

Claro que você não precisa necessariamente usar o geospherepacote, você também pode calcular a distância na base Rcom uma função:

hav.dist <- function(long1, lat1, long2, lat2) {
  R <- 6371
  diff.long <- (long2 - long1)
  diff.lat <- (lat2 - lat1)
  a <- sin(diff.lat/2)^2 + cos(lat1) * cos(lat2) * sin(diff.long/2)^2
  b <- 2 * asin(pmin(1, sqrt(a))) 
  d = R * b
  return(d)
}
Jaap
fonte
Para ter certeza de que estou claro sobre o que você disse: O código que você fornece no final do post: isso é uma implementação da fórmula Vincenty? Até onde você sabe, deve dar a mesma resposta que chamar Vincenty na geosfera? [Eu não tenho geosfera ou outra biblioteca; apenas procurando algum código para incluir em um aplicativo de plataforma cruzada. Eu, claro, verificar alguns casos de teste contra uma boa calculadora conhecido].
ToolmakerSteve
1
@ToolmakerSteve a função no final da minha resposta é uma implementação do método Haversine
Jaap
Oi @ Jaa, posso perguntar qual é a unidade de medida da fórmula? Está em metros?
Jackson
11

O haversine é definitivamente uma boa fórmula para provavelmente a maioria dos casos, outras respostas já o incluem, então não vou ocupar o espaço. Mas é importante observar que, independentemente da fórmula usada (sim, não apenas uma). Devido à enorme variedade de precisão possível, bem como ao tempo de computação necessário. A escolha da fórmula requer um pouco mais de reflexão do que uma resposta simples e simples.

Esta postagem de uma pessoa da NASA, é a melhor que encontrei ao discutir as opções

http://www.cs.nyu.edu/visual/home/proj/tiger/gisfaq.html

Por exemplo, se você estiver apenas classificando linhas por distância em um raio de 160 quilômetros. A fórmula da terra plana será muito mais rápida que a haversine.

HalfPi = 1.5707963;
R = 3956; /* the radius gives you the measurement unit*/

a = HalfPi - latoriginrad;
b = HalfPi - latdestrad;
u = a * a + b * b;
v = - 2 * a * b * cos(longdestrad - longoriginrad);
c = sqrt(abs(u + v));
return R * c;

Observe que há apenas um cosseno e uma raiz quadrada. Vs 9 deles na fórmula Haversine.

Arturo Hernandez
fonte
É uma boa possibilidade. Lembre-se de que a distância máxima recomendada na discussão é de 20 quilômetros, e não 100 , e que, mesmo assim, os erros podem subir até 30 metros (100 pés), dependendo da posição do globo.
Eric Wu
7

Você pode usar a construção em CLLocationDistance para calcular isso:

CLLocation *location1 = [[CLLocation alloc] initWithLatitude:latitude1 longitude:longitude1];
CLLocation *location2 = [[CLLocation alloc] initWithLatitude:latitude2 longitude:longitude2];
[self distanceInMetersFromLocation:location1 toLocation:location2]

- (int)distanceInMetersFromLocation:(CLLocation*)location1 toLocation:(CLLocation*)location2 {
    CLLocationDistance distanceInMeters = [location1 distanceFromLocation:location2];
    return distanceInMeters;
}

No seu caso, se você deseja quilômetros, basta dividir por 1000.

Andre Cytryn
fonte
7

Não gosto de adicionar mais uma resposta, mas a API do Google Maps v.3 possui geometria esférica (e mais). Depois de converter seu WGS84 em graus decimais, você pode fazer o seguinte:

<script src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry" type="text/javascript"></script>  

distance = google.maps.geometry.spherical.computeDistanceBetween(
    new google.maps.LatLng(fromLat, fromLng), 
    new google.maps.LatLng(toLat, toLng));

Nenhuma palavra sobre a precisão dos cálculos do Google ou mesmo qual modelo é usado (embora diga "esférico" em vez de "geóide". A propósito, a distância da "linha reta" obviamente será diferente da distância se alguém viaja no superfície da terra que é o que todos parecem presumir.

Steven Christenson
fonte
a distância está em metros. em alternativa pode-se usar computeLength ()
electrobabe
7

Implementação em Python A origem é o centro dos Estados Unidos contíguos.

from haversine import haversine
origin = (39.50, 98.35)
paris = (48.8567, 2.3508)
haversine(origin, paris, miles=True)

Para obter a resposta em quilômetros, basta definir milhas = false.

invoketheshell
fonte
1
Você está importando um pacote não padrão que faz todo o trabalho. Não sei se isso é tão útil.
Teepeemm
O pacote está no PyPI, Python Package Index, como um pacote python 3 junto com o numpy e o scikit-learn. Não sei por que alguém se opõe aos pacotes. Eles tendem a ser bastante úteis. Como código aberto, pode-se também examinar os métodos contidos. Eu acho que muitos acham este pacote útil, então vou deixar o post, apesar do voto negativo. Felicidades. :)
invoketheshell
7

Poderia haver uma solução mais simples e mais correta: o perímetro da Terra é de 40.000 km no equador, cerca de 37.000 no ciclo de Greenwich (ou qualquer longitude). Portanto:

pythagoras = function (lat1, lon1, lat2, lon2) {
   function sqr(x) {return x * x;}
   function cosDeg(x) {return Math.cos(x * Math.PI / 180.0);}

   var earthCyclePerimeter = 40000000.0 * cosDeg((lat1 + lat2) / 2.0);
   var dx = (lon1 - lon2) * earthCyclePerimeter / 360.0;
   var dy = 37000000.0 * (lat1 - lat2) / 360.0;

   return Math.sqrt(sqr(dx) + sqr(dy));
};

Concordo que deve ser afinado, pois eu mesmo disse que é um elipsóide, de modo que o raio a ser multiplicado pelo cosseno varia. Mas é um pouco mais preciso. Comparado com o Google Maps e reduziu o erro significativamente.

Meymann
fonte
Esta função retorna a distância em km?
Wikki 15/10
É apenas porque os ciclos do equador e da longitude estão em km. Para milhas, basta dividir 40000 e 37000 por 1,6. Sentindo geeky, você pode convertê-lo em Ris, multiplyung por cerca de 7 ou parasanga, dividindo por 2,2 ;-)
Meymann
Esta parece ser a melhor resposta oferecida aqui. Desejo usá-lo, mas me pergunto se existe uma maneira de verificar a correção desse algoritmo. Eu testei f (50,5,58,3). Dá 832 km, enquanto movable-type.co.uk/scripts/latlong.html, usando a fórmula 'haversine', dá 899 km. Existe uma diferença tão grande?
amigos estão dizendo sobre chong lip
Além disso, acho que o valor retornado pelo código acima está em m, e não em km.
amigos estão dizendo sobre chong lip
@ChongLipPhang - CUIDADO: O teorema de Pitágoras é apenas uma aproximação razoável para pequenas áreas , pois esse teorema assume que a Terra é plana. Como um caso extremo, comece no equador e mova-se 90 graus leste e 90 graus norte. O resultado final, é claro, é o pólo norte e é o mesmo que se mover 0 graus leste e 90 graus norte; então, fazer o sqrt (sqr (dx) + sqr (dy)) será muito desagradável no primeiro caso. ~ sqrt (10km sqr + 10km sqr) ~ = 14,4 km vs distância correta ~ 10km.
Home
7

Todas as respostas acima assumem que a Terra é uma esfera. No entanto, uma aproximação mais precisa seria a de um esferóide oblato.

a= 6378.137#equitorial radius in km
b= 6356.752#polar radius in km

def Distance(lat1, lons1, lat2, lons2):
    lat1=math.radians(lat1)
    lons1=math.radians(lons1)
    R1=(((((a**2)*math.cos(lat1))**2)+(((b**2)*math.sin(lat1))**2))/((a*math.cos(lat1))**2+(b*math.sin(lat1))**2))**0.5 #radius of earth at lat1
    x1=R*math.cos(lat1)*math.cos(lons1)
    y1=R*math.cos(lat1)*math.sin(lons1)
    z1=R*math.sin(lat1)

    lat2=math.radians(lat2)
    lons2=math.radians(lons2)
    R1=(((((a**2)*math.cos(lat2))**2)+(((b**2)*math.sin(lat2))**2))/((a*math.cos(lat2))**2+(b*math.sin(lat2))**2))**0.5 #radius of earth at lat2
    x2=R*math.cos(lat2)*math.cos(lons2)
    y2=R*math.cos(lat2)*math.sin(lons2)
    z2=R*math.sin(lat2)

    return ((x1-x2)**2+(y1-y2)**2+(z1-z2)**2)**0.5
Keerthana Gopalakrishnan
fonte
6

Aqui está a implementação do SQL para calcular a distância em km,

SELECT UserId, ( 3959 * acos( cos( radians( your latitude here ) ) * cos( radians(latitude) ) * 
cos( radians(longitude) - radians( your longitude here ) ) + sin( radians( your latitude here ) ) * 
sin( radians(latitude) ) ) ) AS distance FROM user HAVING
distance < 5  ORDER BY distance LIMIT 0 , 5;

Para mais detalhes sobre a implementação, programando a linguagem, você pode simplesmente passar pelo script php fornecido aqui

Kiran Maniya
fonte
5

Aqui está uma implementação datilografada da fórmula Haversine

static getDistanceFromLatLonInKm(lat1: number, lon1: number, lat2: number, lon2: number): number {
    var deg2Rad = deg => {
        return deg * Math.PI / 180;
    }

    var r = 6371; // Radius of the earth in km
    var dLat = deg2Rad(lat2 - lat1);   
    var dLon = deg2Rad(lon2 - lon1);
    var a =
        Math.sin(dLat / 2) * Math.sin(dLat / 2) +
        Math.cos(deg2Rad(lat1)) * Math.cos(deg2Rad(lat2)) *
        Math.sin(dLon / 2) * Math.sin(dLon / 2);
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    var d = r * c; // Distance in km
    return d;
}
Sel
fonte
5

Como apontado, um cálculo preciso deve levar em conta que a Terra não é uma esfera perfeita. Aqui estão algumas comparações dos vários algoritmos oferecidos aqui:

geoDistance(50,5,58,3)
Haversine: 899 km
Maymenn: 833 km
Keerthana: 897 km
google.maps.geometry.spherical.computeDistanceBetween(): 900 km

geoDistance(50,5,-58,-3)
Haversine: 12030 km
Maymenn: 11135 km
Keerthana: 10310 km
google.maps.geometry.spherical.computeDistanceBetween(): 12044 km

geoDistance(.05,.005,.058,.003)
Haversine: 0.9169 km
Maymenn: 0.851723 km
Keerthana: 0.917964 km
google.maps.geometry.spherical.computeDistanceBetween(): 0.917964 km

geoDistance(.05,80,.058,80.3)
Haversine: 33.37 km
Maymenn: 33.34 km
Keerthana: 33.40767 km
google.maps.geometry.spherical.computeDistanceBetween(): 33.40770 km

Em pequenas distâncias, o algoritmo de Keerthana parece coincidir com o do Google Maps. O Google Maps parece não seguir nenhum algoritmo simples, sugerindo que ele pode ser o método mais preciso aqui.

De qualquer forma, aqui está uma implementação Javascript do algoritmo de Keerthana:

function geoDistance(lat1, lng1, lat2, lng2){
    const a = 6378.137; // equitorial radius in km
    const b = 6356.752; // polar radius in km

    var sq = x => (x*x);
    var sqr = x => Math.sqrt(x);
    var cos = x => Math.cos(x);
    var sin = x => Math.sin(x);
    var radius = lat => sqr((sq(a*a*cos(lat))+sq(b*b*sin(lat)))/(sq(a*cos(lat))+sq(b*sin(lat))));

    lat1 = lat1 * Math.PI / 180;
    lng1 = lng1 * Math.PI / 180;
    lat2 = lat2 * Math.PI / 180;
    lng2 = lng2 * Math.PI / 180;

    var R1 = radius(lat1);
    var x1 = R1*cos(lat1)*cos(lng1);
    var y1 = R1*cos(lat1)*sin(lng1);
    var z1 = R1*sin(lat1);

    var R2 = radius(lat2);
    var x2 = R2*cos(lat2)*cos(lng2);
    var y2 = R2*cos(lat2)*sin(lng2);
    var z2 = R2*sin(lat2);

    return sqr(sq(x1-x2)+sq(y1-y2)+sq(z1-z2));
}
Chong Lip Phang
fonte
4

Este script [em PHP] calcula distâncias entre os dois pontos.

public static function getDistanceOfTwoPoints($source, $dest, $unit='K') {
        $lat1 = $source[0];
        $lon1 = $source[1];
        $lat2 = $dest[0];
        $lon2 = $dest[1];

        $theta = $lon1 - $lon2;
        $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
        $dist = acos($dist);
        $dist = rad2deg($dist);
        $miles = $dist * 60 * 1.1515;
        $unit = strtoupper($unit);

        if ($unit == "K") {
            return ($miles * 1.609344);
        }
        else if ($unit == "M")
        {
            return ($miles * 1.609344 * 1000);
        }
        else if ($unit == "N") {
            return ($miles * 0.8684);
        } 
        else {
            return $miles;
        }
    }
Er.Subhendu Kumar Pati
fonte
4

Implementação Java de acordo com a fórmula Haversine

double calculateDistance(double latPoint1, double lngPoint1, 
                         double latPoint2, double lngPoint2) {
    if(latPoint1 == latPoint2 && lngPoint1 == lngPoint2) {
        return 0d;
    }

    final double EARTH_RADIUS = 6371.0; //km value;

    //converting to radians
    latPoint1 = Math.toRadians(latPoint1);
    lngPoint1 = Math.toRadians(lngPoint1);
    latPoint2 = Math.toRadians(latPoint2);
    lngPoint2 = Math.toRadians(lngPoint2);

    double distance = Math.pow(Math.sin((latPoint2 - latPoint1) / 2.0), 2) 
            + Math.cos(latPoint1) * Math.cos(latPoint2)
            * Math.pow(Math.sin((lngPoint2 - lngPoint1) / 2.0), 2);
    distance = 2.0 * EARTH_RADIUS * Math.asin(Math.sqrt(distance));

    return distance; //km value
}
ak-j
fonte
3

Para calcular a distância entre dois pontos em uma esfera, você precisa fazer o cálculo do Grande Círculo .

Existem várias bibliotecas C / C ++ para ajudar na projeção de mapas no MapTools se você precisar suas distâncias em uma superfície plana. Para fazer isso, você precisará da seqüência de projeção dos vários sistemas de coordenadas.

Você também pode encontrar o MapWindow uma ferramenta útil para visualizar os pontos. Também como código aberto, é um guia útil para usar a biblioteca proj.dll, que parece ser a principal biblioteca de projeção de código aberto.


fonte
3

Aqui está a implementação de resposta aceita portada para Java, caso alguém precise.

package com.project529.garage.util;


/**
 * Mean radius.
 */
private static double EARTH_RADIUS = 6371;

/**
 * Returns the distance between two sets of latitudes and longitudes in meters.
 * <p/>
 * Based from the following JavaScript SO answer:
 * http://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula,
 * which is based on https://en.wikipedia.org/wiki/Haversine_formula (error rate: ~0.55%).
 */
public double getDistanceBetween(double lat1, double lon1, double lat2, double lon2) {
    double dLat = toRadians(lat2 - lat1);
    double dLon = toRadians(lon2 - lon1);

    double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
                    Math.sin(dLon / 2) * Math.sin(dLon / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    double d = EARTH_RADIUS * c;

    return d;
}

public double toRadians(double degrees) {
    return degrees * (Math.PI / 180);
}
Eduardo Naveda
fonte
2

Aqui está a implementação VB.NET, essa implementação fornecerá o resultado em KM ou Miles com base no valor de Enum que você passar.

Public Enum DistanceType
    Miles
    KiloMeters
End Enum

Public Structure Position
    Public Latitude As Double
    Public Longitude As Double
End Structure

Public Class Haversine

    Public Function Distance(Pos1 As Position,
                             Pos2 As Position,
                             DistType As DistanceType) As Double

        Dim R As Double = If((DistType = DistanceType.Miles), 3960, 6371)

        Dim dLat As Double = Me.toRadian(Pos2.Latitude - Pos1.Latitude)

        Dim dLon As Double = Me.toRadian(Pos2.Longitude - Pos1.Longitude)

        Dim a As Double = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(Me.toRadian(Pos1.Latitude)) * Math.Cos(Me.toRadian(Pos2.Latitude)) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2)

        Dim c As Double = 2 * Math.Asin(Math.Min(1, Math.Sqrt(a)))

        Dim result As Double = R * c

        Return result

    End Function

    Private Function toRadian(val As Double) As Double

        Return (Math.PI / 180) * val

    End Function

End Class
Taiseer Joudeh
fonte
Ao calcular "a", você escreveu Math.Sin ( dLat ..) duas vezes por engano?
Marco Ottina 24/07/19
2

Condensou o cálculo simplificando a fórmula.

Aqui está em Ruby:

include Math
earth_radius_mi = 3959
radians = lambda { |deg| deg * PI / 180 }
coord_radians = lambda { |c| { :lat => radians[c[:lat]], :lng => radians[c[:lng]] } }

# from/to = { :lat => (latitude_in_degrees), :lng => (longitude_in_degrees) }
def haversine_distance(from, to)
  from, to = coord_radians[from], coord_radians[to]
  cosines_product = cos(to[:lat]) * cos(from[:lat]) * cos(from[:lng] - to[:lng])
  sines_product = sin(to[:lat]) * sin(from[:lat])
  return earth_radius_mi * acos(cosines_product + sines_product)
end
Kache
fonte
2
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2,units) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  var dLon = deg2rad(lon2-lon1); 
  var a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * 
    Math.sin(dLon/2) * Math.sin(dLon/2)
    ; 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  var d = R * c; 
  var miles = d / 1.609344; 

if ( units == 'km' ) {  
return d; 
 } else {
return miles;
}}

A solução de Chuck, válida por milhas também.

MPaulo
fonte
2

Aqui está minha implementação em java para calcular a distância através de graus decimais após alguma pesquisa. Eu usei o raio médio do mundo (da wikipedia) em km. Se você deseja milhas por resultado, use o raio do mundo em milhas.

public static double distanceLatLong2(double lat1, double lng1, double lat2, double lng2) 
{
  double earthRadius = 6371.0d; // KM: use mile here if you want mile result

  double dLat = toRadian(lat2 - lat1);
  double dLng = toRadian(lng2 - lng1);

  double a = Math.pow(Math.sin(dLat/2), 2)  + 
          Math.cos(toRadian(lat1)) * Math.cos(toRadian(lat2)) * 
          Math.pow(Math.sin(dLng/2), 2);

  double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

  return earthRadius * c; // returns result kilometers
}

public static double toRadian(double degrees) 
{
  return (degrees * Math.PI) / 180.0d;
}

fonte
2

No Mysql, use a seguinte função, passe os parâmetros como usando POINT(LONG,LAT)

CREATE FUNCTION `distance`(a POINT, b POINT)
 RETURNS double
    DETERMINISTIC
BEGIN

RETURN

GLength( LineString(( PointFromWKB(a)), (PointFromWKB(b)))) * 100000; -- To Make the distance in meters

END;
shanavascet
fonte
2
function getDistanceFromLatLonInKm(position1, position2) {
    "use strict";
    var deg2rad = function (deg) { return deg * (Math.PI / 180); },
        R = 6371,
        dLat = deg2rad(position2.lat - position1.lat),
        dLng = deg2rad(position2.lng - position1.lng),
        a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
            + Math.cos(deg2rad(position1.lat))
            * Math.cos(deg2rad(position1.lat))
            * Math.sin(dLng / 2) * Math.sin(dLng / 2),
        c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c;
}

console.log(getDistanceFromLatLonInKm(
    {lat: 48.7931459, lng: 1.9483572},
    {lat: 48.827167, lng: 2.2459745}
));
Raphael C
fonte
2

aqui está um exemplo no postgres sql (em km, para versão milhas, substitua 1.609344 por 0.8684 versão)

CREATE OR REPLACE FUNCTION public.geodistance(alat float, alng float, blat  

float, blng  float)
  RETURNS float AS
$BODY$
DECLARE
    v_distance float;
BEGIN

    v_distance = asin( sqrt(
            sin(radians(blat-alat)/2)^2 
                + (
                    (sin(radians(blng-alng)/2)^2) *
                    cos(radians(alat)) *
                    cos(radians(blat))
                )
          )
        ) * cast('7926.3352' as float) * cast('1.609344' as float) ;


    RETURN v_distance;
END 
$BODY$
language plpgsql VOLATILE SECURITY DEFINER;
alter function geodistance(alat float, alng float, blat float, blng float)
owner to postgres;
fisc
fonte
2

Aqui está outro código convertido para Ruby :

include Math
#Note: from/to = [lat, long]

def get_distance_in_km(from, to)
  radians = lambda { |deg| deg * Math.PI / 180 }
  radius = 6371 # Radius of the earth in kilometer
  dLat = radians[to[0]-from[0]]
  dLon = radians[to[1]-from[1]]

  cosines_product = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(radians[from[0]]) * Math.cos(radians[to[1]]) * Math.sin(dLon/2) * Math.sin(dLon/2)

  c = 2 * Math.atan2(Math.sqrt(cosines_product), Math.sqrt(1-cosines_product)) 
  return radius * c # Distance in kilometer
end
aldrien.h
fonte
1

há um bom exemplo aqui para calcular a distância com o PHP http://www.geodatasource.com/developers/php :

 function distance($lat1, $lon1, $lat2, $lon2, $unit) {

     $theta = $lon1 - $lon2;
     $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
     $dist = acos($dist);
     $dist = rad2deg($dist);
     $miles = $dist * 60 * 1.1515;
     $unit = strtoupper($unit);

     if ($unit == "K") {
         return ($miles * 1.609344);
     } else if ($unit == "N") {
          return ($miles * 0.8684);
     } else {
          return $miles;
     }
 }
ayalcinkaya
fonte