¿Calculando la distancia entre dos puntos, usando latitud y longitud?

94

Aquí está mi intento, es solo un fragmento de mi código:

final double RADIUS = 6371.01;
double temp = Math.cos(Math.toRadians(latA))
            * Math.cos(Math.toRadians(latB))
            * Math.cos(Math.toRadians((latB) - (latA)))
            + Math.sin(Math.toRadians(latA))
            * Math.sin(Math.toRadians(latB));
    return temp * RADIUS * Math.PI / 180;

Estoy usando estas fórmulas para obtener la latitud y la longitud:

x = Deg + (Min + Sec / 60) / 60)
m4design
fuente

Respuestas:

217

El código Java proporcionado por Dommer anteriormente da resultados ligeramente incorrectos, pero los pequeños errores se acumulan si está procesando, digamos, una pista de GPS. Aquí hay una implementación del método Haversine en Java que también tiene en cuenta las diferencias de altura entre dos puntos.

/**
 * Calculate distance between two points in latitude and longitude taking
 * into account height difference. If you are not interested in height
 * difference pass 0.0. Uses Haversine method as its base.
 * 
 * lat1, lon1 Start point lat2, lon2 End point el1 Start altitude in meters
 * el2 End altitude in meters
 * @returns Distance in Meters
 */
public static double distance(double lat1, double lat2, double lon1,
        double lon2, double el1, double el2) {

    final int R = 6371; // Radius of the earth

    double latDistance = Math.toRadians(lat2 - lat1);
    double lonDistance = Math.toRadians(lon2 - lon1);
    double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
            + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
            * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    double distance = R * c * 1000; // convert to meters

    double height = el1 - el2;

    distance = Math.pow(distance, 2) + Math.pow(height, 2);

    return Math.sqrt(distance);
}
David George
fuente
7
¿Por qué no Math.toRadians () en lugar de deg2rad ()? Sería realmente autosuficiente.
Aron Lorincz
2
@Bala - Mi mal, está en el comentario sobre el código en mi computadora pero falta aquí. Distancia en metros.
David George
4
@ ÁronNemmondommegavezetéknevem He actualizado el método para usar tu muy buena sugerencia.
David George
2
Algunos comentarios aquí: stackoverflow.com/questions/28510115/…
David George
¿Alguna relevancia particular para los nombres de variables ay c? Son estos los lados de un triángulo rectángulo con las etiquetas tradicionales a, by c?
AlainD
76

Aquí hay una función de Java que calcula la distancia entre dos puntos lat / long , publicada a continuación, en caso de que vuelva a desaparecer.

    private double distance(double lat1, double lon1, double lat2, double lon2, char unit) {
      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.acos(dist);
      dist = rad2deg(dist);
      dist = dist * 60 * 1.1515;
      if (unit == 'K') {
        dist = dist * 1.609344;
      } else if (unit == 'N') {
        dist = dist * 0.8684;
        }
      return (dist);
    }
    
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    /*::  This function converts decimal degrees to radians             :*/
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    private double deg2rad(double deg) {
      return (deg * Math.PI / 180.0);
    }
    
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    /*::  This function converts radians to decimal degrees             :*/
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    private double rad2deg(double rad) {
      return (rad * 180.0 / Math.PI);
    }
    
    System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'M') + " Miles\n");
    System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'K') + " Kilometers\n");
    System.out.println(distance(32.9697, -96.80322, 29.46786, -98.53506, 'N') + " Nautical Miles\n");
dommer
fuente
1
Google Map muestra 200 Kms para 12.915700, 77.632046, 11.665154, 78.145657 donde, como el código anterior muestra 149.82 Kms. Algo sigue siendo incorrecto.
Samy
1
@Samy la función anterior le da una distancia en línea recta.
Rahul_Pawar
1
Mi reino para que los desarrolladores etiqueten sus variables con las Unidades . double dist => double distInMiles (o similar) (no estoy golpeando a la persona que publicó esta respuesta, estoy votando a favor ....... sino al implementador original del código)
granadaCoder
13

Nota: esta solución solo funciona para distancias cortas.

Traté de usar la fórmula publicada de dommer para una aplicación y descubrí que funcionaba bien para largas distancias, pero en mis datos estaba usando todas las distancias muy cortas y la publicación de dommer lo hizo muy mal. Necesitaba velocidad y los cálculos geográficos más complejos funcionaban bien, pero eran demasiado lentos. Entonces, en el caso de que necesite velocidad y todos los cálculos que está haciendo son cortos (tal vez <100 m aproximadamente). Encontré que esta pequeña aproximación funciona muy bien. asume que el mundo es plano, así que no lo use para largas distancias, funciona aproximando la distancia de una sola latitud y longitud en la latitud dada y devolviendo la distancia pitagórica en metros.

public class FlatEarthDist {
    //returns distance in meters
    public static double distance(double lat1, double lng1, 
                                      double lat2, double lng2){
     double a = (lat1-lat2)*FlatEarthDist.distPerLat(lat1);
     double b = (lng1-lng2)*FlatEarthDist.distPerLng(lat1);
     return Math.sqrt(a*a+b*b);
    }

    private static double distPerLng(double lat){
      return 0.0003121092*Math.pow(lat, 4)
             +0.0101182384*Math.pow(lat, 3)
                 -17.2385140059*lat*lat
             +5.5485277537*lat+111301.967182595;
    }

    private static double distPerLat(double lat){
            return -0.000000487305676*Math.pow(lat, 4)
                -0.0033668574*Math.pow(lat, 3)
                +0.4601181791*lat*lat
                -1.4558127346*lat+110579.25662316;
    }
}
zahmde
fuente
13

Futuros lectores que se topan con este artículo de SOF.

Obviamente, la pregunta se hizo en 2010 y ahora es 2019. Pero surge al principio de una búsqueda en Internet. La pregunta original no descarta el uso de una biblioteca de terceros (cuando escribí esta respuesta).

public double calculateDistanceInMeters(double lat1, double long1, double lat2,
                                     double long2) {


    double dist = org.apache.lucene.util.SloppyMath.haversinMeters(lat1, long1, lat2, long2);
    return dist;
}

y

<dependency>
  <groupId>org.apache.lucene</groupId>
  <artifactId>lucene-spatial</artifactId>
  <version>8.2.0</version>
</dependency>

https://mvnrepository.com/artifact/org.apache.lucene/lucene-spatial/8.2.0

¡Lea la documentación sobre "SloppyMath" antes de sumergirse!

https://lucene.apache.org/core/8_2_0/core/org/apache/lucene/util/SloppyMath.html

granadaCoder
fuente
5

Aquí hay una página con ejemplos de JavaScript para varios cálculos esféricos. El primero de la página debería darte lo que necesitas.

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

Aquí está el código Javascript

var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad(); 
var a = Math.sin(dLat/2) * Math.sin(dLat/2) + 
        Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
        Math.sin(dLon/2) * Math.sin(dLon/2); 
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
var d = R * c;

Donde 'd' mantendrá la distancia.

Chris Taylor
fuente
¿Puede "a" ser negativo alguna vez?
Xi Wei
1
package distanceAlgorithm;

public class CalDistance {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
    CalDistance obj=new CalDistance();
    /*obj.distance(38.898556, -77.037852, 38.897147, -77.043934);*/
        System.out.println(obj.distance(38.898556, -77.037852, 38.897147, -77.043934, "M") + " Miles\n");
        System.out.println(obj.distance(38.898556, -77.037852, 38.897147, -77.043934, "K") + " Kilometers\n");
        System.out.println(obj.distance(32.9697, -96.80322, 29.46786, -98.53506, "N") + " Nautical Miles\n");       
    }   
    public double distance(double lat1, double lon1, double lat2, double lon2, String sr) {


          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.acos(dist);
          dist = rad2deg(dist);
          dist = dist * 60 * 1.1515;
          if (sr.equals("K")) {
            dist = dist * 1.609344;
          } else if (sr.equals("N")) {
            dist = dist * 0.8684;
            }
          return (dist);
        }
    public double deg2rad(double deg) {
          return (deg * Math.PI / 180.0);
        }
    public double rad2deg(double rad) {
          return (rad * 180.0 / Math.PI);
        }


    }
MAnoj Sarnaik
fuente
1

Se proporcionaron muchas respuestas excelentes, sin embargo, encontré algunas deficiencias de rendimiento, así que permítanme ofrecer una versión con el rendimiento en mente. Cada constante está precalculada y se introducen variables x, y para evitar calcular el mismo valor dos veces. Espero eso ayude

    private static final double r2d = 180.0D / 3.141592653589793D;
    private static final double d2r = 3.141592653589793D / 180.0D;
    private static final double d2km = 111189.57696D * r2d;
    public static double meters(double lt1, double ln1, double lt2, double ln2) {
        final double x = lt1 * d2r;
        final double y = lt2 * d2r;
        return Math.acos( Math.sin(x) * Math.sin(y) + Math.cos(x) * Math.cos(y) * Math.cos(d2r * (ln1 - ln2))) * d2km;
    }
Stan Sokolov
fuente
0

Respuesta ligeramente mejorada de @David George:

public static double distance(double lat1, double lat2, double lon1,
                              double lon2, double el1, double el2) {

    final int R = 6371; // Radius of the earth

    double latDistance = Math.toRadians(lat2 - lat1);
    double lonDistance = Math.toRadians(lon2 - lon1);
    double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
            + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
            * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    double distance = R * c * 1000; // convert to meters

    double height = el1 - el2;

    distance = Math.pow(distance, 2) + Math.pow(height, 2);

    return Math.sqrt(distance);
}

public static double distanceBetweenLocations(Location l1, Location l2) {
    if(l1.hasAltitude() && l2.hasAltitude()) {
        return distance(l1.getLatitude(), l2.getLatitude(), l1.getLongitude(), l2.getLongitude(), l1.getAltitude(), l2.getAltitude());
    }
    return l1.distanceTo(l2);
}

La función de distancia es la misma, pero he creado una función de envoltura pequeña, que toma 2 objetos de ubicación . Gracias a esto, solo uso la función de distancia si ambas ubicaciones tienen altitud, porque a veces no la tienen. Y puede conducir a resultados extraños (si la ubicación no conoce su altitud 0, se devolverá). En este caso, recurro a la función de distancia clásica .

Makalele
fuente
-1

Este artículo de wikipedia proporciona las fórmulas y un ejemplo. El texto está en alemán, pero los cálculos hablan por sí mismos.

zellus
fuente