Schnellster Weg, um die Entfernung zwischen zwei Lat / Long-Punkten zu finden


227

Ich habe derzeit knapp eine Million Standorte in einer MySQL-Datenbank, alle mit Längen- und Breitengradinformationen.

Ich versuche, den Abstand zwischen einem Punkt und vielen anderen Punkten über eine Abfrage zu ermitteln. Es ist nicht so schnell, wie ich es mir wünsche, besonders bei mehr als 100 Treffern pro Sekunde.

Gibt es dafür eine schnellere Abfrage oder möglicherweise ein schnelleres System als MySQL? Ich benutze diese Abfrage:

SELECT 
  name, 
   ( 3959 * acos( cos( radians(42.290763) ) * cos( radians( locations.lat ) ) 
   * cos( radians(locations.lng) - radians(-71.35368)) + sin(radians(42.290763)) 
   * sin( radians(locations.lat)))) AS distance 
FROM locations 
WHERE active = 1 
HAVING distance < 10 
ORDER BY distance;

Hinweis: Die angegebene Entfernung ist in Meilen angegeben . Wenn Sie Kilometer benötigen , verwenden Sie 6371anstelle von 3959.


31
Die Formel, die Sie geben, scheint viele Elemente zu haben, die konstant sind. Ist es möglich, Daten vorab zu berechnen und diese Werte auch in Ihrer Datenbank zu speichern? Zum Beispiel ist 3959 * acos (cos (Bogenmaß (42.290763)) eine Konstante, enthält aber 4 Hauptberechnungen. Könnten Sie stattdessen einfach 6696.7837 speichern?
Peter M

1
Oder zumindest Konstanten außerhalb der Abfrage vorberechnen? Das wird die Arbeit reduzieren, die erledigt werden muss.
Peter M

2
@Peter M Es ist wahrscheinlich, dass jede anständige SQL-Datenbank so optimiert wird, dass sie nur einmal berechnet wurde.
Mhenry1384

25
Für diejenigen, die sich fragen, ist 42.290763 der Breitengrad und -71.35368 der Längengrad des Punktes, von dem aus die Entfernungen berechnet werden sollen.
user276648

14
Nur zur Information, die durch diese Formel berechnete Entfernung wird in Meilen und nicht in Kilometern angegeben. Bitte ersetzen Sie 3959 durch 6371, um Ergebnisse in Kilometern zu erhalten
Sahil

Antworten:


115
  • Erstellen Sie Ihre Punkte mit PointWerten von GeometryDatentypen in der MyISAMTabelle. Ab MySQL 5.7.5 unterstützen InnoDBTabellen jetzt auch SPATIALIndizes.

  • Erstellen Sie einen SPATIALIndex für diese Punkte

  • Verwenden Sie MBRContains(), um die Werte zu finden:

    SELECT  *
    FROM    table
    WHERE   MBRContains(LineFromText(CONCAT(
            '('
            , @lon + 10 / ( 111.1 / cos(RADIANS(@lon)))
            , ' '
            , @lat + 10 / 111.1
            , ','
            , @lon - 10 / ( 111.1 / cos(RADIANS(@lat)))
            , ' '
            , @lat - 10 / 111.1 
            , ')' )
            ,mypoint)

oder in MySQL 5.1und über:

    SELECT  *
    FROM    table
    WHERE   MBRContains
                    (
                    LineString
                            (
                            Point (
                                    @lon + 10 / ( 111.1 / COS(RADIANS(@lat))),
                                    @lat + 10 / 111.1
                                  ),
                            Point (
                                    @lon - 10 / ( 111.1 / COS(RADIANS(@lat))),
                                    @lat - 10 / 111.1
                                  ) 
                            ),
                    mypoint
                    )

Dadurch werden alle Punkte ungefähr innerhalb des Felds ausgewählt (@lat +/- 10 km, @lon +/- 10km).

Dies ist eigentlich keine Box, sondern ein kugelförmiges Rechteck: Breiten- und Längengrad-gebundenes Segment der Kugel. Dies mag sich von einem einfachen Rechteck im Franz-Joseph-Land unterscheiden , ist aber an den meisten bewohnten Orten ziemlich nahe daran.

  • Wenden Sie zusätzliche Filterung an, um alles innerhalb des Kreises auszuwählen (nicht das Quadrat).

  • Wenden Sie möglicherweise eine zusätzliche Feinfilterung an, um den großen Kreisabstand zu berücksichtigen (für große Entfernungen).


15
@Quassnoi: Ein paar Korrekturen: Sie möchten wahrscheinlich die Reihenfolge der Koordinaten auf lat, long ändern. Auch sind Längsabstände proportional zum Kosinus des Breitengrads und nicht des Längengrads. Und Sie möchten es von Multiplikation zu Division ändern, damit Ihre erste Koordinate als korrigiert wird @lon - 10 / ( 111.1 / cos(@lat))(und die zweite im Paar ist, sobald alles korrekt ist).
M. Dave Auayan

8
WARNUNG : Der Text der Antwort wurde NICHT bearbeitet, um dem sehr gültigen Kommentar von @M zu entsprechen. Dave Auayan. Weitere Hinweise: Diese Methode ist birnenförmig, wenn der interessierende Kreis (a) einen Pol enthält oder (b) vom +/- 180-Grad-Längenmeridian geschnitten wird. Auch die Verwendung cos(lon)ist nur für kleinere Entfernungen genau. Siehe janmatuschek.de/LatitudeLongitudeBoundingCoordinates
John Machin

3
Gibt es eine Möglichkeit, einen Einblick in die Repräsentanten der Konstanten (10, 111.11, @lat, @lon, mypoint) zu bekommen? Ich gehe davon aus, dass die 10 für Kilometer Entfernung steht, @lat und @lon repräsentieren das bereitgestellte Gitter und den Längengrad, aber was bedeuten 111.11 und mypoint im Beispiel?
Ashays

4
@ashays: Es gibt ungefähr 111.(1)km in einem Breitengrad. mypointist das Feld in der Tabelle, in dem die Koordinaten gespeichert sind.
Quassnoi

1
Eine weitere Fehlerkorrektur - Sie sind ein Schließen) auf der vorletzten Zeile fehlt
ina

99

Keine MySQL-spezifische Antwort, aber es verbessert die Leistung Ihrer SQL-Anweisung.

Was Sie effektiv tun, ist die Berechnung des Abstands zu jedem Punkt in der Tabelle, um festzustellen, ob er innerhalb von 10 Einheiten eines bestimmten Punkts liegt.

Bevor Sie diese SQL ausführen, können Sie vier Punkte erstellen, die eine Box mit 20 Einheiten auf einer Seite zeichnen, wobei sich Ihr Punkt in der Mitte befindet. (x1, y1). . . (x4, y4), wobei (x1, y1) ist (gegeben + 10 Einheiten, gegebenLat + 10 Einheiten). . . (GivenLong - 10 Einheiten, GivenLat -10 Einheiten). Eigentlich brauchen Sie nur zwei Punkte, oben links und unten rechts nennen sie (X1, Y1) und (X2, Y2)

Jetzt verwendet Ihre SQL-Anweisung diese Punkte, um Zeilen auszuschließen, die definitiv mehr als 10u von Ihrem angegebenen Punkt entfernt sind. Sie kann Indizes für die Breiten- und Längengrade verwenden, sodass sie um Größenordnungen schneller sind als derzeit.

z.B

select . . . 
where locations.lat between X1 and X2 
and   locations.Long between y1 and y2;

Der Box-Ansatz kann falsch positive Ergebnisse zurückgeben (Sie können Punkte in den Ecken der Box aufnehmen, die> 10u vom angegebenen Punkt entfernt sind), sodass Sie immer noch den Abstand jedes Punkts berechnen müssen. Dies wird jedoch wieder viel schneller sein, da Sie die Anzahl der zu testenden Punkte drastisch auf die Punkte in der Box beschränkt haben.

Ich nenne diese Technik "Denken in der Kiste" :)

EDIT: Kann dies in eine SQL-Anweisung eingefügt werden?

Ich habe keine Ahnung, wozu mySql oder Php in der Lage ist, sorry. Ich weiß nicht, wo der beste Ort ist, um die vier Punkte zu erstellen, oder wie sie an eine mySql-Abfrage in PHP übergeben werden könnten. Sobald Sie jedoch die vier Punkte erreicht haben, hindert Sie nichts mehr daran, Ihre eigene SQL-Anweisung mit meiner zu kombinieren.

select name, 
       ( 3959 * acos( cos( radians(42.290763) ) 
              * cos( radians( locations.lat ) ) 
              * cos( radians( locations.lng ) - radians(-71.35368) ) 
              + sin( radians(42.290763) ) 
              * sin( radians( locations.lat ) ) ) ) AS distance 
from locations 
where active = 1 
and locations.lat between X1 and X2 
and locations.Long between y1 and y2
having distance < 10 ORDER BY distance;

Ich weiß, dass ich mit MS SQL eine SQL-Anweisung erstellen kann, die vier Gleitkommazahlen deklariert (X1, Y1, X2, Y2) und diese vor der "main" select-Anweisung berechnet, wie gesagt, ich habe keine Ahnung, ob dies möglich ist MySql. Ich wäre jedoch immer noch geneigt, die vier Punkte in C # zu erstellen und sie als Parameter an die SQL-Abfrage zu übergeben.

Es tut mir leid, ich kann nicht mehr helfen. Wenn jemand die MySQL- und PHP-spezifischen Teile davon beantworten kann, können Sie diese Antwort jederzeit bearbeiten.


4
Eine MySQL-Prozedur für diesen Ansatz finden Sie in dieser Präsentation: scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
Lucia

37
Um nach Kilometern statt nach Meilen zu suchen, ersetzen Sie 3959 durch 6371.
ErichBSchulz

4
+1, großartige Option; Durch Hinzufügen der Box wurde meine Abfrage von 4 auf durchschnittlich 0,03 Sekunden reduziert.
Jvenema

1
Obwohl es so logisch erscheint, reservieren Sie eine Auszeichnung für diese Lösung! In einer 2-Millionen-Datensatzdatenbank ging die Abfrage von 16 Sekunden auf 0,06 Sekunden. Hinweis: Es ist sogar noch schneller (für große Tabellen), wenn Sie die Entfernungsberechnung aus der Abfrage herausschneiden und die Entfernungsberechnung in Ihrem Programmcode durchführen!
NLAnaconda

2
@Binary Worrier: X1, X2 und Y1, Y2 sind also Längengrad Min und Max und Breitengrad Min und Max gemäß dem hier angegebenen Beispiel: blog.fedecarg.com/2009/02/08/… Bitte geben Sie an.
Prabhat

14

Die folgende MySQL-Funktion wurde in diesem Blogbeitrag veröffentlicht . Ich habe es nicht viel getestet, aber nach dem, was ich aus dem Beitrag erfahren habe , kann dies für Sie gut funktionieren , wenn Ihre Längen- und Breitengrade indiziert sind :

DELIMITER $$

DROP FUNCTION IF EXISTS `get_distance_in_miles_between_geo_locations` $$
CREATE FUNCTION get_distance_in_miles_between_geo_locations(
  geo1_latitude decimal(10,6), geo1_longitude decimal(10,6), 
  geo2_latitude decimal(10,6), geo2_longitude decimal(10,6)) 
returns decimal(10,3) DETERMINISTIC
BEGIN
  return ((ACOS(SIN(geo1_latitude * PI() / 180) * SIN(geo2_latitude * PI() / 180) 
    + COS(geo1_latitude * PI() / 180) * COS(geo2_latitude * PI() / 180) 
    * COS((geo1_longitude - geo2_longitude) * PI() / 180)) * 180 / PI()) 
    * 60 * 1.1515);
END $$

DELIMITER ;

Beispielnutzung:

Angenommen, eine Tabelle placesmit den Feldern latitude& longitude:

SELECT get_distance_in_miles_between_geo_locations(-34.017330, 22.809500,
latitude, longitude) AS distance_from_input FROM places;

Ich habe es versucht und es funktioniert perfekt, aber irgendwie erlaubt es mir nicht, eine WHERE-Anweisung basierend auf distance_from_input einzugeben. Irgendeine Idee warum nicht?
Chris Visser

Sie können dies als Unterauswahl tun: Wählen Sie * aus (...) als t, wobei distance_from_input> 5;
Brad Parks

2
oder gehen Sie einfach geradeaus mit: Wählen Sie * aus Stellen aus, an denen get_distance_in_miles_between_geo_locations (-34.017330, 22.809500, Breite, Länge)> 5000;
Brad Parks

2
Rückgabe Meter:SELECT ROUND(((ACOS(SIN(lat1 * PI() / 180) * SIN(lat2 * PI() / 180) + COS(lat1 * PI() / 180) * COS(lat2 * PI() / 180) * COS((lnt1 - lnt2) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) * 1.609344 * 1000) AS distance
Mohammad

13

Ich musste ein ähnliches Problem lösen (Zeilen nach Abstand von einem Punkt filtern) und durch die Kombination der ursprünglichen Frage mit Antworten und Kommentaren eine Lösung finden, die sowohl für MySQL 5.6 als auch für 5.7 perfekt funktioniert.

SELECT 
    *,
    (6371 * ACOS(COS(RADIANS(56.946285)) * COS(RADIANS(Y(coordinates))) 
    * COS(RADIANS(X(coordinates)) - RADIANS(24.105078)) + SIN(RADIANS(56.946285))
    * SIN(RADIANS(Y(coordinates))))) AS distance
FROM places
WHERE MBRContains
    (
    LineString
        (
        Point (
            24.105078 + 15 / (111.320 * COS(RADIANS(56.946285))),
            56.946285 + 15 / 111.133
        ),
        Point (
            24.105078 - 15 / (111.320 * COS(RADIANS(56.946285))),
            56.946285 - 15 / 111.133
        )
    ),
    coordinates
    )
HAVING distance < 15
ORDER By distance

coordinatesDieses Feld mit Typ POINTund SPATIALIndex
6371dient zur Berechnung der Entfernung in Kilometern. Der
56.946285Breitengrad für den Mittelpunkt
24.105078ist der Längengrad für den Mittelpunkt. Der
15maximale Abstand in Kilometern

In meinen Tests verwendet MySQL den SPATIAL-Index für das coordinatesFeld, um schnell alle Zeilen innerhalb des Rechtecks ​​auszuwählen, und berechnet dann den tatsächlichen Abstand für alle gefilterten Stellen, um Orte von Rechteckecken auszuschließen und nur Stellen innerhalb des Kreises zu belassen .

Dies ist eine Visualisierung meines Ergebnisses:

Karte

Graue Sterne visualisieren alle Punkte auf der Karte, gelbe Sterne werden von der MySQL-Abfrage zurückgegeben. Graue Sterne innerhalb der Ecken des Rechtecks ​​(aber außerhalb des Kreises) wurden MBRContains()durch eine HAVINGKlausel ausgewählt und dann durch eine Klausel abgewählt .


Kann das nicht genug bewerten. Wenn Sie mit dieser Methode eine Tabelle mit ca. 5 Millionen Datensätzen und einem räumlichen Index durchsuchen, beträgt die Suchzeit auf einem alten A8-Prozessor 0,005 Sekunden. Ich weiß, dass 6371 durch 3959 ersetzt werden kann, um Ergebnisse in Meilen zu erhalten, aber müssen die Werte von 111.133 und 111.320 angepasst werden oder sind sie universell konstant?
Wranorn

Tolle Lösung.
SeaBiscuit

So erstellen Sie einen Punkt: POINT (lat, lng) oder POINT (lng, lat)
user606669

2
@ user606669 Es ist POINT (lng, lat)
Māris Kiseļovs

Die Funktionen X () und Y () sollten heutzutage ST_Y und ST_X sein.
Andreas

11

Wenn Sie MySQL 5.7. * verwenden, können Sie st_distance_sphere (POINT, POINT) verwenden .

Select st_distance_sphere(POINT(-2.997065, 53.404146 ), POINT(58.615349, 23.56676 ))/1000  as distcance

1
Dies ist eine sehr gute und leicht zu lesende Alternative. Beachten Sie, dass die Parameterreihenfolge für POINT () (lng, lat) ist, da Sie sonst möglicherweise "close" erhalten, aber immer noch sehr unterschiedliche Ergebnisse zu den anderen Methoden hier erzielen. siehe: stackoverflow.com/questions/35939853/…
Andy P

9
SELECT * FROM (SELECT *,(((acos(sin((43.6980168*pi()/180)) * 
sin((latitude*pi()/180))+cos((43.6980168*pi()/180)) * 
cos((latitude*pi()/180)) * cos(((7.266903899999988- longitude)* 
pi()/180))))*180/pi())*60*1.1515 ) as distance 
FROM wp_users WHERE 1 GROUP BY ID limit 0,10) as X 
ORDER BY ID DESC

Dies ist die Abfrage zur Entfernungsberechnung zwischen Punkten in MySQL. Ich habe sie in einer langen Datenbank verwendet. Sie funktioniert perfekt! Hinweis: Nehmen Sie die Änderungen (Datenbankname, Tabellenname, Spalte usw.) gemäß Ihren Anforderungen vor.


Was bedeutet der Wert 1.1515? Ich habe schon eine ähnliche Formel gesehen, aber sie verwendete 1,75 anstelle von 1,1515.
TryHarder

1
Als Antwort auf meine eigene Frage denke ich, dass die Antwort hier liegen könnte stackoverflow.com/a/389251/691053
TryHarder

8
set @latitude=53.754842;
set @longitude=-2.708077;
set @radius=20;

set @lng_min = @longitude - @radius/abs(cos(radians(@latitude))*69);
set @lng_max = @longitude + @radius/abs(cos(radians(@latitude))*69);
set @lat_min = @latitude - (@radius/69);
set @lat_max = @latitude + (@radius/69);

SELECT * FROM postcode
WHERE (longitude BETWEEN @lng_min AND @lng_max)
AND (latitude BETWEEN @lat_min and @lat_max);

Quelle


11
Bitte zitieren Sie Ihre Quellen. Dies ist von: blog.fedecarg.com/2009/02/08/…
Redburn

Was ist 69 in diesem Fall? Wie geht es, wenn wir den Erdradius haben?
CodeRunner

2
Kilometer in 1 Latittude ist 111 km. Meile in 1 Latittude ist 69 Meilen. und 69 Meilen = 111 km. Deshalb haben wir die Parameter in den Konvertierungen verwendet.
CodeRunner

Ich hatte schon immer danach gesucht. Wusste nicht, dass es so einfach sein kann. Ich danke dir sehr.
Vikas

Wäre das nicht falsch, da lng_min / lng_max lat_min und lat_max in der Radiusmathematik verwenden müsste?
Ben

6
   select
   (((acos(sin(('$latitude'*pi()/180)) * sin((`lat`*pi()/180))+cos(('$latitude'*pi()/180)) 
    * cos((`lat`*pi()/180)) * cos((('$longitude'- `lng`)*pi()/180))))*180/pi())*60*1.1515) 
    AS distance
    from table having distance<22;

5

Eine MySQL-Funktion, die die Anzahl der Meter zwischen den beiden Koordinaten zurückgibt:

CREATE FUNCTION DISTANCE_BETWEEN (lat1 DOUBLE, lon1 DOUBLE, lat2 DOUBLE, lon2 DOUBLE)
RETURNS DOUBLE DETERMINISTIC
RETURN ACOS( SIN(lat1*PI()/180)*SIN(lat2*PI()/180) + COS(lat1*PI()/180)*COS(lat2*PI()/180)*COS(lon2*PI()/180-lon1*PI()/180) ) * 6371000

Um den Wert in einem anderen Format zurückzugeben, ersetzen Sie die 6371000Funktion in der Funktion durch den Erdradius in der von Ihnen gewählten Einheit. Zum Beispiel wären Kilometer 6371und Meilen 3959.

Um die Funktion zu verwenden, rufen Sie sie einfach wie jede andere Funktion in MySQL auf. Wenn Sie beispielsweise einen Tisch hätten city, könnten Sie die Entfernung zwischen jeder Stadt und jeder anderen Stadt ermitteln:

SELECT
    `city1`.`name`,
    `city2`.`name`,
    ROUND(DISTANCE_BETWEEN(`city1`.`latitude`, `city1`.`longitude`, `city2`.`latitude`, `city2`.`longitude`)) AS `distance`
FROM
    `city` AS `city1`
JOIN
    `city` AS `city2`

4

Der vollständige Code mit Details zur Installation als MySQL-Plugin finden Sie hier: https://github.com/lucasepe/lib_mysqludf_haversine

Ich habe dies letztes Jahr als Kommentar gepostet. Da mir freundlicherweise @TylerCollier vorgeschlagen hat, als Antwort zu posten, hier ist es.

Eine andere Möglichkeit besteht darin, eine benutzerdefinierte UDF-Funktion zu schreiben, die den Haversine-Abstand von zwei Punkten zurückgibt. Diese Funktion kann Eingaben aufnehmen:

lat1 (real), lng1 (real), lat2 (real), lng2 (real), type (string - optinal - 'km', 'ft', 'mi')

Also können wir so etwas schreiben:

SELECT id, name FROM MY_PLACES WHERE haversine_distance(lat1, lng1, lat2, lng2) < 40;

alle Datensätze mit einer Entfernung von weniger als 40 Kilometern abzurufen. Oder:

SELECT id, name FROM MY_PLACES WHERE haversine_distance(lat1, lng1, lat2, lng2, 'ft') < 25;

um alle Datensätze mit einer Entfernung von weniger als 25 Fuß abzurufen.

Die Kernfunktion ist:

double
haversine_distance( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error ) {
    double result = *(double*) initid->ptr;
    /*Earth Radius in Kilometers.*/ 
    double R = 6372.797560856;
    double DEG_TO_RAD = M_PI/180.0;
    double RAD_TO_DEG = 180.0/M_PI;
    double lat1 = *(double*) args->args[0];
    double lon1 = *(double*) args->args[1];
    double lat2 = *(double*) args->args[2];
    double lon2 = *(double*) args->args[3];
    double dlon = (lon2 - lon1) * DEG_TO_RAD;
    double dlat = (lat2 - lat1) * DEG_TO_RAD;
    double a = pow(sin(dlat * 0.5),2) + 
        cos(lat1*DEG_TO_RAD) * cos(lat2*DEG_TO_RAD) * pow(sin(dlon * 0.5),2);
    double c = 2.0 * atan2(sqrt(a), sqrt(1-a));
    result = ( R * c );
    /*
     * If we have a 5th distance type argument...
     */
    if (args->arg_count == 5) {
        str_to_lowercase(args->args[4]);
        if (strcmp(args->args[4], "ft") == 0) result *= 3280.8399;
        if (strcmp(args->args[4], "mi") == 0) result *= 0.621371192;
    }

    return result;
}

3

Eine schnelle, einfache und genaue Annäherung (für kleinere Entfernungen) kann mit einer sphärischen Projektion erfolgen . Zumindest in meinem Routing-Algorithmus bekomme ich einen 20% igen Boost im Vergleich zur korrekten Berechnung. Im Java-Code sieht es so aus:

public double approxDistKm(double fromLat, double fromLon, double toLat, double toLon) {
    double dLat = Math.toRadians(toLat - fromLat);
    double dLon = Math.toRadians(toLon - fromLon);
    double tmp = Math.cos(Math.toRadians((fromLat + toLat) / 2)) * dLon;
    double d = dLat * dLat + tmp * tmp;
    return R * Math.sqrt(d);
}

Ich bin mir nicht sicher über MySQL (sorry!).

Stellen Sie sicher, dass Sie die Einschränkung kennen (der dritte Parameter von assertEquals bedeutet die Genauigkeit in Kilometern):

    float lat = 24.235f;
    float lon = 47.234f;
    CalcDistance dist = new CalcDistance();
    double res = 15.051;
    assertEquals(res, dist.calcDistKm(lat, lon, lat - 0.1, lon + 0.1), 1e-3);
    assertEquals(res, dist.approxDistKm(lat, lon, lat - 0.1, lon + 0.1), 1e-3);

    res = 150.748;
    assertEquals(res, dist.calcDistKm(lat, lon, lat - 1, lon + 1), 1e-3);
    assertEquals(res, dist.approxDistKm(lat, lon, lat - 1, lon + 1), 1e-2);

    res = 1527.919;
    assertEquals(res, dist.calcDistKm(lat, lon, lat - 10, lon + 10), 1e-3);
    assertEquals(res, dist.approxDistKm(lat, lon, lat - 10, lon + 10), 10);


3

Lesen Sie Geo Distance Search mit MySQL , einer Lösung, die auf der Implementierung von Haversine Formula in MySQL basiert. Dies ist eine vollständige Lösungsbeschreibung mit Theorie, Implementierung und weiterer Leistungsoptimierung. Obwohl der räumliche Optimierungsteil in meinem Fall nicht richtig funktioniert hat.

Ich habe dabei zwei Fehler bemerkt:

  1. die Verwendung von absin der select-Anweisung auf Seite 8. Ich habe es einfach weggelassen absund es hat funktioniert.

  2. Die räumliche Suchentfernungsfunktion auf p27 konvertiert nicht in Bogenmaß oder multipliziert den Längengrad mit cos(latitude), es sei denn, seine räumlichen Daten werden mit dieser Berücksichtigung geladen (kann nicht aus dem Kontext des Artikels entnommen werden), aber sein Beispiel auf p26 zeigt an, dass seine räumlichen Daten POINTnicht mit geladen sind Bogenmaß oder Grad.


0
$objectQuery = "SELECT table_master.*, ((acos(sin((" . $latitude . "*pi()/180)) * sin((`latitude`*pi()/180))+cos((" . $latitude . "*pi()/180)) * cos((`latitude`*pi()/180)) * cos(((" . $longitude . "- `longtude`)* pi()/180))))*180/pi())*60*1.1515  as distance FROM `table_post_broadcasts` JOIN table_master ON table_post_broadcasts.master_id = table_master.id WHERE table_master.type_of_post ='type' HAVING distance <='" . $Radius . "' ORDER BY distance asc";

0

Mit MySQL

SET @orig_lon = 1.027125;
SET @dest_lon = 1.027125;

SET @orig_lat = 2.398441;
SET @dest_lat = 2.398441;

SET @kmormiles = 6371;-- for distance in miles set to : 3956

SELECT @kmormiles * ACOS(LEAST(COS(RADIANS(@orig_lat)) * 
 COS(RADIANS(@dest_lat)) * COS(RADIANS(@orig_lon - @dest_lon)) + 
 SIN(RADIANS(@orig_lat)) * SIN(RADIANS(@dest_lat)),1.0)) as distance;

Siehe: https://andrew.hedges.name/experiments/haversine/

Siehe: https://stackoverflow.com/a/24372831/5155484

Siehe: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/

HINWEIS: Wird LEASTverwendet, um Nullwerte zu vermeiden, da ein Kommentar unter https://stackoverflow.com/a/24372831/5155484 vorgeschlagen wird

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.