Todas las respuestas anteriores son sólo parcialmente correcta. Especialmente en regiones como Australia, siempre incluyen polos y calculan un rectángulo muy grande incluso para 10kms.
Especialmente el algoritmo de Jan Philip Matuschek en http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex incluía un rectángulo muy grande de (-37, -90, -180, 180) para casi todos los puntos en Australia. Esto afecta a los usuarios grandes en la base de datos y la distancia debe calcularse para todos los usuarios en casi la mitad del país.
Encontré que el Drupal API Earth Algorithm por Rochester Institute of Technology funciona mejor alrededor de la pértiga y en otros lugares y es mucho más fácil de implementar.
https://www.rit.edu/drupal/api/drupal/sites%21all%21modules%21location%21earth.inc/7.54
Uso earth_latitude_range
y earth_longitude_range
del algoritmo anterior para calcular rectángulo de delimitación
Aquí está la aplicación es Java
/**
* Get bouding rectangle using Drupal Earth Algorithm
* @see https://www.rit.edu/drupal/api/drupal/sites%21all%21modules%21location%21earth.inc/7.54
* @param lat
* @param lng
* @param distance
* @return
*/
default BoundingRectangle getBoundingRectangleDrupalEarthAlgo(double lat, double lng, int distance) {
lng = Math.toRadians(lng);
lat = Math.toRadians(lat);
double radius = earth_radius(lat);
List<Double> retLats = earth_latitude_range(lat, radius, distance);
List<Double> retLngs = earth_longitude_range(lat, lng, radius, distance);
return new BoundingRectangle(retLats.get(0), retLats.get(1), retLngs.get(0), retLngs.get(1));
}
/**
* Calculate latitude range based on earths radius at a given point
* @param latitude
* @param longitude
* @param distance
* @return
*/
default List<Double> earth_latitude_range(double lat, double radius, double distance) {
// Estimate the min and max latitudes within distance of a given location.
double angle = distance/radius;
double minlat = lat - angle;
double maxlat = lat + angle;
double rightangle = Math.PI/2;
// Wrapped around the south pole.
if (minlat < -rightangle) {
double overshoot = -minlat - rightangle;
minlat = -rightangle + overshoot;
if (minlat > maxlat) {
maxlat = minlat;
}
minlat = -rightangle;
}
// Wrapped around the north pole.
if (maxlat > rightangle) {
double overshoot = maxlat - rightangle;
maxlat = rightangle - overshoot;
if (maxlat < minlat) {
minlat = maxlat;
}
maxlat = rightangle;
}
List<Double> ret = new ArrayList<>();
ret.add((minlat));
ret.add((maxlat));
return ret;
}
/**
* Calculate longitude range based on earths radius at a given point
* @param lat
* @param lng
* @param earth_radius
* @param distance
* @return
*/
default List<Double> earth_longitude_range(double lat, double lng, double earth_radius, int distance) {
// Estimate the min and max longitudes within distance of a given location.
double radius = earth_radius * Math.cos(lat);
double angle;
if (radius > 0) {
angle = Math.abs(distance/radius);
angle = Math.min(angle, Math.PI);
}
else {
angle = Math.PI;
}
double minlong = lng - angle;
double maxlong = lng + angle;
if (minlong < -Math.PI) {
minlong = minlong + Math.PI * 2;
}
if (maxlong > Math.PI) {
maxlong = maxlong - Math.PI * 2;
}
List<Double> ret = new ArrayList<>();
ret.add((minlong));
ret.add((maxlong));
return ret;
}
/**
* Calculate earth radius at given latitude
* @param latitude
* @return
*/
default Double earth_radius(double latitude) {
// Estimate the Earth's radius at a given latitude.
// Default to an approximate average radius for the United States.
double lat = Math.toRadians(latitude);
double x = Math.cos(lat)/6378137.0;
double y = Math.sin(lat)/(6378137.0 * (1 - (1/298.257223563)));
//Make sure earth's radius is in km , not meters
return (1/(Math.sqrt(x * x + y * y)))/1000;
}
y utilizar la fórmula de cálculo distancia documentado por Google Maps para calcular la distancia
https://developers.google.com/maps/solutions/store-locator/clothing-store-locator#outputting-data-as-xml-using-php
Para buscar por kilómetros en lugar de millas, reemplace 3959 con 6371. Para (latitud, la longitud) = (37, -122) y una mesa de marcadores con columnas lat y lng, la fórmula es:
SELECT id, (3959 * acos(cos(radians(37)) * cos(radians(lat)) * cos(radians(lng) - radians(-122)) + sin(radians(37)) * sin(radians(lat)))) AS distance FROM markers HAVING distance < 25 ORDER BY distance LIMIT 0 , 20;
Esta pregunta es solo trigonometría directa; Sugeriría eliminar las etiquetas Java y Algoritmo. (Si eso es posible). –
oh, lo siento, hice esto ahora. –
La naturaleza esférica de la tierra necesita ser considerada. ¿Qué respuesta quieres si la posición de entrada es el Polo Norte? – MarkJ