Come calcolare il riquadro di delimitazione per una data posizione lat / lng?


101

Ho fornito una posizione definita da latitudine e longitudine. Ora voglio calcolare un riquadro di delimitazione ad esempio entro 10 chilometri da quel punto.

Il riquadro di delimitazione dovrebbe essere definito come latmin, lngmin e latmax, lngmax.

Ho bisogno di questa roba per utilizzare l' API panoramio .

Qualcuno conosce la formula di come ottenere questi punti?

Modifica: ragazzi sto cercando una formula / funzione che prenda lat & lng come input e restituisca un riquadro di delimitazione come latmin & lngmin e latmax e latmin. Mysql, php, c #, javascript va bene ma anche lo pseudocodice dovrebbe andare bene.

Modifica: non sto cercando una soluzione che mi mostri la distanza di 2 punti


Se stai usando un geodatabase da qualche parte, sicuramente hanno un calcolo del riquadro di delimitazione integrato. Potresti anche andare a controllare la fonte di PostGIS / GEOS, ad esempio.
Vinko Vrsalovic

Risposte:


60

Suggerisco di approssimare localmente la superficie terrestre come una sfera con raggio dato dall'ellissoide WGS84 alla latitudine data. Sospetto che il calcolo esatto di latMin e latMax richiederebbe funzioni ellittiche e non produrrebbe un aumento apprezzabile della precisione (WGS84 è di per sé un'approssimazione).

Segue la mia implementazione (è scritta in Python; non l'ho testata):

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

MODIFICA: il codice seguente converte (gradi, numeri primi, secondi) in gradi + frazioni di grado e viceversa (non testato):

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

4
Come sottolineato nella documentazione della libreria CPAN suggerita, questo ha senso solo per halfSide <= 10km.
Federico A. Ramponi

1
Funziona vicino ai poli? Non sembra, perché sembra che finisca con latMin <-pi (per il polo sud) o latMax> pi (per il polo nord)? Penso che quando sei a metà lato di un palo devi restituire un riquadro di delimitazione che includa tutte le longitudini e le latitudini calcolate normalmente per il lato lontano dal palo e in corrispondenza del palo sul lato vicino al palo.
Doug McClean,

1
Ecco un'implementazione PHP dalla specifica trovata su JanMatuschek.de: github.com/anthonymartin/GeoLocation.class.php
Anthony Martin

2
Ho aggiunto un'implementazione C # di questa risposta in basso.
Ε Г И І И О

2
@ FedericoA.Ramponi che cos'è l'haldSideinKm qui? non capisco ... cosa devo passare in queste argomentazioni, il raggio tra due punti in mappa o cosa?

53

Ho scritto un articolo sulla ricerca delle coordinate di delimitazione:

http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates

L'articolo spiega le formule e fornisce anche un'implementazione Java. (Mostra anche perché la formula di Federico per la longitudine min / max non è accurata.)


4
Ho creato un port PHP della tua classe GeoLocation. Può essere trovato qui: pastie.org/5416584
Anthony Martin


1
Questo risponde anche alla domanda? Se abbiamo solo 1 punto di partenza non possiamo calcolare la distanza del cerchio grande come viene fatto in questo codice, che richiede due posizioni lat, lunghe.
mdoran3844

c'è un codice errato nella tua variante C #, ad esempio public override string ToString():, è molto brutto sovrascrivere un metodo globale di questo tipo solo per uno scopo, meglio solo aggiungere un altro metodo, quindi sovrascrivere il metodo standard, che può essere utilizzato in altre parti dell'applicazione, non per il gis esatto ...

Ecco un collegamento aggiornato al port PHP della classe GeoLocaiton di Jan: github.com/anthonymartin/GeoLocation.php
Anthony Martin

34

Qui ho convertito la risposta di Federico A. Ramponi in C # per chiunque fosse interessato:

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

1
Grazie, questo lavoro per me. Ho dovuto testare il codice a mano, non sapevo come scrivere un test unitario per questo, ma genera risultati accurati al grado di precisione di cui avevo bisogno
mdoran3844

qual è l'haldSideinKm qui? non capisco ... cosa devo passare in queste argomentazioni, il raggio tra due punti in mappa o cosa?

@GeloVolro: questa è la mezza lunghezza del riquadro di delimitazione che desideri.
Ε Г И І И О

1
Non devi necessariamente scrivere la tua classe MapPoint. C'è una classe GeoCoordinate in System.Device.Location che accetta Latitude e Longitude come parametri.
Lawyerson

1
Funziona magnificamente. Apprezzo molto la porta C #.
Tom Larcher

10

Ho scritto una funzione JavaScript che restituisce le quattro coordinate di un riquadro di delimitazione quadrato, data una distanza e una coppia di coordinate:

'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()
  ];
};

Questo codice non funziona affatto. Voglio dire, anche dopo aver fissato i errori evidenti come minLon = void 0;e maxLon = MAX_LON;che ancora non funziona.
aroth

1
@aroth, l'ho appena provato e non ho avuto problemi. Ricorda che l' centerPointargomento è un array costituito da due coordinate. Ad esempio, getBoundingBox([42.2, 34.5], 50)- void 0è l'output di CoffeeScript per "undefined" e non influirà sulla capacità di esecuzione dei codici.
Asalisbury

questo codice non funziona. degLat.degToRadnon è una funzione
user299709

Il codice ha funzionato in Node e Chrome così com'è, finché non l'ho inserito in un progetto su cui sto lavorando e ho iniziato a ricevere degToRaderrori "non è una funzione". Non Number.prototype.ho mai scoperto il motivo, ma non è una buona idea per una funzione di utilità come questa, quindi le ho convertite in normali funzioni locali. Inoltre è importante notare che la scatola restituita è [LNG, LAT, LNG, LAT] invece di [LAT, LNG, LAT, LNG]. Ho modificato la funzione di ritorno quando l'ho usata per evitare confusione.
KernelDeimos

9

Poiché avevo bisogno di una stima molto approssimativa, quindi per filtrare alcuni documenti inutili in una query di ricerca elastica, ho utilizzato la formula seguente:

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 = km richiesti dalla posizione data. Per il tuo caso N = 10

Non preciso ma pratico.


In effetti, non preciso ma comunque utile e molto facile da implementare.
MV.

6

Stai cercando una formula ellissoide.

Il posto migliore che ho trovato per iniziare a scrivere codice è basato sulla libreria Geo :: Ellipsoid di CPAN. Ti fornisce una linea di base per creare i tuoi test e per confrontare i tuoi risultati con i suoi risultati. L'ho usato come base per una libreria simile per PHP presso il mio precedente datore di lavoro.

Geo :: Ellipsoid

Dai un'occhiata al locationmetodo. Chiamalo due volte e hai il tuo bbox.

Non hai pubblicato la lingua che stavi usando. Potrebbe essere già disponibile una libreria di geocodifica.

Oh, e se non l'hai ancora capito, Google Maps utilizza l'ellissoide WGS84.


5

Illustrazione di @Jan Philip Matuschek eccellente spiegazione (si prega di votare la sua risposta, non questa; lo aggiungo perché ho impiegato un po 'di tempo per capire la risposta originale)

La tecnica del bounding box per l'ottimizzazione della ricerca dei vicini più vicini dovrebbe derivare la latitudine minima e massima, coppie di longitudine, per un punto P a distanza d. Tutti i punti che cadono al di fuori di questi sono sicuramente a una distanza maggiore di d dal punto. Una cosa da notare qui è il calcolo della latitudine di intersezione come evidenziato nella spiegazione di Jan Philip Matuschek. La latitudine di intersezione non è alla latitudine del punto P ma leggermente sfalsata da esso. Questa è una parte spesso mancata ma importante nel determinare la longitudine di delimitazione minima e massima corretta per il punto P per la distanza d. Ciò è utile anche nella verifica.

La distanza haversine tra (latitudine dell'intersezione, longitudine alta) e (latitudine, longitudine) di P è uguale alla distanza d.

Python gist qui https://gist.github.com/alexcpn/f95ae83a7ee0293a5225

inserisci qui la descrizione dell'immagine


5

Ecco una semplice implementazione che utilizza javascript che si basa sulla conversione del grado di latitudine in km dove 1 degree latitude ~ 111.2 km .

Sto calcolando i limiti della mappa da una data latitudine e longitudine con una larghezza di 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;
}

4

Ho adattato uno script PHP che ho trovato per fare proprio questo. Puoi usarlo per trovare gli angoli di una scatola attorno a un punto (diciamo, 20 km fuori). Il mio esempio specifico è per l'API di Google Maps:

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


-1 Quello che l'OP sta cercando è: dato un punto di riferimento (lat, lon) e una distanza, trova la casella più piccola in modo che tutti i punti che sono <= "distanza" dal punto di riferimento non siano fuori dalla casella. La tua scatola ha i suoi angoli "distanti" dal punto di riferimento ed è quindi troppo piccola. Esempio: il punto che è "distanza" a nord è ben al di fuori del riquadro.
John Machin

Beh, per caso, è esattamente quello di cui avevo bisogno. Quindi grazie, anche se non risponde perfettamente a questa domanda :)
Simon Steinberger

Beh, sono contento che possa aiutare qualcuno!
Richard

1

Stavo lavorando al problema del riquadro di delimitazione come problema secondario per trovare tutti i punti all'interno del raggio SrcRad di un punto LAT, LONG statico. Ci sono stati parecchi calcoli che usano

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

per calcolare i limiti di longitudine, ma ho scoperto che questo non fornisce tutte le risposte necessarie. Perché quello che vuoi veramente fare è

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

Lo so, lo so che la risposta dovrebbe essere la stessa, ma ho scoperto che non lo era. Sembrava che non assicurandomi di fare prima (SRCrad / RadEarth) e poi dividendo per la parte Cos, stavo tralasciando alcuni punti di localizzazione.

Dopo aver ottenuto tutti i punti del riquadro di delimitazione, se si dispone di una funzione che calcola la distanza da punto a punto data la lat, è facile ottenere solo quei punti che sono un certo raggio di distanza dal punto fisso. Ecco cosa ho fatto. So che ci sono voluti alcuni passaggi in più, ma mi ha aiutato

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

0

È molto semplice, basta andare sul sito Web di panoramio e quindi aprire la mappa del mondo dal sito Web di panoramio, quindi andare nella posizione specificata che è richiesta dalla latitudine e dalla longitudine.

Quindi hai trovato latitudine e longitudine nella barra degli indirizzi, ad esempio in questo indirizzo.

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

lt = 32.739485 => latitudine ln = 70.491211 => longitudine

questo widget dell'API JavaScript di Panoramio crea un riquadro di delimitazione attorno a una coppia latitudine / longitudine e quindi restituisce tutte le foto con quei limiti.

Un altro tipo di widget API JavaScript Panoramio in cui puoi anche cambiare il colore di sfondo con esempio e codice è qui .

Non si mostra nello stato d'animo di composizione. Mostra dopo la pubblicazione.

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

0

Qui ho convertito la risposta di Federico A. Ramponi in PHP se qualcuno è interessato:

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

0

Grazie @Fedrico A. per l'implementazione di Phyton, l'ho portato in una classe di categoria Objective C. Qui è:

#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

L'ho testato e sembra funzionare bene. Struct BoundsLocation dovrebbe essere sostituito da una classe, l'ho usato solo per condividerlo qui.


0

Tutte le risposte precedenti sono solo parzialmente corrette . Specialmente in regioni come l'Australia, includono sempre i pali e calcolano un rettangolo molto grande anche per 10 km.

Specialmente l'algoritmo di Jan Philip Matuschek su http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex includeva un rettangolo molto grande da (-37, -90, -180, 180) per quasi tutti i punti dell'Australia. Ciò colpisce un grande numero di utenti nel database e la distanza deve essere calcolata per tutti gli utenti in quasi metà del paese.

Ho scoperto che l' algoritmo Drupal API Earth del Rochester Institute of Technology funziona meglio intorno al palo e altrove ed è molto più facile da implementare.

https://www.rit.edu/drupal/api/drupal/sites%21all%21modules%21location%21earth.inc/7.54

Utilizzare earth_latitude_rangee earth_longitude_rangedall'algoritmo di cui sopra per il calcolo del rettangolo di delimitazione

E usa la formula di calcolo della distanza documentata da google maps per calcolare la distanza

https://developers.google.com/maps/solutions/store-locator/clothing-store-locator#outputting-data-as-xml-using-php

Per cercare per chilometri invece che per miglia, sostituire 3959 con 6371. Per (Lat, Lng) = (37, -122) e una tabella Markers con colonne lat e lng , la formula è:

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;

Leggi la mia risposta dettagliata su https://stackoverflow.com/a/45950426/5076414


0

Ecco la risposta di Federico Ramponi in Go. Nota: nessun controllo degli errori :(

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),
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.