¿Cómo calcular el cuadro delimitador para una ubicación dada de lat / lng?

101

He dado una ubicación definida por latitud y longitud. Ahora quiero calcular un cuadro delimitador, por ejemplo, a 10 kilómetros de ese punto.

El cuadro delimitador debe definirse como latmin, lngmin y latmax, lngmax.

Necesito estas cosas para usar la API de panoramio .

¿Alguien conoce la fórmula de cómo obtener esos puntos?

Editar: Chicos, estoy buscando una fórmula / función que tome lat & lng como entrada y devuelva un cuadro delimitador como latmin & lngmin y latmax & latmin. Mysql, php, c #, javascript está bien, pero también el pseudocódigo debería estar bien.

Editar: no estoy buscando una solución que me muestre la distancia de 2 puntos

Michal
fuente
Si está utilizando una geodatabase en algún lugar, seguramente tienen un cálculo de cuadro delimitador integrado. Incluso puede comprobar la fuente de PostGIS / GEOS, por ejemplo.
Vinko Vrsalovic

Respuestas:

60

Sugiero aproximar localmente la superficie de la Tierra como una esfera con radio dado por el elipsoide WGS84 en la latitud dada. Sospecho que el cálculo exacto de latMin y latMax requeriría funciones elípticas y no produciría un aumento apreciable en la precisión (WGS84 es en sí mismo una aproximación).

A continuación, mi implementación (está escrita en Python; no la he probado):

# degrees to radians
def deg2rad(degrees):
    return math.pi*degrees/180.0
# radians to degrees
def rad2deg(radians):
    return 180.0*radians/math.pi

# Semi-axes of WGS-84 geoidal reference
WGS84_a = 6378137.0  # Major semiaxis [m]
WGS84_b = 6356752.3  # Minor semiaxis [m]

# Earth radius at a given latitude, according to the WGS-84 ellipsoid [m]
def WGS84EarthRadius(lat):
    # http://en.wikipedia.org/wiki/Earth_radius
    An = WGS84_a*WGS84_a * math.cos(lat)
    Bn = WGS84_b*WGS84_b * math.sin(lat)
    Ad = WGS84_a * math.cos(lat)
    Bd = WGS84_b * math.sin(lat)
    return math.sqrt( (An*An + Bn*Bn)/(Ad*Ad + Bd*Bd) )

# Bounding box surrounding the point at given coordinates,
# assuming local approximation of Earth surface as a sphere
# of radius given by WGS84
def boundingBox(latitudeInDegrees, longitudeInDegrees, halfSideInKm):
    lat = deg2rad(latitudeInDegrees)
    lon = deg2rad(longitudeInDegrees)
    halfSide = 1000*halfSideInKm

    # Radius of Earth at given latitude
    radius = WGS84EarthRadius(lat)
    # Radius of the parallel at given latitude
    pradius = radius*math.cos(lat)

    latMin = lat - halfSide/radius
    latMax = lat + halfSide/radius
    lonMin = lon - halfSide/pradius
    lonMax = lon + halfSide/pradius

    return (rad2deg(latMin), rad2deg(lonMin), rad2deg(latMax), rad2deg(lonMax))

EDITAR: El siguiente código convierte (grados, números primos, segundos) a grados + fracciones de grado, y viceversa (no probado):

def dps2deg(degrees, primes, seconds):
    return degrees + primes/60.0 + seconds/3600.0

def deg2dps(degrees):
    intdeg = math.floor(degrees)
    primes = (degrees - intdeg)*60.0
    intpri = math.floor(primes)
    seconds = (primes - intpri)*60.0
    intsec = round(seconds)
    return (int(intdeg), int(intpri), int(intsec))
Federico A. Ramponi
fuente
4
Como se señala en la documentación de la biblioteca CPAN sugerida, esto tiene sentido solo para halfSide <= 10km.
Federico A. Ramponi
1
¿Funciona esto cerca de los polos? No parece, porque parece que termina con latMin <-pi (para el polo sur) o latMax> pi (para el polo norte)? Creo que cuando estás dentro de la mitad del lado de un poste, debes devolver un cuadro delimitador que incluya todas las longitudes y latitudes calculadas normalmente para el lado alejado del poste y en el poste del lado cerca del poste.
Doug McClean
1
Aquí hay una implementación de PHP de la especificación que se encuentra en JanMatuschek.de: github.com/anthonymartin/GeoLocation.class.php
Anthony Martin
2
He agregado una implementación de C # de esta respuesta a continuación.
Ε Г И І И О
2
@ FedericoA.Ramponi ¿Qué es el haldSideinKm aquí? no entiendo ... ¿qué debo pasar en estos argumentos, el radio entre dos puntos en el mapa o qué?
53

Escribí un artículo sobre cómo encontrar las coordenadas delimitadas:

http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates

El artículo explica las fórmulas y también proporciona una implementación de Java. (También muestra por qué la fórmula de Federico para la longitud mínima / máxima es inexacta).

Jan Philip Matuschek
fuente
4
Hice un puerto PHP de su clase GeoLocation. Se puede encontrar aquí: pastie.org/5416584
Anthony Martin
1
Lo he subido a github ahora: github.com/anthonymartin/GeoLocation.class.php
Anthony Martin
1
¿Esto siquiera responde a la pregunta? Si solo tenemos 1 punto de partida, no podemos calcular la distancia del gran círculo como se hace en este código, que requiere dos ubicaciones de latitud y longitud.
mdoran3844
hay un código incorrecto en su variante de C #, por ejemplo:, public override string ToString()es muy malo anular un método global de este tipo solo para un propósito, es mejor simplemente agregar otro método, luego anular el método estándar, que se puede usar en otras partes de la aplicación, no para el gis exacto ...
Aquí hay un enlace actualizado al puerto PHP de la clase GeoLocaiton de Jan: github.com/anthonymartin/GeoLocation.php
Anthony Martin
34

Aquí he convertido la respuesta de Federico A. Ramponi a C # para cualquiera que esté interesado:

public class MapPoint
{
    public double Longitude { get; set; } // In Degrees
    public double Latitude { get; set; } // In Degrees
}

public class BoundingBox
{
    public MapPoint MinPoint { get; set; }
    public MapPoint MaxPoint { get; set; }
}        

// Semi-axes of WGS-84 geoidal reference
private const double WGS84_a = 6378137.0; // Major semiaxis [m]
private const double WGS84_b = 6356752.3; // Minor semiaxis [m]

// 'halfSideInKm' is the half length of the bounding box you want in kilometers.
public static BoundingBox GetBoundingBox(MapPoint point, double halfSideInKm)
{            
    // Bounding box surrounding the point at given coordinates,
    // assuming local approximation of Earth surface as a sphere
    // of radius given by WGS84
    var lat = Deg2rad(point.Latitude);
    var lon = Deg2rad(point.Longitude);
    var halfSide = 1000 * halfSideInKm;

    // Radius of Earth at given latitude
    var radius = WGS84EarthRadius(lat);
    // Radius of the parallel at given latitude
    var pradius = radius * Math.Cos(lat);

    var latMin = lat - halfSide / radius;
    var latMax = lat + halfSide / radius;
    var lonMin = lon - halfSide / pradius;
    var lonMax = lon + halfSide / pradius;

    return new BoundingBox { 
        MinPoint = new MapPoint { Latitude = Rad2deg(latMin), Longitude = Rad2deg(lonMin) },
        MaxPoint = new MapPoint { Latitude = Rad2deg(latMax), Longitude = Rad2deg(lonMax) }
    };            
}

// degrees to radians
private static double Deg2rad(double degrees)
{
    return Math.PI * degrees / 180.0;
}

// radians to degrees
private static double Rad2deg(double radians)
{
    return 180.0 * radians / Math.PI;
}

// Earth radius at a given latitude, according to the WGS-84 ellipsoid [m]
private static double WGS84EarthRadius(double lat)
{
    // http://en.wikipedia.org/wiki/Earth_radius
    var An = WGS84_a * WGS84_a * Math.Cos(lat);
    var Bn = WGS84_b * WGS84_b * Math.Sin(lat);
    var Ad = WGS84_a * Math.Cos(lat);
    var Bd = WGS84_b * Math.Sin(lat);
    return Math.Sqrt((An*An + Bn*Bn) / (Ad*Ad + Bd*Bd));
}
Ε Г И І И О
fuente
1
Gracias, este trabajo para mí. Tuve que probar el código a mano, no sabía cómo escribir una prueba unitaria para esto, pero genera resultados precisos con el grado de precisión que necesito
mdoran3844
¿Qué es el haldSideinKm aquí? no entiendo ... ¿qué debo pasar en estos argumentos, el radio entre dos puntos en el mapa o qué?
@GeloVolro: Esa es la mitad de la longitud del cuadro delimitador que desea.
Ε Г И І И О
1
No es necesario que escriba su propia clase MapPoint. Hay una clase GeoCoordinate en System.Device.Location que toma la latitud y la longitud como parámetros.
Lawyerson
1
Esto funciona de maravilla. Realmente aprecio el puerto C #.
Tom Larcher
10

Escribí una función de JavaScript que devuelve las cuatro coordenadas de un cuadro delimitador cuadrado, dada una distancia y un par de coordenadas:

'use strict';

/**
 * @param {number} distance - distance (km) from the point represented by centerPoint
 * @param {array} centerPoint - two-dimensional array containing center coords [latitude, longitude]
 * @description
 *   Computes the bounding coordinates of all points on the surface of a sphere
 *   that has a great circle distance to the point represented by the centerPoint
 *   argument that is less or equal to the distance argument.
 *   Technique from: Jan Matuschek <http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates>
 * @author Alex Salisbury
*/

getBoundingBox = function (centerPoint, distance) {
  var MIN_LAT, MAX_LAT, MIN_LON, MAX_LON, R, radDist, degLat, degLon, radLat, radLon, minLat, maxLat, minLon, maxLon, deltaLon;
  if (distance < 0) {
    return 'Illegal arguments';
  }
  // helper functions (degrees<–>radians)
  Number.prototype.degToRad = function () {
    return this * (Math.PI / 180);
  };
  Number.prototype.radToDeg = function () {
    return (180 * this) / Math.PI;
  };
  // coordinate limits
  MIN_LAT = (-90).degToRad();
  MAX_LAT = (90).degToRad();
  MIN_LON = (-180).degToRad();
  MAX_LON = (180).degToRad();
  // Earth's radius (km)
  R = 6378.1;
  // angular distance in radians on a great circle
  radDist = distance / R;
  // center point coordinates (deg)
  degLat = centerPoint[0];
  degLon = centerPoint[1];
  // center point coordinates (rad)
  radLat = degLat.degToRad();
  radLon = degLon.degToRad();
  // minimum and maximum latitudes for given distance
  minLat = radLat - radDist;
  maxLat = radLat + radDist;
  // minimum and maximum longitudes for given distance
  minLon = void 0;
  maxLon = void 0;
  // define deltaLon to help determine min and max longitudes
  deltaLon = Math.asin(Math.sin(radDist) / Math.cos(radLat));
  if (minLat > MIN_LAT && maxLat < MAX_LAT) {
    minLon = radLon - deltaLon;
    maxLon = radLon + deltaLon;
    if (minLon < MIN_LON) {
      minLon = minLon + 2 * Math.PI;
    }
    if (maxLon > MAX_LON) {
      maxLon = maxLon - 2 * Math.PI;
    }
  }
  // a pole is within the given distance
  else {
    minLat = Math.max(minLat, MIN_LAT);
    maxLat = Math.min(maxLat, MAX_LAT);
    minLon = MIN_LON;
    maxLon = MAX_LON;
  }
  return [
    minLon.radToDeg(),
    minLat.radToDeg(),
    maxLon.radToDeg(),
    maxLat.radToDeg()
  ];
};
asalisbury
fuente
Este código no funciona en absoluto. Es decir, incluso después de arreglar los errores obvios, como minLon = void 0;y maxLon = MAX_LON;que todavía no funciona.
aroth
1
@aroth, acabo de probarlo y no tuve ningún problema. Recuerde que el centerPointargumento es una matriz que consta de dos coordenadas. Por ejemplo, getBoundingBox([42.2, 34.5], 50)- void 0es la salida de CoffeeScript para "indefinido" y no afectará la capacidad de ejecución de los códigos.
asalisbury
este código no funciona. degLat.degToRadno es una función
user299709
El código funcionó en Node y Chrome tal cual, hasta que lo puse dentro de un proyecto en el que estoy trabajando y comencé a recibir degToRaderrores de "no es una función". Nunca supe por qué, pero Number.prototype.no es una buena idea para una función de utilidad como esta, así que las convertí en funciones locales normales. También es importante tener en cuenta que la caja devuelta es [LNG, LAT, LNG, LAT] en lugar de [LAT, LNG, LAT, LNG]. Modifiqué la función de retorno cuando usé esto para evitar confusiones.
KernelDeimos
9

Como necesitaba una estimación muy aproximada, para filtrar algunos documentos innecesarios en una consulta de búsqueda elástica, empleé la siguiente fórmula:

Min.lat = Given.Lat - (0.009 x N)
Max.lat = Given.Lat + (0.009 x N)
Min.lon = Given.lon - (0.009 x N)
Max.lon = Given.lon + (0.009 x N)

N = kms requeridos desde la ubicación dada. Para su caso N = 10

No es exacto pero práctico.

Ajay
fuente
De hecho, no es precisa, pero sigue siendo útil y muy fácil de implementar.
MV.
6

Estás buscando una fórmula elipsoide.

El mejor lugar que he encontrado para comenzar a codificar se basa en la biblioteca Geo :: Ellipsoid de CPAN. Le brinda una línea de base para crear sus pruebas y comparar sus resultados con sus resultados. Lo usé como base para una biblioteca similar para PHP en mi empleador anterior.

Geo :: Elipsoide

Eche un vistazo al locationmétodo. Llámalo dos veces y tendrás tu bbox.

No publicaste el idioma que estabas usando. Es posible que ya exista una biblioteca de codificación geográfica disponible para usted.

Ah, y si aún no lo ha descubierto, los mapas de Google usan el elipsoide WGS84.

jcoby
fuente
5

Ilustración de @Jan Philip Matuschek excelente explicación (por favor, vote a favor su respuesta, no esta; estoy agregando esto porque me tomé un poco de tiempo para comprender la respuesta original)

La técnica del cuadro delimitador para optimizar la búsqueda de vecinos más cercanos necesitaría derivar los pares de longitud y latitud mínima y máxima para un punto P a la distancia d. Todos los puntos que quedan fuera de estos definitivamente están a una distancia mayor que d del punto. Una cosa a tener en cuenta aquí es el cálculo de la latitud de intersección, como se destaca en la explicación de Jan Philip Matuschek. La latitud de la intersección no se encuentra en la latitud del punto P, sino que está ligeramente desviada. Esta es una parte que a menudo se pasa por alto, pero es importante para determinar la longitud límite mínima y máxima correcta para el punto P para la distancia d. Esto también es útil en la verificación.

La distancia haversine entre (latitud de intersección, longitud alta) y (latitud, longitud) de P es igual a la distancia d.

Python gist aquí https://gist.github.com/alexcpn/f95ae83a7ee0293a5225

ingrese la descripción de la imagen aquí

Alex Punnen
fuente
5

Aquí hay una implementación simple usando javascript que se basa en la conversión del grado de latitud a kms donde 1 degree latitude ~ 111.2 km.

Estoy calculando los límites del mapa a partir de una latitud y longitud determinadas con un ancho de 10 km.

function getBoundsFromLatLng(lat, lng){
     var lat_change = 10/111.2;
     var lon_change = Math.abs(Math.cos(lat*(Math.PI/180)));
     var bounds = { 
         lat_min : lat - lat_change,
         lon_min : lng - lon_change,
         lat_max : lat + lat_change,
         lon_max : lng + lon_change
     };
     return bounds;
}
Noushad
fuente
4

Adapté un script PHP que encontré para hacer precisamente esto. Puede usarlo para encontrar las esquinas de un cuadro alrededor de un punto (digamos, a 20 km). Mi ejemplo específico es para la API de Google Maps:

http://www.richardpeacock.com/blog/2011/11/draw-box-around-coordinate-google-maps-based-miles-or-kilometers

Ricardo
fuente
-1 Lo que busca el OP es: dado un punto de referencia (lat, lon) y una distancia, encuentre el cuadro más pequeño de modo que todos los puntos que estén a <= "distancia" del punto de referencia no estén fuera del cuadro. Su caja tiene sus esquinas a "distancia" del punto de referencia y, por lo tanto, es demasiado pequeña. Ejemplo: el punto que está a "distancia" hacia el norte está fuera de su casilla.
John Machin
Bueno, por casualidad, es exactamente lo que necesitaba. Así que gracias, incluso si no responde esta pregunta :)
Simon Steinberger
Bueno, ¡me alegro de que pueda ayudar a alguien!
Richard
1

Estaba trabajando en el problema del cuadro delimitador como un problema secundario para encontrar todos los puntos dentro del radio SrcRad de un punto LAT, LONG estático. Ha habido bastantes cálculos que utilizan

maxLon = $lon + rad2deg($rad/$R/cos(deg2rad($lat)));
minLon = $lon - rad2deg($rad/$R/cos(deg2rad($lat)));

para calcular los límites de longitud, pero encontré que esto no da todas las respuestas que se necesitaban. Porque lo que realmente quieres hacer es

(SrcRad/RadEarth)/cos(deg2rad(lat))

Lo sé, sé que la respuesta debería ser la misma, pero descubrí que no lo era. Parecía que al no asegurarme de que estaba haciendo (SRCrad / RadEarth) Primero y luego dividiendo por la parte de Cos, estaba omitiendo algunos puntos de ubicación.

Después de obtener todos los puntos del cuadro delimitador, si tiene una función que calcula la distancia de punto a punto dada la latitud, es fácil obtener solo aquellos puntos que están a un cierto radio de distancia del punto fijo. Aquí esta lo que hice. Sé que tomó algunos pasos adicionales pero me ayudó

-- GLOBAL Constants
gc_pi CONSTANT REAL := 3.14159265359;  -- Pi

-- Conversion Factor Constants
gc_rad_to_degs          CONSTANT NUMBER := 180/gc_pi; -- Conversion for Radians to Degrees 180/pi
gc_deg_to_rads          CONSTANT NUMBER := gc_pi/180; --Conversion of Degrees to Radians

lv_stat_lat    -- The static latitude point that I am searching from 
lv_stat_long   -- The static longitude point that I am searching from 

-- Angular radius ratio in radians
lv_ang_radius := lv_search_radius / lv_earth_radius;
lv_bb_maxlat := lv_stat_lat + (gc_rad_to_deg * lv_ang_radius);
lv_bb_minlat := lv_stat_lat - (gc_rad_to_deg * lv_ang_radius);

--Here's the tricky part, accounting for the Longitude getting smaller as we move up the latitiude scale
-- I seperated the parts of the equation to make it easier to debug and understand
-- I may not be a smart man but I know what the right answer is... :-)

lv_int_calc := gc_deg_to_rads * lv_stat_lat;
lv_int_calc := COS(lv_int_calc);
lv_int_calc := lv_ang_radius/lv_int_calc;
lv_int_calc := gc_rad_to_degs*lv_int_calc;

lv_bb_maxlong := lv_stat_long + lv_int_calc;
lv_bb_minlong := lv_stat_long - lv_int_calc;

-- Now select the values from your location datatable 
SELECT *  FROM (
SELECT cityaliasname, city, state, zipcode, latitude, longitude, 
-- The actual distance in miles
spherecos_pnttopntdist(lv_stat_lat, lv_stat_long, latitude, longitude, 'M') as miles_dist    
FROM Location_Table 
WHERE latitude between lv_bb_minlat AND lv_bb_maxlat
AND   longitude between lv_bb_minlong and lv_bb_maxlong)
WHERE miles_dist <= lv_limit_distance_miles
order by miles_dist
;
Greg Beck
fuente
0

Es muy simple, simplemente vaya al sitio web de panoramio y luego abra el mapa mundial desde el sitio web de panoramio. Luego, vaya a la ubicación especificada, cuya latitud y longitud se requieren.

Luego, encontró latitud y longitud en la barra de direcciones, por ejemplo, en esta dirección.

http://www.panoramio.com/map#lt=32.739485&ln=70.491211&z=9&k=1&a=1&tab=1&pl=all

lt = 32.739485 => latitud ln = 70.491211 => longitud

este widget de la API de JavaScript de Panoramio crea un cuadro delimitador alrededor de un par lat / long y luego devuelve todas las fotos dentro de esos límites.

Otro tipo de widget de la API de JavaScript de Panoramio en el que también puede cambiar el color de fondo con el ejemplo y el código está aquí .

No se muestra en el estado de ánimo de composición. Se muestra después de la publicación.

<div dir="ltr" style="text-align: center;" trbidi="on">
<script src="https://ssl.panoramio.com/wapi/wapi.js?v=1&amp;hl=en"></script>
<div id="wapiblock" style="float: right; margin: 10px 15px"></div>
<script type="text/javascript">
var myRequest = {
  'tag': 'kahna',
  'rect': {'sw': {'lat': -30, 'lng': 10.5}, 'ne': {'lat': 50.5, 'lng': 30}}
};
  var myOptions = {
  'width': 300,
  'height': 200
};
var wapiblock = document.getElementById('wapiblock');
var photo_widget = new panoramio.PhotoWidget('wapiblock', myRequest, myOptions);
photo_widget.setPosition(0);
</script>
</div>
kahna9
fuente
0

Aquí he convertido la respuesta de Federico A. Ramponi a PHP si alguien está interesado:

<?php
# deg2rad and rad2deg are already within PHP

# Semi-axes of WGS-84 geoidal reference
$WGS84_a = 6378137.0;  # Major semiaxis [m]
$WGS84_b = 6356752.3;  # Minor semiaxis [m]

# Earth radius at a given latitude, according to the WGS-84 ellipsoid [m]
function WGS84EarthRadius($lat)
{
    global $WGS84_a, $WGS84_b;

    $an = $WGS84_a * $WGS84_a * cos($lat);
    $bn = $WGS84_b * $WGS84_b * sin($lat);
    $ad = $WGS84_a * cos($lat);
    $bd = $WGS84_b * sin($lat);

    return sqrt(($an*$an + $bn*$bn)/($ad*$ad + $bd*$bd));
}

# Bounding box surrounding the point at given coordinates,
# assuming local approximation of Earth surface as a sphere
# of radius given by WGS84
function boundingBox($latitudeInDegrees, $longitudeInDegrees, $halfSideInKm)
{
    $lat = deg2rad($latitudeInDegrees);
    $lon = deg2rad($longitudeInDegrees);
    $halfSide = 1000 * $halfSideInKm;

    # Radius of Earth at given latitude
    $radius = WGS84EarthRadius($lat);
    # Radius of the parallel at given latitude
    $pradius = $radius*cos($lat);

    $latMin = $lat - $halfSide / $radius;
    $latMax = $lat + $halfSide / $radius;
    $lonMin = $lon - $halfSide / $pradius;
    $lonMax = $lon + $halfSide / $pradius;

    return array(rad2deg($latMin), rad2deg($lonMin), rad2deg($latMax), rad2deg($lonMax));
}
?>
Joe Black
fuente
0

Gracias @Fedrico A. por la implementación de Phyton, lo he portado a una clase de categoría Objective C. Aquí está:

#import "LocationService+Bounds.h"

//Semi-axes of WGS-84 geoidal reference
const double WGS84_a = 6378137.0; //Major semiaxis [m]
const double WGS84_b = 6356752.3; //Minor semiaxis [m]

@implementation LocationService (Bounds)

struct BoundsLocation {
    double maxLatitude;
    double minLatitude;
    double maxLongitude;
    double minLongitude;
};

+ (struct BoundsLocation)locationBoundsWithLatitude:(double)aLatitude longitude:(double)aLongitude maxDistanceKm:(NSInteger)aMaxKmDistance {
    return [self boundingBoxWithLatitude:aLatitude longitude:aLongitude halfDistanceKm:aMaxKmDistance/2];
}

#pragma mark - Algorithm 

+ (struct BoundsLocation)boundingBoxWithLatitude:(double)aLatitude longitude:(double)aLongitude halfDistanceKm:(double)aDistanceKm {
    double radianLatitude = [self degreesToRadians:aLatitude];
    double radianLongitude = [self degreesToRadians:aLongitude];
    double halfDistanceMeters = aDistanceKm*1000;


    double earthRadius = [self earthRadiusAtLatitude:radianLatitude];
    double parallelRadius = earthRadius*cosl(radianLatitude);

    double radianMinLatitude = radianLatitude - halfDistanceMeters/earthRadius;
    double radianMaxLatitude = radianLatitude + halfDistanceMeters/earthRadius;
    double radianMinLongitude = radianLongitude - halfDistanceMeters/parallelRadius;
    double radianMaxLongitude = radianLongitude + halfDistanceMeters/parallelRadius;

    struct BoundsLocation bounds;
    bounds.minLatitude = [self radiansToDegrees:radianMinLatitude];
    bounds.maxLatitude = [self radiansToDegrees:radianMaxLatitude];
    bounds.minLongitude = [self radiansToDegrees:radianMinLongitude];
    bounds.maxLongitude = [self radiansToDegrees:radianMaxLongitude];

    return bounds;
}

+ (double)earthRadiusAtLatitude:(double)aRadianLatitude {
    double An = WGS84_a * WGS84_a * cosl(aRadianLatitude);
    double Bn = WGS84_b * WGS84_b * sinl(aRadianLatitude);
    double Ad = WGS84_a * cosl(aRadianLatitude);
    double Bd = WGS84_b * sinl(aRadianLatitude);
    return sqrtl( ((An * An) + (Bn * Bn))/((Ad * Ad) + (Bd * Bd)) );
}

+ (double)degreesToRadians:(double)aDegrees {
    return M_PI*aDegrees/180.0;
}

+ (double)radiansToDegrees:(double)aRadians {
    return 180.0*aRadians/M_PI;
}



@end

Lo he probado y parece que funciona bien. Struct BoundsLocation debe reemplazarse por una clase, la he usado solo para compartirla aquí.

Jesúslg123
fuente
0

Todas las respuestas anteriores son solo parcialmente correctas . Especialmente en regiones como Australia, siempre incluyen un poste y calculan un rectángulo muy grande incluso para 10 km.

Especialmente el algoritmo de Jan Philip Matuschek en http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex incluyó un rectángulo muy grande de (-37, -90, -180, 180) para casi todos los puntos de Australia. Esto afecta a un gran número de usuarios en la base de datos y la distancia debe calcularse para todos los usuarios en casi la mitad del país.

Descubrí que el algoritmo de tierra API de Drupal del Instituto de Tecnología de Rochester funciona mejor tanto en el polo como 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

Utilice earth_latitude_rangey earth_longitude_rangedel algoritmo anterior para calcular el rectángulo delimitador

Y use la fórmula de cálculo de distancia documentada 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 (Lat, Lng) = (37, -122) y una tabla 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;

Lea mi respuesta detallada en https://stackoverflow.com/a/45950426/5076414

Sacky San
fuente
0

Aquí está la respuesta de Federico Ramponi en Go. Nota: sin verificación de errores :(

import (
    "math"
)

// Semi-axes of WGS-84 geoidal reference
const (
    // Major semiaxis (meters)
    WGS84A = 6378137.0
    // Minor semiaxis (meters)
    WGS84B = 6356752.3
)

// BoundingBox represents the geo-polygon that encompasses the given point and radius
type BoundingBox struct {
    LatMin float64
    LatMax float64
    LonMin float64
    LonMax float64
}

// Convert a degree value to radians
func deg2Rad(deg float64) float64 {
    return math.Pi * deg / 180.0
}

// Convert a radian value to degrees
func rad2Deg(rad float64) float64 {
    return 180.0 * rad / math.Pi
}

// Get the Earth's radius in meters at a given latitude based on the WGS84 ellipsoid
func getWgs84EarthRadius(lat float64) float64 {
    an := WGS84A * WGS84A * math.Cos(lat)
    bn := WGS84B * WGS84B * math.Sin(lat)

    ad := WGS84A * math.Cos(lat)
    bd := WGS84B * math.Sin(lat)

    return math.Sqrt((an*an + bn*bn) / (ad*ad + bd*bd))
}

// GetBoundingBox returns a BoundingBox encompassing the given lat/long point and radius
func GetBoundingBox(latDeg float64, longDeg float64, radiusKm float64) BoundingBox {
    lat := deg2Rad(latDeg)
    lon := deg2Rad(longDeg)
    halfSide := 1000 * radiusKm

    // Radius of Earth at given latitude
    radius := getWgs84EarthRadius(lat)

    pradius := radius * math.Cos(lat)

    latMin := lat - halfSide/radius
    latMax := lat + halfSide/radius
    lonMin := lon - halfSide/pradius
    lonMax := lon + halfSide/pradius

    return BoundingBox{
        LatMin: rad2Deg(latMin),
        LatMax: rad2Deg(latMax),
        LonMin: rad2Deg(lonMin),
        LonMax: rad2Deg(lonMax),
    }
}
sma
fuente