2008-09-23 16 views
74

Estoy queriendo encontrar la distancia entre dos puntos diferentes. Esto sé que se puede lograr con la gran distancia circular. http://www.meridianworlddata.com/Distance-calculation.asp¿Cómo puedo medir la distancia y crear un cuadro delimitador basado en dos puntos de latitud + longitud en Java?

Una vez hecho, con un punto y una distancia me gustaría encontrar el punto que distancia al norte, y esa distancia hacia el este para crear una caja alrededor del punto.

+1

se refieren a este blog http://xebee.xebia.in/2010/10/28/working-with-geolocations/ – Robin

Respuesta

20

Hemos tenido cierto éxito al usar OpenMap para graficar una gran cantidad de datos posicionales. Hay una clase LatLonPoint que tiene algunas funciones básicas, incluida la distancia.

+5

Advertencia a los posibles adoptantes: Me acabo de encontrar con un problema grande con OpenMap; usan flotadores internamente para decimales lat/lon, lo que limita la precisión dependiendo de cuán cerca del ecuador se encuentre. Planean admitir la opción de dobles con sus clases OMGraphic a partir de la versión 4.7, pero la estabilidad actual es solo 4.6.5 (a partir de marzo de 2010). Fuente: http://openmap.bbn.com/mailArchives/openmap-users/2006-01/4522.html – Marc

+0

También tenga en cuenta que el Contrato de licencia de software actual de OpenMap http://openmap.bbn.com/license.html tiene ha sido considerado no libre. http://web.archiveorange.com/archive/v/XyE55YoXwS3lME936I0U Se han producido algunas conversaciones con BBN para cambiar las licencias, pero todavía no ha sucedido nada. –

+0

El enlace de LatLonPoint está roto – Petriborg

7

Aparecerá una búsqueda rápida en Google GeoTools, que probablemente tenga el tipo de funciones que está buscando.

140

Aquí hay una implementación de Java de la fórmula Haversine. Lo uso en un proyecto para calcular la distancia en millas entre lat/longs.

public static double distFrom(double lat1, double lng1, double lat2, double lng2) { 
    double earthRadius = 3958.75; // miles (or 6371.0 kilometers) 
    double dLat = Math.toRadians(lat2-lat1); 
    double dLng = Math.toRadians(lng2-lng1); 
    double sindLat = Math.sin(dLat/2); 
    double sindLng = Math.sin(dLng/2); 
    double a = Math.pow(sindLat, 2) + Math.pow(sindLng, 2) 
      * Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)); 
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
    double dist = earthRadius * c; 

    return dist; 
    } 
+22

Solo una nota al respecto, devolverá la distancia en millas (debido a la configuración earthRadius). Para otras unidades, cambie el earthRadius (vea http://en.wikipedia.org/wiki/Earth_radius para obtener más información) –

+0

¿Hay alguna razón por la que esté usando flotadores en vez de dobles? Si lo entiendo correctamente, puede aumentar la precisión de sus resultados simplemente cambiando sus parámetros de entrada tipo – Hamy

+0

. Tampoco es necesario calcular el seno de la mitad de los deltas dos veces. Calcule una vez y multiplíquelo consigo mismo. Definitivamente vale la pena en un círculo cerrado. –

43

O podría usar SimpleLatLng. Apache 2.0 con licencia y utilizado en un sistema de producción que conozco: el mío.

historia corta:

que estaba buscando un simple biblioteca de geo y no podía encontrar uno para adaptarse a mis necesidades. ¿Y quién quiere escribir y probar y depurar estas pequeñas herramientas geo una y otra vez en cada aplicación? ¡Tiene que haber una mejor manera!

Así que SimpleLatLng nació como una forma de almacenar datos de latitud y longitud, hacer cálculos de distancia y crear límites en forma.

Sé que tengo dos años demasiado tarde para ayudar al póster original, pero mi objetivo es ayudar a las personas como yo que encuentran esta pregunta en una búsqueda. Me encantaría que algunas personas lo usen y contribuyan a las pruebas y la visión de esta pequeña utilidad liviana.

+0

¡esto podría ayudarme! lo creaste? ¿Utiliza la fórmula de Haversine para los cálculos de distancia?¡Intentaré saltar si encuentro el tiempo! – Gevorg

+0

Correcto, usa Haversine para cálculos de distancia con énfasis en (aunque no obsesionada) velocidad y un perfil de memoria bajo. Creo que también tiene otras buenas propiedades de manejo de números, como considerar que las coordenadas que están "realmente cerca" son iguales. – JavadocMD

1

Puede usar el Java Geodesy Library for GPS, usa el Vincenty's formulae que tiene en cuenta la curvatura de la superficie de la tierra.

implementación es el siguiente:

import org.gavaghan.geodesy.*; 
... 
GeodeticCalculator geoCalc = new GeodeticCalculator(); 
Ellipsoid reference = Ellipsoid.WGS84; 
GlobalPosition pointA = new GlobalPosition(latitude, longitude, 0.0); 
GlobalPosition userPos = new GlobalPosition(userLat, userLon, 0.0); 
double distance = geoCalc.calculateGeodeticCurve(reference, userPos, pointA).getEllipsoidalDistance(); 

La distancia resultante es en metros.

11

Para una distancia más precisa (0,5 mm) también se puede utilizar la aproximación Vincenty:

/** 
* Calculates geodetic distance between two points specified by latitude/longitude using Vincenty inverse formula 
* for ellipsoids 
* 
* @param lat1 
*   first point latitude in decimal degrees 
* @param lon1 
*   first point longitude in decimal degrees 
* @param lat2 
*   second point latitude in decimal degrees 
* @param lon2 
*   second point longitude in decimal degrees 
* @returns distance in meters between points with 5.10<sup>-4</sup> precision 
* @see <a href="http://www.movable-type.co.uk/scripts/latlong-vincenty.html">Originally posted here</a> 
*/ 
public static double distVincenty(double lat1, double lon1, double lat2, double lon2) { 
    double a = 6378137, b = 6356752.314245, f = 1/298.257223563; // WGS-84 ellipsoid params 
    double L = Math.toRadians(lon2 - lon1); 
    double U1 = Math.atan((1 - f) * Math.tan(Math.toRadians(lat1))); 
    double U2 = Math.atan((1 - f) * Math.tan(Math.toRadians(lat2))); 
    double sinU1 = Math.sin(U1), cosU1 = Math.cos(U1); 
    double sinU2 = Math.sin(U2), cosU2 = Math.cos(U2); 

    double sinLambda, cosLambda, sinSigma, cosSigma, sigma, sinAlpha, cosSqAlpha, cos2SigmaM; 
    double lambda = L, lambdaP, iterLimit = 100; 
    do { 
     sinLambda = Math.sin(lambda); 
     cosLambda = Math.cos(lambda); 
     sinSigma = Math.sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda) 
       + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda)); 
     if (sinSigma == 0) 
      return 0; // co-incident points 
     cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda; 
     sigma = Math.atan2(sinSigma, cosSigma); 
     sinAlpha = cosU1 * cosU2 * sinLambda/sinSigma; 
     cosSqAlpha = 1 - sinAlpha * sinAlpha; 
     cos2SigmaM = cosSigma - 2 * sinU1 * sinU2/cosSqAlpha; 
     if (Double.isNaN(cos2SigmaM)) 
      cos2SigmaM = 0; // equatorial line: cosSqAlpha=0 (§6) 
     double C = f/16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha)); 
     lambdaP = lambda; 
     lambda = L + (1 - C) * f * sinAlpha 
       * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM))); 
    } while (Math.abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0); 

    if (iterLimit == 0) 
     return Double.NaN; // formula failed to converge 

    double uSq = cosSqAlpha * (a * a - b * b)/(b * b); 
    double A = 1 + uSq/16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq))); 
    double B = uSq/1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq))); 
    double deltaSigma = B 
      * sinSigma 
      * (cos2SigmaM + B 
        /4 
        * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B/6 * cos2SigmaM 
          * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM))); 
    double dist = b * A * (sigma - deltaSigma); 

    return dist; 
} 

Este código fue adaptado libremente de http://www.movable-type.co.uk/scripts/latlong-vincenty.html

6

corregido fórmula Haversine Distancia ....

public static double HaverSineDistance(double lat1, double lng1, double lat2, double lng2) 
{ 
    // mHager 08-12-2012 
    // http://en.wikipedia.org/wiki/Haversine_formula 
    // Implementation 

    // convert to radians 
    lat1 = Math.toRadians(lat1); 
    lng1 = Math.toRadians(lng1); 
    lat2 = Math.toRadians(lat2); 
    lng2 = Math.toRadians(lng2); 

    double dlon = lng2 - lng1; 
    double dlat = lat2 - lat1; 

    double a = Math.pow((Math.sin(dlat/2)),2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(dlon/2),2); 

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

    return EARTH_RADIUS * c; 
} 
+0

La Tierra no es una esfera perfecta –

+0

Correcto, a veces ** cerrar ** es lo suficientemente bueno. IE: ¿Por qué el Sr. Inman lo creó? Le diría que está equivocado, pero que está muerto. : o (Si necesita calcular la forma oblonga del mundo, hay mejores fórmulas para eso. También hay algunas bibliotecas de Apache geniales que puede usar también. Si solo necesita algo simple, este es un buen ejemplo rápido . :) –

+0

Ya, la fórmula de Haversine se basa en el principio de 'cerrar es lo suficientemente bueno'. En ese momento estábamos midiendo distancias que eran <50 millas para determinar la proximidad de un lugar a otro como una "heurística". –

0

Me suelen utilizar MATLAB con el Mapping Toolbox, y luego usar el código en Java usando mi MATLAB Builder JA. Se me hace la vida mucho más simple. Dado que la mayoría de las escuelas lo tienen para el acceso gratuito de los estudiantes, puedes probarlo (u obtener la versión de prueba para superar tu trabajo).

2

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

public static Double distanceBetweenTwoLocationsInKm(Double latitudeOne, Double longitudeOne, Double latitudeTwo, Double longitudeTwo) { 
     if (latitudeOne == null || latitudeTwo == null || longitudeOne == null || longitudeTwo == null) { 
      return null; 
     } 

     Double earthRadius = 6371.0; 
     Double diffBetweenLatitudeRadians = Math.toRadians(latitudeTwo - latitudeOne); 
     Double diffBetweenLongitudeRadians = Math.toRadians(longitudeTwo - longitudeOne); 
     Double latitudeOneInRadians = Math.toRadians(latitudeOne); 
     Double latitudeTwoInRadians = Math.toRadians(latitudeTwo); 
     Double a = Math.sin(diffBetweenLatitudeRadians/2) * Math.sin(diffBetweenLatitudeRadians/2) + Math.cos(latitudeOneInRadians) * Math.cos(latitudeTwoInRadians) * Math.sin(diffBetweenLongitudeRadians/2) 
       * Math.sin(diffBetweenLongitudeRadians/2); 
     Double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 
     return (earthRadius * c); 
    } 
1

Sé que hay muchas respuestas, pero al hacer algunas investigaciones sobre este tema, he encontrado que la mayoría de las respuestas aquí utilizan la fórmula Haversine, pero la fórmula Vincenty es en realidad más precisa. Hubo una publicación que adaptó el cálculo de una versión de Javascript, pero es muy difícil de manejar. Encontré una versión que es superior porque:

  1. También tiene una licencia abierta.
  2. Utiliza principios de OOP.
  3. Tiene una mayor flexibilidad para elegir el elipsoide que desea utilizar.
  4. Tiene más métodos para permitir diferentes cálculos en el futuro.
  5. Está bien documentado.

VincentyDistanceCalculator

1

Este método podría ayudar a encontrar la distancia entre la ubicación geográfica en km.

private double getDist(double lat1, double lon1, double lat2, double lon2) 
{ 
    int R = 6373; // radius of the earth in kilometres 
    double lat1rad = Math.toRadians(lat1); 
    double lat2rad = Math.toRadians(lat2); 
    double deltaLat = Math.toRadians(lat2-lat1); 
    double deltaLon = Math.toRadians(lon2-lon1); 

    double a = Math.sin(deltaLat/2) * Math.sin(deltaLat/2) + 
      Math.cos(lat1rad) * Math.cos(lat2rad) * 
      Math.sin(deltaLon/2) * Math.sin(deltaLon/2); 
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 

    double d = R * c; 
    return d; 
} 
Cuestiones relacionadas