Calcular la distancia entre dos puntos de latitud y longitud? (Fórmula de Haversine)

908

¿Cómo calculo la distancia entre dos puntos especificados por latitud y longitud?

Para aclarar, me gustaría la distancia en kilómetros; los puntos usan el sistema WGS84 y me gustaría entender las precisiones relativas de los enfoques disponibles.

Robin Minto
fuente
Para una mejor precisión, consulte stackoverflow.com/questions/1420045/…
Lior Kogan
3
Tenga en cuenta que no puede aplicar una fórmula de Haversine en un elipsoide de revolución como WGS 84. Solo puede aplicar este método en una esfera con un radio.
Mike T
3
La mayoría de las respuestas aquí están usando trigonometría esférica simple, por lo que los resultados son bastante crudos en comparación con las distancias elipsoides WGS84 utilizadas en el sistema GPS. Algunas de las respuestas se refieren a la fórmula de Vincenty para elipsoides, pero ese algoritmo fue diseñado para usarse en las calculadoras de escritorio de la era de la década de 1960 y tiene problemas de estabilidad y precisión; Tenemos un mejor hardware y software ahora. Consulte GeographicLib para obtener una biblioteca de alta calidad con implementaciones en varios idiomas.
PM 2Ring
@MikeT: cierto, aunque muchas de las respuestas aquí parecen útiles a distancias cortas : si toma lat / long de WGS 84 y aplica Haversine como si fueran puntos en una esfera, ¿no obtiene respuestas cuyos errores solo se deban a ¿El factor de aplanamiento de la tierra, entonces quizás dentro del 1% de una fórmula más precisa? Con la advertencia de que estas son distancias pequeñas, digamos dentro de una sola ciudad.
ToolmakerSteve
1
Para estas plataformas: Mono / .NET 4.5 / .NET Core / Windows Phone 8.x / Plataforma universal de Windows / Xamarin iOS / Xamarin Android, consulte stackoverflow.com/a/54296314/2736742
A. Morel

Respuestas:

1149

Este enlace puede serle útil, ya que detalla el uso de la fórmula de Haversine para calcular la distancia.

Extracto:

Este script [en Javascript] calcula las distancias de gran círculo entre los dos puntos, es decir, la distancia más corta sobre la superficie de la tierra, utilizando la 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)
}
Arrojar
fuente
51
¿Este cálculo / método explica que la Tierra sea un esferoide (no una esfera perfecta)? La pregunta original pedía la distancia entre puntos en un globo WGS84. No estoy seguro de cuánto error se produce al usar una esfera perfecta, pero sospecho que puede ser bastante dependiendo de dónde estén los puntos en el globo, por lo que vale la pena tener en cuenta la distinción.
Redcalx
15
La fórmula de Haversine no tiene en cuenta que la Tierra es un esferoide, por lo que recibirá algún error debido a ese hecho. No se puede garantizar que sea correcto por encima del 0,5%. Sin embargo, eso puede o no ser un nivel aceptable de error.
Brandon
24
¿Hay alguna razón para usar en Math.atan2(Math.sqrt(a), Math.sqrt(1-a))lugar de Math.asin(Math.sqrt(h)), cuál sería la implementación directa de la fórmula que usa el artículo de Wikipedia? ¿Es más eficiente y / o más estable numéricamente?
musiphil
16
@UsmanMutawakil Bueno, las 38 millas que obtienes es la distancia en la carretera. Este algoritmo calcula una distancia en línea recta sobre la superficie de la tierra. Google Maps tiene una herramienta de distancia (abajo a la izquierda, "Labs") que hace lo mismo, úsala para comparar.
Pascal
44
@ Forte_201092: Porque eso no es necesario - como (sin(x))²es igual(sin(-x))²
Jean Hominal
360

Necesitaba calcular muchas distancias entre los puntos para mi proyecto, así que seguí adelante e intenté optimizar el código que encontré aquí. En promedio, en diferentes navegadores, mi nueva implementación se ejecuta 2 veces más rápido que la respuesta más 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
}

Puedes jugar con mi jsPerf y ver los resultados aquí .

Recientemente necesitaba hacer lo mismo en Python, así que aquí hay una implementación de 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...

Y en aras de la integridad: Haversine en wiki.

Salvador Dalí
fuente
13
@AngularM y es muy probable que Google calcule la distancia si va a tomar algunas carreteras y no una línea recta.
Salvador Dali
3
Google calcula la distancia de conducción, esto calcula "mientras el cuervo vuela"
Aficionado
44
@Ouadie y ¿mejorará la velocidad? Lo más probable es que no, pero terminaré con un montón de 'tus cosas no funcionan' para las personas que las copian en viejos navegadores
Salvador Dali
44
bueno si pero que // 2 * R; R = 6371 kmsignifica? y el método actual proporciona respuesta en km o millas? Necesita mejor documentación. Gracias
Khalil Khalaf
20
@KhalilKhalaf ¿Estás bromeando o tratando de troll aquí? km significa kilómetros. ¿Qué crees que representa R (especialmente si hablamos de un shpere)? Adivina en qué unidades será la respuesta si ya ves el km. ¿Qué tipo de documentación está buscando aquí? Hay literalmente 4 líneas allí.
Salvador Dali
69

Aquí hay una implementación 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
fuente
14
Está utilizando el radio ecuatorial, pero debería estar utilizando el radio medio, que es 6371 km
Philippe Leybaert
77
No debe ser esto double dlon = Radians(lon2 - lon1);ydouble dlat = Radians(lat2 - lat1);
Chris Marisic
Estoy de acuerdo con Chris Marisic. Usé el código original y los cálculos estaban equivocados. Agregué la llamada para convertir los deltas a radianes y ahora funciona correctamente. Envié una edición y estoy esperando que sea revisada por pares.
Bryan Bedard
Envié otra edición porque lat1 y lat2 también deben convertirse a radianes. También revisé la fórmula de la asignación a a para que coincida con la fórmula y el código que se encuentran aquí: movable-type.co.uk/scripts/latlong.html
Bryan Bedard
¿El RADIUSvalor debe ser 6371 como en las otras respuestas?
Chris Hayes
66

Aquí hay una implementación de Java de la 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));
}

Tenga en cuenta que aquí estamos redondeando la respuesta al km más cercano.

Whostolebenfrog
fuente
2
Si quisiéramos calcular la distancia entre dos puntos en metros, ¿cuál sería la forma más precisa? Para usar 6371000como el radio de la tierra? (radio promedio de la tierra es 6371000 metros) o convertir kilómetros a metros de su función?
Micro
si quiere millas, multiplique el resultado por0.621371
lasec0203
42

Muchas gracias por todo esto. Usé el siguiente código en mi aplicación de iPhone Objective-C:

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

La latitud y la longitud están en decimal. No usé min () para la llamada asin () ya que las distancias que estoy usando son tan pequeñas que no lo requieren.

Dio respuestas incorrectas hasta que pasé los valores en radianes; ahora es más o menos lo mismo que los valores obtenidos de la aplicación de mapas de Apple :-)

Actualización extra:

Si está utilizando iOS4 o posterior, Apple proporciona algunos métodos para hacerlo para que se logre la misma funcionalidad con:

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

    MKMapPoint  start, finish;


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

    return MKMetersBetweenMapPoints(start, finish) / 1000;
}
Stephen Watson
fuente
1
iOS SDK tiene su propia implementación: developer.apple.com/library/ios/documentation/CoreLocation/… :
tuler
Creo que el paréntesis pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))es incorrecto. Elimínelos y el resultado coincide con lo que obtengo cuando uso otras implementaciones en esta página, o implemento la fórmula Haversine de Wikipedia desde cero.
zanedp
Usando las coordenadas (40.7127837, -74.0059413) para Nueva York y (34.052234, -118.243685) para LA, con ()esa suma, obtengo 3869.75. Sin ellos, obtengo 3935.75, que es más o menos lo que aparece en una búsqueda web.
zanedp
40

Esta es una función PHP simple que dará una aproximación muy razonable (bajo un margen de error 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 se dijo antes; La tierra NO es una esfera. Es como un viejo, viejo béisbol con el que Mark McGwire decidió practicar: está lleno de abolladuras y golpes. Los cálculos más simples (como este) lo tratan como una esfera.

Los diferentes métodos pueden ser más o menos precisos según dónde se encuentre en este ovoide irregular Y cuán separados estén sus puntos (cuanto más cerca estén, menor será el margen de error absoluto). Cuanto más precisas sean sus expectativas, más complejas serán las matemáticas.

Para más información: distancia geográfica de wikipedia

Tony Gil
fuente
44
Esto funciona perfectamente! Acabo de agregar $ distance_miles = $ km * 0.621371; ¡Y eso es todo lo que necesitaba para una distancia aproximada en millas! Gracias Tony
31

Publico aquí mi ejemplo de trabajo.

Enumere todos los puntos en la tabla que tienen distancia entre un punto designado (usamos un punto aleatorio - lat: 45.20327, largo: 23.7806) menos de 50 KM, con latitud y longitud, en MySQL (los campos de la tabla son coord_lat y coord_long):

Enumere todos los que tengan DISTANCIA <50, en kilómetros (considerado radio de la Tierra 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

El ejemplo anterior se probó en MySQL 5.0.95 y 5.5.16 (Linux).

conualfy
fuente
Creo que un buen enfoque podría ser prefiltrar los resultados usando una aproximación, por lo que la fórmula pesada se aplica solo en algunos casos. Especialmente útil si tienes otras condiciones. Estoy usando esto para el aprox inicial: stackoverflow.com/questions/1253499/…
Pato
28

En las otras respuestas, una implementación en Está perdido.

Calcular la distancia entre dos puntos es bastante sencillo con la distmfunción del geospherepaquete:

distm(p1, p2, fun = distHaversine)

dónde:

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

Como la tierra no es perfectamente esférica, la fórmula de Vincenty para elipsoides es probablemente la mejor manera de calcular distancias. Por lo tanto, en el geospherepaquete que usa, entonces:

distm(p1, p2, fun = distVincentyEllipsoid)

Por supuesto, no necesariamente tiene que usar el geospherepaquete, también puede calcular la distancia en base Rcon una función:

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
fuente
Para asegurarme de que tengo claro lo que dijiste: El código que das al final de la publicación: ¿Es una implementación de la fórmula de Vincenty? Por lo que sabes, ¿debería dar la misma respuesta que llamar a Vincenty en la geosfera? [No tengo geosfera u otra biblioteca; solo buscando algún código para incluir en una aplicación multiplataforma. Por supuesto, verificaría algunos casos de prueba con una calculadora buena conocida.]
ToolmakerSteve
1
@ToolmakerSteve la función al final de mi respuesta es una implementación del método Haversine
Jaap
Hola @Jaap, ¿podría preguntar cuál es la unidad de medida de la fórmula? ¿Está en metros?
Jackson
11

La haversina es definitivamente una buena fórmula para la mayoría de los casos, otras respuestas ya la incluyen, así que no voy a tomar el espacio. Pero es importante tener en cuenta que no importa qué fórmula se use (sí, no solo una). Debido al amplio rango de precisión posible, así como al tiempo de cálculo requerido. La elección de la fórmula requiere un poco más de reflexión que una simple respuesta sin complicaciones.

Esta publicación de una persona de la NASA es la mejor que encontré al analizar las opciones.

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

Por ejemplo, si solo está ordenando filas por distancia en un radio de 100 millas. La fórmula de tierra plana será mucho más rápida que la haversina.

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 solo hay un coseno y una raíz cuadrada. Vs 9 de ellos en la fórmula Haversine.

Arturo Hernández
fuente
Es una buena posibilidad. Solo tenga en cuenta que la distancia máxima recomendada en la discusión es de 12 millas, no 100 , y que aun así, los errores pueden arrastrarse hasta 30 metros (100 pies), dependiendo de la posición del globo.
Eric Wu
7

Puede usar la compilación en CLLocationDistance para calcular esto:

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

En tu caso, si quieres kilómetros, simplemente divide entre 1000.

Andre Cytryn
fuente
7

No me gusta agregar otra respuesta, pero la API de Google Maps v.3 tiene geometría esférica (y más). Después de convertir su WGS84 a grados decimales, puede hacer esto:

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

No se sabe cuán precisos son los cálculos de Google o incluso qué modelo se usa (aunque sí dice "esférico" en lugar de "geoide". Por cierto, la distancia de "línea recta" obviamente será diferente de la distancia si uno viaja en el superficie de la tierra que es lo que todos parecen estar presumiendo.

Steven Christenson
fuente
La distancia es en metros. alternativamente, uno puede usar computeLength ()
electrobabe
7

La implicación de Python Origin es el centro de los Estados Unidos contiguos.

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

Para obtener la respuesta en kilómetros, simplemente establezca millas = falso.

invoketheshell
fuente
1
Está importando un paquete no estándar que hace todo el trabajo. No sé si eso es tan útil.
Teepeemm
El paquete está en PyPI, Python Package Index, como un paquete de Python 3 junto con numpy y scikit-learn. No estoy seguro de por qué uno se opone a los paquetes. Tienden a ser bastante útiles. Como código abierto, uno también podría examinar los métodos contenidos. Creo que muchos encontrarán útil este paquete, así que dejaré la publicación a pesar del voto negativo. Salud. :)
invoketheshell
7

Podría haber una solución más simple y más correcta: el perímetro de la tierra es de 40,000 km en el ecuador, alrededor de 37,000 en el ciclo de Greenwich (o cualquier longitud). Así:

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

Estoy de acuerdo en que debe ajustarse, ya que yo mismo dije que es un elipsoide, por lo que el radio que se multiplicará por el coseno varía. Pero es un poco más preciso. En comparación con Google Maps, redujo significativamente el error.

Meymann
fuente
¿Es esta función distancia de retorno en km?
Wikki
Lo es, solo porque el ecuador y los ciclos de longitud están en km. Para millas, solo divida 40000 y 37000 por 1.6. Sintiéndose geek, puede convertirlo a Ris, multiplyung por aproximadamente 7 o parasang, dividiendo por 2.2 ;-)
Meymann
Esta parece ser la mejor respuesta ofrecida aquí. Deseo usarlo pero solo me pregunto si hay una manera de verificar la exactitud de este algoritmo. Probé f (50,5,58,3). Da 832 km, mientras que movable-type.co.uk/scripts/latlong.html que usa la fórmula 'haversine' da 899 km. ¿Hay una gran diferencia?
Chong Lip Phang
Además, creo que el valor devuelto por el código anterior está en m, y no en km.
Chong Lip Phang
@ChongLipPhang - PRECAUCIÓN: el teorema de Pitágoras es solo una aproximación razonable para áreas pequeñas , ya que este teorema supone que la tierra es plana. Como caso extremo, comience en el ecuador y avance 90 grados al este y 90 grados al norte. El resultado final, por supuesto, es el polo norte, y es lo mismo que moverse 0 grados al este y 90 grados al norte; por lo tanto, hacer sqrt (sqr (dx) + sqr (dy)) estará descabellado en el primer caso. ~ sqrt (10km sqr + 10km sqr) ~ = 14.4 km vs distancia correcta ~ 10km.
ToolmakerSteve
7

Todas las respuestas anteriores asumen que la tierra es una esfera. Sin embargo, una aproximación más precisa sería la de un esferoide achatado.

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

Aquí está la implementación de SQL para calcular la distancia en 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 obtener más detalles sobre la implementación mediante la programación de langugage, puede pasar por el script php que se proporciona aquí.

Kiran Maniya
fuente
5

Aquí hay una implementación mecanografiada de la fórmula de 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
fuente
5

Como se señaló, un cálculo preciso debe tener en cuenta que la tierra no es una esfera perfecta. Aquí hay algunas comparaciones de los diversos algoritmos que se ofrecen aquí:

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

En distancias pequeñas, el algoritmo de Keerthana parece coincidir con el de Google Maps. Google Maps no parece seguir ningún algoritmo simple, lo que sugiere que puede ser el método más preciso aquí.

De todos modos, aquí hay una implementación de Javascript del 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
fuente
4

Este script [en PHP] calcula las distancias entre los dos puntos.

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

Implementación de Java según la fórmula de 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
fuente
3

Para calcular la distancia entre dos puntos en una esfera, debes hacer el cálculo del Gran Círculo .

Hay una serie de bibliotecas C / C ++ para ayudar con la proyección de mapas en MapTools si necesita reproyectar sus distancias a una superficie plana. Para hacer esto, necesitará la cadena de proyección de los diversos sistemas de coordenadas.

También puede encontrar MapWindow una herramienta útil para visualizar los puntos. Además, como su código abierto es una guía útil sobre cómo usar la biblioteca proj.dll, que parece ser la biblioteca principal de proyección de código abierto.


fuente
3

Aquí está la implementación de respuesta aceptada portada a Java en caso de que alguien la necesite.

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

Aquí está la implementación VB.NET, esta implementación le dará el resultado en KM o Miles en función de un valor Enum que pase.

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
fuente
Al calcular "a", ¿escribió Math.Sin ( dLat ..) dos veces por error?
Marco Ottina
2

Condensé el cálculo simplificando la fórmula.

Aquí está en 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
fuente
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;
}}

La solución de Chuck, válida para millas también.

MPaulo
fuente
2

Aquí está mi implementación de Java para calcular la distancia a través de grados decimales después de alguna búsqueda. Utilicé el radio medio del mundo (de wikipedia) en km. Si desea millas de resultado, utilice el radio mundial en millas.

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

fuente
2

En Mysql, use la siguiente función para pasar los parámetros como 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
fuente
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}
));
Rafael C
fuente
2

Aquí hay un ejemplo en Postgres sql (en km, para la versión en millas, reemplace 1.609344 por la versión 0.8684)

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

Aquí hay otro convertido al código 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
fuente
1

Hay un buen ejemplo aquí para calcular la distancia con 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
fuente