Calcular a distância entre 2 coordenadas GPS

Respostas:

406

Calcule a distância entre duas coordenadas por latitude e longitude , incluindo uma implementação Javascript.

Os locais oeste e sul são negativos. Lembre-se de minutos e segundos fora de 60, então S31 30 'é -31,50 graus.

Não se esqueça de converter graus em radianos . Muitos idiomas têm essa função. Ou é um cálculo simples: radians = degrees * PI / 180.

function degreesToRadians(degrees) {
  return degrees * Math.PI / 180;
}

function distanceInKmBetweenEarthCoordinates(lat1, lon1, lat2, lon2) {
  var earthRadiusKm = 6371;

  var dLat = degreesToRadians(lat2-lat1);
  var dLon = degreesToRadians(lon2-lon1);

  lat1 = degreesToRadians(lat1);
  lat2 = degreesToRadians(lat2);

  var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
          Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  return earthRadiusKm * c;
}

Aqui estão alguns exemplos de uso:

distanceInKmBetweenEarthCoordinates(0,0,0,0)  // Distance between same 
                                              // points should be 0
0

distanceInKmBetweenEarthCoordinates(51.5, 0, 38.8, -77.1) // From London
                                                          // to Arlington
5918.185064088764
cleto
fonte
17
No caso, não é óbvio, o método toRad () é uma personalização para o número protótipo, tais como: Number.prototype.toRad = function() { return this * (Math.PI / 180); }; . Ou, como indicado abaixo, você pode substituir (Math.PI/2)por 0,0174532925199433 (... qualquer precisão que considerar necessária) para aumentar o desempenho.
Vinney Kelly
44
Se alguém, especificamente aqueles que não procuram comentários no final da linha, estão olhando para esta fórmula e procurando uma unidade de distância, a unidade é km. :)
Dylan Knowles
11
Typo @VinneyKelly pequeno mas substitua (Math.PI / 180) não (Math.PI / 2), obrigado por todos ajuda
Patrick Murphy
11
@ChristianKRider Olhe para a primeira linha. Pense no que Rnormalmente significa em matemática e procure quantidades relevantes relacionadas à Terra para ver se os números correspondem.
Fund Monica's Lawsuit
3
Para as unidades imperiais (milhas), você pode mudar earthRadiusKma ser var earthRadiusMiles = 3959;, fyi.
Chapeljuice
59

Procure por estrias no Google; aqui está a minha solução:

#include <math.h>
#include "haversine.h"

#define d2r (M_PI / 180.0)

//calculate haversine distance for linear distance
double haversine_km(double lat1, double long1, double lat2, double long2)
{
    double dlong = (long2 - long1) * d2r;
    double dlat = (lat2 - lat1) * d2r;
    double a = pow(sin(dlat/2.0), 2) + cos(lat1*d2r) * cos(lat2*d2r) * pow(sin(dlong/2.0), 2);
    double c = 2 * atan2(sqrt(a), sqrt(1-a));
    double d = 6367 * c;

    return d;
}

double haversine_mi(double lat1, double long1, double lat2, double long2)
{
    double dlong = (long2 - long1) * d2r;
    double dlat = (lat2 - lat1) * d2r;
    double a = pow(sin(dlat/2.0), 2) + cos(lat1*d2r) * cos(lat2*d2r) * pow(sin(dlong/2.0), 2);
    double c = 2 * atan2(sqrt(a), sqrt(1-a));
    double d = 3956 * c; 

    return d;
}
Peter Greis
fonte
3
Você pode substituir (M_PI / 180.0) por 0.0174532925199433 para obter melhor desempenho.
Hlung 01/08/19
3
Em termos de desempenho: é possível calcular o pecado (dlat / 2.0) apenas uma vez, armazená-lo na variável a1 e, em vez de pow (, 2), é MUITO melhor usar a1 * a1. O mesmo para o outro pow (, 2).
pms
71
Sim, ou apenas use um compilador pós-década de 1960.
rightfold
17
Não há necessidade de "otimizar" (M_PI / 180.0) para uma constante que ninguém entende sem contexto. O compilador calcula esses termos fixos para você!
Patrick Cornelissen
2
@ TõnuSamuel Muito obrigado pelo seu comentário. Eu realmente gostei disso. Faz sentido que o compilador com otimização habilitada (-O) possa pré-calcular operações de constantes, tornando inútil o recolhimento manual. Vou testá-lo quando tiver tempo.
Hlung 02/01/19
44

Versão C # do Haversine

double _eQuatorialEarthRadius = 6378.1370D;
double _d2r = (Math.PI / 180D);

private int HaversineInM(double lat1, double long1, double lat2, double long2)
{
    return (int)(1000D * HaversineInKM(lat1, long1, lat2, long2));
}

private double HaversineInKM(double lat1, double long1, double lat2, double long2)
{
    double dlong = (long2 - long1) * _d2r;
    double dlat = (lat2 - lat1) * _d2r;
    double a = Math.Pow(Math.Sin(dlat / 2D), 2D) + Math.Cos(lat1 * _d2r) * Math.Cos(lat2 * _d2r) * Math.Pow(Math.Sin(dlong / 2D), 2D);
    double c = 2D * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1D - a));
    double d = _eQuatorialEarthRadius * c;

    return d;
}

Aqui está um .NET Fiddle disso , para que você possa testá-lo com seu próprio Lat / Longs.

Roman Makarov
fonte
11
Também adicionei um violino violento do .NET para que as pessoas possam testar isso com facilidade.
precisa saber é o seguinte
7
o .Net Framework possui uma compilação no método GeoCoordinate.GetDistanceTo. O assembly System.Device deve ser referenciado. Artigo MSDN msdn.microsoft.com/en-us/library/…
fnx 14/02/16
27

Versão Java do Algoritmo Haversine baseado na resposta de Roman Makarov a esta discussão

public class HaversineAlgorithm {

    static final double _eQuatorialEarthRadius = 6378.1370D;
    static final double _d2r = (Math.PI / 180D);

    public static int HaversineInM(double lat1, double long1, double lat2, double long2) {
        return (int) (1000D * HaversineInKM(lat1, long1, lat2, long2));
    }

    public static double HaversineInKM(double lat1, double long1, double lat2, double long2) {
        double dlong = (long2 - long1) * _d2r;
        double dlat = (lat2 - lat1) * _d2r;
        double a = Math.pow(Math.sin(dlat / 2D), 2D) + Math.cos(lat1 * _d2r) * Math.cos(lat2 * _d2r)
                * Math.pow(Math.sin(dlong / 2D), 2D);
        double c = 2D * Math.atan2(Math.sqrt(a), Math.sqrt(1D - a));
        double d = _eQuatorialEarthRadius * c;

        return d;
    }

}
Paulo Miguel Almeida
fonte
O @Radu garante que você o esteja usando corretamente e não troque locais de lat / log ao passá-los para qualquer método.
Paulo Miguel Almeida
11
Eu recebi uma resposta razoavelmente próxima usando esta fórmula. Baseei a precisão usando este site: movable-type.co.uk/scripts/latlong.html que me deu 0.07149km ao passo que a sua fórmula deu-me 0.07156que é uma precisão de cerca de 99%
Janac Meena
24

Isso é muito fácil de fazer com o tipo de região geográfica no SQL Server 2008.

SELECT geography::Point(lat1, lon1, 4326).STDistance(geography::Point(lat2, lon2, 4326))
-- computes distance in meters using eliptical model, accurate to the mm

4326 é SRID para o modelo Terra elipsoidal WGS84

Marko Tintor
fonte
19

Aqui está uma função Haversine em Python que eu uso:

from math import pi,sqrt,sin,cos,atan2

def haversine(pos1, pos2):
    lat1 = float(pos1['lat'])
    long1 = float(pos1['long'])
    lat2 = float(pos2['lat'])
    long2 = float(pos2['long'])

    degree_to_rad = float(pi / 180.0)

    d_lat = (lat2 - lat1) * degree_to_rad
    d_long = (long2 - long1) * degree_to_rad

    a = pow(sin(d_lat / 2), 2) + cos(lat1 * degree_to_rad) * cos(lat2 * degree_to_rad) * pow(sin(d_long / 2), 2)
    c = 2 * atan2(sqrt(a), sqrt(1 - a))
    km = 6367 * c
    mi = 3956 * c

    return {"km":km, "miles":mi}
PaulMcG
fonte
11

Aqui está em C # (lat e long em radianos):

double CalculateGreatCircleDistance(double lat1, double long1, double lat2, double long2, double radius)
{
    return radius * Math.Acos(
        Math.Sin(lat1) * Math.Sin(lat2)
        + Math.Cos(lat1) * Math.Cos(lat2) * Math.Cos(long2 - long1));
}

Se seu lat e long estão em graus, divida por 180 / PI para converter em radianos.

Mike Chamberlain
fonte
11
Este é o cálculo da "lei esférica dos cossenos", que é o método de cálculo menos preciso e mais suscetível a erros de uma grande distância do círculo.
John Machin
11

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
def distance(lat1, lon1, lat2, lon2):
    p = 0.017453292519943295
    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))

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

Salvador Dalí
fonte
11

Versão do PHP:

(Remova tudo deg2rad()se suas coordenadas já estiverem em radianos.)

$R = 6371; // km
$dLat = deg2rad($lat2-$lat1);
$dLon = deg2rad($lon2-$lon1);
$lat1 = deg2rad($lat1);
$lat2 = deg2rad($lat2);

$a = sin($dLat/2) * sin($dLat/2) +
     sin($dLon/2) * sin($dLon/2) * cos($lat1) * cos($lat2); 

$c = 2 * atan2(sqrt($a), sqrt(1-$a)); 
$d = $R * $c;
quape
fonte
11
Altere lat1 e lat2 para $ lat1 nad $ lat2.
vez disso
7

Uma função T-SQL, que eu uso para selecionar registros por distância para um centro

Create Function  [dbo].[DistanceInMiles] 
 (  @fromLatitude float ,
    @fromLongitude float ,
    @toLatitude float, 
    @toLongitude float
  )
   returns float
AS 
BEGIN
declare @distance float

select @distance = cast((3963 * ACOS(round(COS(RADIANS(90-@fromLatitude))*COS(RADIANS(90-@toLatitude))+ 
SIN(RADIANS(90-@fromLatitude))*SIN(RADIANS(90-@toLatitude))*COS(RADIANS(@fromLongitude-@toLongitude)),15)) 
)as float) 
  return  round(@distance,1)
END
Henry Vilinskiy
fonte
Este é o cálculo da "lei esférica dos cossenos", que é o método de cálculo menos preciso e mais suscetível a erros de uma grande distância do círculo.
John Machin
5

Se você precisar de algo mais preciso, dê uma olhada nisso .

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 que métodos como a distância do grande círculo que assume uma Terra esférica.

O primeiro método (direto) calcula a localização de um ponto que é uma determinada distância e azimute (direção) de outro ponto. O segundo método (inverso) calcula a distância geográfica e o azimute entre dois pontos dados. Eles têm sido amplamente utilizados em geodésia porque são precisos a 0,5 mm (0,020 ″) no elipsóide da Terra.

dsmelser
fonte
5

I. Quanto ao método "Breadcrumbs"

  1. O raio da Terra é diferente em diferentes Lat. Isso deve ser levado em consideração no algoritmo Haversine.
  2. Considere Troca de rolamento, que transforma linhas retas em arcos (que são mais longos)
  3. Levar em consideração a mudança de velocidade transformará arcos em espirais (que são maiores ou menores que os arcos)
  4. A mudança de altitude transformará espirais planas em 3D (que são mais longas novamente). Isso é muito importante para áreas montanhosas.

Veja abaixo a função em C, que leva em consideração os números 1 e 2:

double   calcDistanceByHaversine(double rLat1, double rLon1, double rHeading1,
       double rLat2, double rLon2, double rHeading2){
  double rDLatRad = 0.0;
  double rDLonRad = 0.0;
  double rLat1Rad = 0.0;
  double rLat2Rad = 0.0;
  double a = 0.0;
  double c = 0.0;
  double rResult = 0.0;
  double rEarthRadius = 0.0;
  double rDHeading = 0.0;
  double rDHeadingRad = 0.0;

  if ((rLat1 < -90.0) || (rLat1 > 90.0) || (rLat2 < -90.0) || (rLat2 > 90.0)
              || (rLon1 < -180.0) || (rLon1 > 180.0) || (rLon2 < -180.0)
              || (rLon2 > 180.0)) {
        return -1;
  };

  rDLatRad = (rLat2 - rLat1) * DEGREE_TO_RADIANS;
  rDLonRad = (rLon2 - rLon1) * DEGREE_TO_RADIANS;
  rLat1Rad = rLat1 * DEGREE_TO_RADIANS;
  rLat2Rad = rLat2 * DEGREE_TO_RADIANS;

  a = sin(rDLatRad / 2) * sin(rDLatRad / 2) + sin(rDLonRad / 2) * sin(
              rDLonRad / 2) * cos(rLat1Rad) * cos(rLat2Rad);

  if (a == 0.0) {
        return 0.0;
  }

  c = 2 * atan2(sqrt(a), sqrt(1 - a));
  rEarthRadius = 6378.1370 - (21.3847 * 90.0 / ((fabs(rLat1) + fabs(rLat2))
              / 2.0));
  rResult = rEarthRadius * c;

  // Chord to Arc Correction based on Heading changes. Important for routes with many turns and U-turns

  if ((rHeading1 >= 0.0) && (rHeading1 < 360.0) && (rHeading2 >= 0.0)
              && (rHeading2 < 360.0)) {
        rDHeading = fabs(rHeading1 - rHeading2);
        if (rDHeading > 180.0) {
              rDHeading -= 180.0;
        }
        rDHeadingRad = rDHeading * DEGREE_TO_RADIANS;
        if (rDHeading > 5.0) {
              rResult = rResult * (rDHeadingRad / (2.0 * sin(rDHeadingRad / 2)));
        } else {
              rResult = rResult / cos(rDHeadingRad);
        }
  }
  return rResult;
}

II Existe uma maneira mais fácil que fornece bons resultados.

Pela velocidade média.

Distância de viagem = Velocidade_ média da viagem * Tempo de viagem

Como a velocidade do GPS é detectada pelo efeito Doppler e não está diretamente relacionada a [Lon, Lat], ela pode pelo menos ser considerada secundária (backup ou correção), se não como o método de cálculo da distância principal.

Tod Samay
fonte
4

Se você estiver usando o .NET, não reivente a roda. Consulte System.Device.Location . Crédito para fnx nos comentários em outra resposta .

using System.Device.Location;

double lat1 = 45.421527862548828D;
double long1 = -75.697189331054688D;
double lat2 = 53.64135D;
double long2 = -113.59273D;

GeoCoordinate geo1 = new GeoCoordinate(lat1, long1);
GeoCoordinate geo2 = new GeoCoordinate(lat2, long2);

double distance = geo1.GetDistanceTo(geo2);
Tim Partridge
fonte
3

Este código Lua é adaptado de material encontrado na Wikipedia e na ferramenta GPSbabel de Robert Lipe :

local EARTH_RAD = 6378137.0 
  -- earth's radius in meters (official geoid datum, not 20,000km / pi)

local radmiles = EARTH_RAD*100.0/2.54/12.0/5280.0;
  -- earth's radius in miles

local multipliers = {
  radians = 1, miles = radmiles, mi = radmiles, feet = radmiles * 5280,
  meters = EARTH_RAD, m = EARTH_RAD, km = EARTH_RAD / 1000, 
  degrees = 360 / (2 * math.pi), min = 60 * 360 / (2 * math.pi)
}

function gcdist(pt1, pt2, units) -- return distance in radians or given units
  --- this formula works best for points close together or antipodal
  --- rounding error strikes when distance is one-quarter Earth's circumference
  --- (ref: wikipedia Great-circle distance)
  if not pt1.radians then pt1 = rad(pt1) end
  if not pt2.radians then pt2 = rad(pt2) end
  local sdlat = sin((pt1.lat - pt2.lat) / 2.0);
  local sdlon = sin((pt1.lon - pt2.lon) / 2.0);
  local res = sqrt(sdlat * sdlat + cos(pt1.lat) * cos(pt2.lat) * sdlon * sdlon);
  res = res > 1 and 1 or res < -1 and -1 or res
  res = 2 * asin(res);
  if units then return res * assert(multipliers[units])
  else return res
  end
end
Norman Ramsey
fonte
3
    private double deg2rad(double deg)
    {
        return (deg * Math.PI / 180.0);
    }

    private double rad2deg(double rad)
    {
        return (rad / Math.PI * 180.0);
    }

    private double GetDistance(double lat1, double lon1, double lat2, double lon2)
    {
        //code for Distance in Kilo Meter
        double theta = lon1 - lon2;
        double dist = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) + Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(deg2rad(theta));
        dist = Math.Abs(Math.Round(rad2deg(Math.Acos(dist)) * 60 * 1.1515 * 1.609344 * 1000, 0));
        return (dist);
    }

    private double GetDirection(double lat1, double lon1, double lat2, double lon2)
    {
        //code for Direction in Degrees
        double dlat = deg2rad(lat1) - deg2rad(lat2);
        double dlon = deg2rad(lon1) - deg2rad(lon2);
        double y = Math.Sin(dlon) * Math.Cos(lat2);
        double x = Math.Cos(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) - Math.Sin(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(dlon);
        double direct = Math.Round(rad2deg(Math.Atan2(y, x)), 0);
        if (direct < 0)
            direct = direct + 360;
        return (direct);
    }

    private double GetSpeed(double lat1, double lon1, double lat2, double lon2, DateTime CurTime, DateTime PrevTime)
    {
        //code for speed in Kilo Meter/Hour
        TimeSpan TimeDifference = CurTime.Subtract(PrevTime);
        double TimeDifferenceInSeconds = Math.Round(TimeDifference.TotalSeconds, 0);
        double theta = lon1 - lon2;
        double dist = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) + Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(deg2rad(theta));
        dist = rad2deg(Math.Acos(dist)) * 60 * 1.1515 * 1.609344;
        double Speed = Math.Abs(Math.Round((dist / Math.Abs(TimeDifferenceInSeconds)) * 60 * 60, 0));
        return (Speed);
    }

    private double GetDuration(DateTime CurTime, DateTime PrevTime)
    {
        //code for speed in Kilo Meter/Hour
        TimeSpan TimeDifference = CurTime.Subtract(PrevTime);
        double TimeDifferenceInSeconds = Math.Abs(Math.Round(TimeDifference.TotalSeconds, 0));
        return (TimeDifferenceInSeconds);
    }
Elanchezhian Babu P
fonte
11
Penso que a sua função de valor getDistance retornos em metros
Przemek
Isso está correto? GetDirection () não usa 'dlat'.
quer tocar
3

Esta é a versão de "Henry Vilinskiy" adaptada para MySQL e Quilômetros:

CREATE FUNCTION `CalculateDistanceInKm`(
  fromLatitude float,
  fromLongitude float,
  toLatitude float, 
  toLongitude float
) RETURNS float
BEGIN
  declare distance float;

  select 
    6367 * ACOS(
            round(
              COS(RADIANS(90-fromLatitude)) *
                COS(RADIANS(90-toLatitude)) +
                SIN(RADIANS(90-fromLatitude)) *
                SIN(RADIANS(90-toLatitude)) *
                COS(RADIANS(fromLongitude-toLongitude))
              ,15)
            )
    into distance;

  return  round(distance,3);
END;
Maxs
fonte
MySQLdisseSomething is wrong in your syntax near '' on line 8 // declare distance float;
Legionär
Esta é a "lei esférica dos cossenos" cálculo que é o menos preciso e mais método sujeito a erros de cálculo de uma grande distância círculo
John Machin
3

aqui está a implementação Swift da resposta

func degreesToRadians(degrees: Double) -> Double {
    return degrees * Double.pi / 180
}

func distanceInKmBetweenEarthCoordinates(lat1: Double, lon1: Double, lat2: Double, lon2: Double) -> Double {

    let earthRadiusKm: Double = 6371

    let dLat = degreesToRadians(degrees: lat2 - lat1)
    let dLon = degreesToRadians(degrees: lon2 - lon1)

    let lat1 = degreesToRadians(degrees: lat1)
    let lat2 = degreesToRadians(degrees: lat2)

    let a = sin(dLat/2) * sin(dLat/2) +
    sin(dLon/2) * sin(dLon/2) * cos(lat1) * cos(lat2)
    let c = 2 * atan2(sqrt(a), sqrt(1 - a))
    return earthRadiusKm * c
}
Sai Li
fonte
3

peguei a resposta principal e usei-a em um programa Scala

import java.lang.Math.{atan2, cos, sin, sqrt}

def latLonDistance(lat1: Double, lon1: Double)(lat2: Double, lon2: Double): Double = {
    val earthRadiusKm = 6371
    val dLat = (lat2 - lat1).toRadians
    val dLon = (lon2 - lon1).toRadians
    val latRad1 = lat1.toRadians
    val latRad2 = lat2.toRadians

    val a = sin(dLat / 2) * sin(dLat / 2) + sin(dLon / 2) * sin(dLon / 2) * cos(latRad1) * cos(latRad2)
    val c = 2 * atan2(sqrt(a), sqrt(1 - a))
    earthRadiusKm * c
}

usei a função para poder produzir facilmente funções que têm um dos dois locais fixos e requerem apenas um par de lat / lon para produzir distância.

Peter Perháč
fonte
2

Eu acho que você quer ao longo da curvatura da terra. Seus dois pontos e o centro da terra estão em um avião. O centro da terra é o centro de um círculo nesse plano e os dois pontos estão (aproximadamente) no perímetro desse círculo. A partir disso, você pode calcular a distância, descobrindo qual é o ângulo de um ponto para o outro.

Se os pontos não tiverem as mesmas alturas, ou se você precisar levar em consideração que a Terra não é uma esfera perfeita, fica um pouco mais difícil.

Guge
fonte
2

Recentemente, tive que fazer a mesma coisa. Achei este site muito útil para explicar trigonométricas esféricas com exemplos fáceis de seguir.

Twotymz
fonte
2

você pode encontrar uma implementação disso (com algumas boas explicações) em F # no fssnip

aqui estão as partes importantes:


let GreatCircleDistance<[<Measure>] 'u> (R : float<'u>) (p1 : Location) (p2 : Location) =
    let degToRad (x : float<deg>) = System.Math.PI * x / 180.0<deg/rad>

    let sq x = x * x
    // take the sin of the half and square the result
    let sinSqHf (a : float<rad>) = (System.Math.Sin >> sq) (a / 2.0<rad>)
    let cos (a : float<deg>) = System.Math.Cos (degToRad a / 1.0<rad>)

    let dLat = (p2.Latitude - p1.Latitude) |> degToRad
    let dLon = (p2.Longitude - p1.Longitude) |> degToRad

    let a = sinSqHf dLat + cos p1.Latitude * cos p2.Latitude * sinSqHf dLon
    let c = 2.0 * System.Math.Atan2(System.Math.Sqrt(a), System.Math.Sqrt(1.0-a))

    R * c
Carsten
fonte
2

Eu precisava implementar isso no PowerShell, espero que possa ajudar outra pessoa. Algumas notas sobre este método

  1. Não divida nenhuma das linhas ou o cálculo estará errado
  2. Para calcular em KM, remova o * 1000 no cálculo de $ distance
  3. Altere $ earthsRadius = 3963.19059 e remova * 1000 no cálculo de $ distance the para calcular a distância em milhas
  4. Estou usando o Haversine, pois outras postagens apontaram que as fórmulas do Vincenty são muito mais precisas

    Function MetresDistanceBetweenTwoGPSCoordinates($latitude1, $longitude1, $latitude2, $longitude2)  
    {  
      $Rad = ([math]::PI / 180);  
    
      $earthsRadius = 6378.1370 # Earth's Radius in KM  
      $dLat = ($latitude2 - $latitude1) * $Rad  
      $dLon = ($longitude2 - $longitude1) * $Rad  
      $latitude1 = $latitude1 * $Rad  
      $latitude2 = $latitude2 * $Rad  
    
      $a = [math]::Sin($dLat / 2) * [math]::Sin($dLat / 2) + [math]::Sin($dLon / 2) * [math]::Sin($dLon / 2) * [math]::Cos($latitude1) * [math]::Cos($latitude2)  
      $c = 2 * [math]::ATan2([math]::Sqrt($a), [math]::Sqrt(1-$a))  
    
      $distance = [math]::Round($earthsRadius * $c * 1000, 0) #Multiple by 1000 to get metres  
    
      Return $distance  
    }
    
TheLukeMcCarthy
fonte
2

Versão Scala

  def deg2rad(deg: Double) = deg * Math.PI / 180.0

  def rad2deg(rad: Double) = rad / Math.PI * 180.0

  def getDistanceMeters(lat1: Double, lon1: Double, lat2: Double, lon2: Double) = {
    val theta = lon1 - lon2
    val dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) *
      Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta))
    Math.abs(
      Math.round(
        rad2deg(Math.acos(dist)) * 60 * 1.1515 * 1.609344 * 1000)
    )
  }
Przemek
fonte
1

// Talvez um erro de digitação?
Temos uma variável não utilizada dlon em GetDirection,
suponho

double y = Math.Sin(dlon) * Math.Cos(lat2);
// cannot use degrees in Cos ?

deveria estar

double y = Math.Sin(dlon) * Math.Cos(dlat);
Gerrie de Jager
fonte
11
Esta não é uma resposta, é, na melhor das hipóteses, um comentário.
Kevin
1

Aqui está minha implementação no Elixir

defmodule Geo do
  @earth_radius_km 6371
  @earth_radius_sm 3958.748
  @earth_radius_nm 3440.065
  @feet_per_sm 5280

  @d2r :math.pi / 180

  def deg_to_rad(deg), do: deg * @d2r

  def great_circle_distance(p1, p2, :km), do: haversine(p1, p2) * @earth_radius_km
  def great_circle_distance(p1, p2, :sm), do: haversine(p1, p2) * @earth_radius_sm
  def great_circle_distance(p1, p2, :nm), do: haversine(p1, p2) * @earth_radius_nm
  def great_circle_distance(p1, p2, :m), do: great_circle_distance(p1, p2, :km) * 1000
  def great_circle_distance(p1, p2, :ft), do: great_circle_distance(p1, p2, :sm) * @feet_per_sm

  @doc """
  Calculate the [Haversine](https://en.wikipedia.org/wiki/Haversine_formula)
  distance between two coordinates. Result is in radians. This result can be
  multiplied by the sphere's radius in any unit to get the distance in that unit.
  For example, multiple the result of this function by the Earth's radius in
  kilometres and you get the distance between the two given points in kilometres.
  """
  def haversine({lat1, lon1}, {lat2, lon2}) do
    dlat = deg_to_rad(lat2 - lat1)
    dlon = deg_to_rad(lon2 - lon1)

    radlat1 = deg_to_rad(lat1)
    radlat2 = deg_to_rad(lat2)

    a = :math.pow(:math.sin(dlat / 2), 2) +
        :math.pow(:math.sin(dlon / 2), 2) *
        :math.cos(radlat1) * :math.cos(radlat2)

    2 * :math.atan2(:math.sqrt(a), :math.sqrt(1 - a))
  end
end
barata
fonte
1

Versão dardo

Algoritmo Haversine.

import 'dart:math';

class GeoUtils {

  static double _degreesToRadians(degrees) {
    return degrees * pi / 180;
  }

  static double distanceInKmBetweenEarthCoordinates(lat1, lon1, lat2, lon2) {
    var earthRadiusKm = 6371;

    var dLat = _degreesToRadians(lat2-lat1);
    var dLon = _degreesToRadians(lon2-lon1);

    lat1 = _degreesToRadians(lat1);
    lat2 = _degreesToRadians(lat2);

    var a = sin(dLat/2) * sin(dLat/2) +
        sin(dLon/2) * sin(dLon/2) * cos(lat1) * cos(lat2);
    var c = 2 * atan2(sqrt(a), sqrt(1-a));
    return earthRadiusKm * c;
  }
}
abd3llatif
fonte
0

Eu acho que uma versão do algoritmo em R ainda está faltando:

gpsdistance<-function(lat1,lon1,lat2,lon2){

# internal function to change deg to rad

degreesToRadians<- function (degrees) {
return (degrees * pi / 180)
}

R<-6371e3  #radius of Earth in meters

phi1<-degreesToRadians(lat1) # latitude 1
phi2<-degreesToRadians(lat2) # latitude 2
lambda1<-degreesToRadians(lon1) # longitude 1
lambda2<-degreesToRadians(lon2) # longitude 2

delta_phi<-phi1-phi2 # latitude-distance
delta_lambda<-lambda1-lambda2 # longitude-distance

a<-sin(delta_phi/2)*sin(delta_phi/2)+
cos(phi1)*cos(phi2)*sin(delta_lambda/2)*
sin(delta_lambda/2)

cc<-2*atan2(sqrt(a),sqrt(1-a))

distance<- R * cc

return(distance)  # in meters
}
shghm
fonte
0

Aqui está uma variação do Kotlin:

import kotlin.math.*

class HaversineAlgorithm {

    companion object {
        private const val MEAN_EARTH_RADIUS = 6371.0
        private const val D2R = Math.PI / 180.0
    }

    private fun haversineInKm(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
        val lonDiff = (lon2 - lon1) * D2R
        val latDiff = (lat2 - lat1) * D2R
        val latSin = sin(latDiff / 2.0)
        val lonSin = sin(lonDiff / 2.0)
        val a = latSin * latSin + (cos(lat1 * D2R) * cos(lat2 * D2R) * lonSin * lonSin)
        val c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a))
        return EQATORIAL_EARTH_RADIUS * c
    }
}
Csaba Toth
fonte
Por que você usou o raio equatorial em vez do raio médio da Terra?
user13044086
@ user13044086 Boa pergunta. É porque eu tirei isso da versão Java de Paulo Miguel Almeida. Parece que a versão C # também está usando essa distância. Outras versões aqui têm o 6371, mas é preciso perceber que todos esses algoritmos podem não lidar perfeitamente com a forma geóide da Terra. Sinta-se à vontade para modificar isso e usar o 6371. Se você me disser que isso leva a valores mais precisos, mudarei minha resposta.
Csaba Toth
11
6371.008 é comumente usado porque minimiza o erro relativo da fórmula, conforme explicado nas notas na página movable-type.co.uk/scripts/latlong.html#ellipsoid
user13044086
Justo! Vou editar minha resposta amanhã
Csaba Toth
@ user13044086 Obrigado pelo link, editei minha resposta há um tempo atrás com base nisso
Csaba Toth