Wie berechnet man den Begrenzungsrahmen für einen bestimmten Lat / Lng-Standort?


101

Ich habe einen Ort angegeben, der durch Breite und Länge definiert ist. Jetzt möchte ich einen Begrenzungsrahmen innerhalb von z. B. 10 Kilometern von diesem Punkt berechnen.

Der Begrenzungsrahmen sollte als latmin, lngmin und latmax, lngmax definiert werden.

Ich brauche dieses Zeug, um die Panoramio-API zu verwenden .

Kennt jemand die Formel, wie man diese Punkte bekommt?

Bearbeiten: Leute, ich suche nach einer Formel / Funktion, die lat & lng als Eingabe verwendet und einen Begrenzungsrahmen als latmin & lngmin und latmax & latmin zurückgibt. MySQL, PHP, C #, Javascript ist in Ordnung, aber auch Pseudocode sollte in Ordnung sein.

Bearbeiten: Ich suche keine Lösung, die mir den Abstand von 2 Punkten anzeigt


Wenn Sie irgendwo eine Geodatabase verwenden, ist sicherlich eine Begrenzungsrahmenberechnung integriert. Sie können beispielsweise sogar die Quelle von PostGIS / GEOS überprüfen.
Vinko Vrsalovic

Antworten:


60

Ich schlage vor, die Erdoberfläche lokal als Kugel mit einem Radius zu approximieren, der durch das WGS84-Ellipsoid bei dem angegebenen Breitengrad angegeben wird. Ich vermute, dass die genaue Berechnung von latMin und latMax elliptische Funktionen erfordern und keine nennenswerte Erhöhung der Genauigkeit ergeben würde (WGS84 ist selbst eine Annäherung).

Meine Implementierung folgt (Es ist in Python geschrieben; ich habe es nicht getestet):

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

BEARBEITEN: Der folgende Code konvertiert (Grad, Primzahlen, Sekunden) in Grad + Bruchteile eines Grades und umgekehrt (nicht getestet):

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
Wie in der Dokumentation der vorgeschlagenen CPAN-Bibliothek ausgeführt, ist dies nur für halfSide <= 10 km sinnvoll.
Federico A. Ramponi

1
Funktioniert das in der Nähe der Pole? Es scheint nicht so, weil es so aussieht, als würde es mit latMin <-pi (für den Südpol) oder latMax> pi (für den Nordpol) enden? Ich denke, wenn Sie sich innerhalb der halben Seite einer Stange befinden, müssen Sie einen Begrenzungsrahmen zurückgeben, der alle Längen- und Breitengrade enthält, die normalerweise für die Seite berechnet werden, die von der Stange entfernt ist, und an der Stange auf der Seite in der Nähe der Stange.
Doug McClean

1
Hier ist eine PHP-Implementierung aus der Spezifikation auf JanMatuschek.de: github.com/anthonymartin/GeoLocation.class.php
Anthony Martin

2
Ich habe unten eine C # -Implementierung dieser Antwort hinzugefügt.
Г И І І О

2
@ FedericoA.Ramponi was ist der haldSideinKm hier? verstehe nicht ... was muss ich in diesen Argumenten passieren, den Radius zwischen zwei Punkten in der Karte oder was?

53

Ich habe einen Artikel über das Finden der Begrenzungskoordinaten geschrieben:

http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates

Der Artikel erklärt die Formeln und bietet auch eine Java-Implementierung. (Es zeigt auch, warum Federicos Formel für die minimale / maximale Länge ungenau ist.)


4
Ich habe einen PHP-Port Ihrer GeoLocation-Klasse erstellt. Es kann hier gefunden werden: pastie.org/5416584
Anthony Martin

1
Ich habe es jetzt auf github hochgeladen: github.com/anthonymartin/GeoLocation.class.php
Anthony Martin

1
Beantwortet dies überhaupt die Frage? Wenn wir nur 1 Startpunkt haben, können wir die Großkreisentfernung nicht wie in diesem Code berechnet berechnen, was zwei lat, lange Positionen erfordert.
mdoran3844

Ihre C # -Variante enthält einen fehlerhaften Code, z. B.: public override string ToString()Es ist sehr schlecht, eine solche globale Methode nur für einen Zweck zu überschreiben, besser nur eine andere Methode hinzuzufügen und dann die Standardmethode zu überschreiben, die in anderen Teilen der Anwendung verwendet werden kann. nicht für die gis genau ...

Hier ist ein aktualisierter Link zum PHP-Port der GeoLocaiton-Klasse von Jan: github.com/anthonymartin/GeoLocation.php
Anthony Martin

34

Hier habe ich die Antwort von Federico A. Ramponi auf C # für alle Interessierten konvertiert:

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
Danke, diese Arbeit für mich. Musste den Code von Hand testen, wusste nicht, wie man einen Unit-Test dafür schreibt, aber es generiert genaue Ergebnisse mit dem Grad an Genauigkeit, den ich brauche
mdoran3844

Was ist der haldSideinKm hier? verstehe nicht ... was muss ich in diesen Argumenten passieren, den Radius zwischen zwei Punkten in der Karte oder was?

@GeloVolro: Das ist die halbe Länge des gewünschten Begrenzungsrahmens.
О Г И І І О

1
Sie müssen nicht unbedingt Ihre eigene MapPoint-Klasse schreiben. In System.Device.Location gibt es eine GeoCoordinate-Klasse, die Latitude und Longitude als Parameter verwendet.
Anwalt

1
Das funktioniert wunderbar. Ich schätze den C # -Port sehr.
Tom Larcher

10

Ich habe eine JavaScript-Funktion geschrieben, die die vier Koordinaten eines quadratischen Begrenzungsrahmens bei gegebenem Abstand und zwei Koordinaten zurückgibt:

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

Dieser Code funktioniert überhaupt nicht. Ich meine, auch nach dem Beheben der offensichtlichen Fehler wie minLon = void 0;und maxLon = MAX_LON;es funktioniert immer noch nicht.
Uhr

1
@aroth, ich habe es gerade getestet und hatte kein Problem. Denken Sie daran, dass das centerPointArgument ein Array ist, das aus zwei Koordinaten besteht. Beispiel: getBoundingBox([42.2, 34.5], 50)- void 0ist die CoffeeScript-Ausgabe für "undefiniert" und hat keinen Einfluss auf die Ausführungsfähigkeit des Codes.
Asalisbury

Dieser Code funktioniert nicht. degLat.degToRadist keine Funktion
user299709

Code funktionierte in Node und Chrome unverändert, bis ich ihn in ein Projekt einfügte, an dem ich gerade arbeite, und degToRadFehler "Ist keine Funktion" erhielt. Ich habe nie herausgefunden warum, bin aber Number.prototype.keine gute Idee für eine solche Dienstprogrammfunktion, deshalb habe ich diese in normale lokale Funktionen konvertiert. Es ist auch wichtig zu beachten, dass die zurückgegebene Box [LNG, LAT, LNG, LAT] anstelle von [LAT, LNG, LAT, LNG] ist. Ich habe die Rückgabefunktion geändert, als ich diese verwendet habe, um Verwirrung zu vermeiden.
KernelDeimos

9

Da ich eine sehr grobe Schätzung benötigte, um einige unnötige Dokumente in einer Elasticsearch-Abfrage herauszufiltern, habe ich die folgende Formel verwendet:

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 erforderlich vom angegebenen Ort. Für Ihren Fall ist N = 10

Nicht genau, aber praktisch.


In der Tat nicht genau, aber dennoch nützlich und sehr einfach zu implementieren.
MV.

6

Sie suchen nach einer Ellipsoidformel.

Der beste Ort, an dem ich mit dem Codieren beginnen konnte, basiert auf der Geo :: Ellipsoid-Bibliothek von CPAN. Sie erhalten eine Basis, aus der Sie Ihre Tests erstellen und Ihre Ergebnisse mit den Ergebnissen vergleichen können. Ich habe es als Grundlage für eine ähnliche Bibliothek für PHP bei meinem früheren Arbeitgeber verwendet.

Geo :: Ellipsoid

Schauen Sie sich die locationMethode an. Rufen Sie es zweimal an und Sie haben Ihre bbox.

Sie haben nicht angegeben, welche Sprache Sie verwendet haben. Möglicherweise ist bereits eine Geokodierungsbibliothek für Sie verfügbar.

Oh, und wenn Sie es bis jetzt noch nicht herausgefunden haben, verwendet Google Maps das Ellipsoid WGS84.


5

Illustration von @Jan Philip Matuschek ausgezeichnete Erklärung. (Bitte stimmen Sie seine Antwort ab, nicht diese; ich füge dies hinzu, da ich mir etwas Zeit genommen habe, um die ursprüngliche Antwort zu verstehen.)

Die Bounding-Box-Technik zur Optimierung der Suche nach nächsten Nachbarn müsste die minimalen und maximalen Breiten- und Längengrade für einen Punkt P im Abstand d ableiten. Alle Punkte, die außerhalb dieser Punkte liegen, befinden sich definitiv in einem Abstand von mehr als d vom Punkt. Eine Sache, die hier zu beachten ist, ist die Berechnung des Breitengrads der Kreuzung, wie in der Erklärung von Jan Philip Matuschek hervorgehoben. Der Schnittpunkt liegt nicht auf dem Punkt P, sondern ist leicht versetzt dazu. Dies ist ein häufig übersehener, aber wichtiger Teil bei der Bestimmung der korrekten minimalen und maximalen Grenzlänge für Punkt P für den Abstand d. Dies ist auch bei der Überprüfung nützlich.

Der Haversine-Abstand zwischen (Schnittbreite, Länge hoch) und (Breite, Länge) von P ist gleich der Entfernung d.

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

Geben Sie hier die Bildbeschreibung ein


5

Hier ist eine einfache Implementierung mit Javascript, die auf der Umrechnung des Breitengrads in km basiert, wobei 1 degree latitude ~ 111.2 km.

Ich berechne die Grenzen der Karte aus einem bestimmten Breiten- und Längengrad mit einer Breite von 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

Ich habe ein PHP-Skript angepasst, das ich gefunden habe, um genau dies zu tun. Sie können es verwenden, um die Ecken einer Box um einen Punkt herum zu finden (z. B. 20 km entfernt). Mein spezielles Beispiel ist für die Google Maps-API:

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


-1 Was das OP sucht, ist: Wenn Sie einen Referenzpunkt (lat, lon) und eine Entfernung haben, suchen Sie die kleinste Box, sodass alle Punkte, die <= "Entfernung" vom Referenzpunkt entfernt sind, nicht außerhalb der Box liegen. Ihre Box hat ihre Ecken "Abstand" vom Referenzpunkt entfernt und ist daher zu klein. Beispiel: Der Punkt "Entfernung" genau nach Norden liegt weit außerhalb Ihrer Box.
John Machin

Zufällig ist es genau das, was ich gerade brauchte. Also danke, auch wenn es diese Frage nicht ganz beantwortet :)
Simon Steinberger

Nun, ich bin froh, dass es jemandem helfen konnte!
Richard

1

Ich habe an dem Bounding-Box-Problem als Nebenproblem gearbeitet, um alle Punkte innerhalb des SrcRad-Radius eines statischen LAT, LONG-Punkts zu finden. Es gab einige Berechnungen, die verwendet wurden

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

um die Längengrade zu berechnen, aber ich fand, dass dies nicht alle Antworten gab, die benötigt wurden. Denn was Sie wirklich tun wollen, ist

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

Ich weiß, ich weiß, dass die Antwort dieselbe sein sollte, aber ich fand, dass es nicht so war. Es stellte sich heraus, dass ich einige Positionspunkte ausließ, indem ich nicht sicher war, ob ich zuerst das (SRCrad / RadEarth) machte und dann durch den Cos-Teil teilte.

Nachdem Sie alle Ihre Begrenzungsrahmenpunkte erhalten haben und eine Funktion haben, die den Punkt-zu-Punkt-Abstand berechnet, ist es einfach, nur die Punkte zu erhalten, die einen bestimmten Abstandsradius vom festen Punkt haben. Hier ist was ich getan habe. Ich weiß, dass es ein paar zusätzliche Schritte gedauert hat, aber es hat mir geholfen

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

Es ist sehr einfach, gehen Sie einfach zur Panoramio-Website und öffnen Sie dann die Weltkarte von der Panoramio-Website. Gehen Sie dann zu dem angegebenen Ort, für den Breite und Länge erforderlich sind.

Dann haben Sie Breiten- und Längengrade in der Adressleiste gefunden, zum Beispiel in dieser Adresse.

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

lt = 32.739485 => Breite ln = 70.491211 => Länge

Dieses Panoramio JavaScript API-Widget erstellt einen Begrenzungsrahmen um ein Lat / Long-Paar und gibt dann alle Fotos mit diesen Grenzen zurück.

Eine andere Art von Panoramio JavaScript API-Widget, in dem Sie auch die Hintergrundfarbe mit Beispiel und Code ändern können, finden Sie hier .

Es wird nicht in der Kompositionsstimmung angezeigt. Es wird nach der Veröffentlichung angezeigt.

<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

Hier habe ich Federico A. Ramponis Antwort auf PHP konvertiert, wenn jemand interessiert ist:

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

Vielen Dank an @Fedrico A. für die Phyton-Implementierung. Ich habe sie in eine Objective C-Kategorieklasse portiert. Hier ist:

#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

Ich habe es getestet und scheint gut zu funktionieren. Struct BoundsLocation sollte durch eine Klasse ersetzt werden. Ich habe es nur verwendet, um es hier zu teilen.


0

Alle obigen Antworten sind nur teilweise richtig . Speziell in Regionen wie Australien enthalten sie immer Stangen und berechnen auch für 10 km ein sehr großes Rechteck.

Insbesondere der Algorithmus von Jan Philip Matuschek unter http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex enthielt für fast jeden Punkt in Australien ein sehr großes Rechteck von (-37, -90, -180, 180). Dies trifft einen großen Benutzer in der Datenbank und die Entfernung muss für alle Benutzer in fast der Hälfte des Landes berechnet werden.

Ich fand heraus, dass der Drupal API Earth-Algorithmus des Rochester Institute of Technology sowohl am Pol als auch anderswo besser funktioniert und viel einfacher zu implementieren ist.

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

Verwenden Sie earth_latitude_rangeund earth_longitude_rangeaus dem obigen Algorithmus zur Berechnung des Begrenzungsrechtecks

Verwenden Sie die von Google Maps dokumentierte Entfernungsberechnungsformel, um die Entfernung zu berechnen

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

Um nach Kilometern statt nach Meilen zu suchen, ersetzen Sie 3959 durch 6371. Für (Lat, Lng) = (37, -122) und eine Markertabelle mit den Spalten lat und lng lautet die Formel:

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;

Lesen Sie meine ausführliche Antwort unter https://stackoverflow.com/a/45950426/5076414


0

Hier ist Federico Ramponis Antwort in Go. Hinweis: keine Fehlerprüfung :(

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),
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.