Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Verwenden Sie räumliche MySQL-Erweiterungen, um Punkte innerhalb des Kreises auszuwählen

Es gibt keine geospatialen Erweiterungsfunktionen in MySQL, die Breiten-/Längengrad-Entfernungsberechnungen unterstützen. Gibt es ab MySQL 5.7 .

Sie fragen nach Annäherungskreisen auf der Erdoberfläche. Sie erwähnen in Ihrer Frage, dass Sie Lat/Long-Werte für jede Zeile in Ihren flags haben Tabelle und auch Universal Transversal Mercator (UTM) projizierte Werte in einer von mehreren verschiedenen UTM-Zonen . Wenn ich mich richtig an meine UK Ordnance Survey-Karten erinnere, ist UTM nützlich, um Elemente auf diesen Karten zu lokalisieren.

Es ist einfach, die Entfernung zwischen zwei Punkten in derselben Zone zu berechnen in UTM:die kartesische Distanz tut es. Aber wenn sich Punkte in verschiedenen Zonen befinden, funktioniert diese Berechnung nicht.

Dementsprechend muss für die in Ihrer Frage beschriebene Anwendung die Great Circle Distance , die mit dem Haversinus oder einer anderen geeigneten Formel berechnet wird.

MySQL, erweitert um Geodaten-Erweiterungen, unterstützt eine Möglichkeit, verschiedene planare Formen (Punkte, Polylinien, Polygone usw.) als geometrische Primitive darzustellen. MySQL 5.6 implementiert eine undokumentierte Abstandsfunktion st_distance(p1, p2) . Diese Funktion gibt jedoch kartesische Entfernungen zurück. Also völlig ungeeignet für auf Breiten- und Längengraden basierende Berechnungen. In gemäßigten Breiten überspannt ein Breitengrad fast doppelt so viel Oberflächenentfernung (Nord-Süd) wie ein Längengrad (Ost-West), weil die Breitengradlinien in der Nähe der Pole dichter zusammenwachsen.

Daher muss eine kreisförmige Näherungsformel echte Breiten- und Längengrade verwenden.

In Ihrer Anwendung finden Sie alle flags Punkte innerhalb von zehn Meilen von einem bestimmten latpoint,longpoint mit einer Abfrage wie dieser:

 SELECT id, coordinates, name, r,
        units * DEGREES(ACOS(LEAST(1.0, COS(RADIANS(latpoint))
                  * COS(RADIANS(latitude))
                  * COS(RADIANS(longpoint) - RADIANS(longitude))
                  + SIN(RADIANS(latpoint))
                  * SIN(RADIANS(latitude))))) AS distance
   FROM flags
   JOIN (
        SELECT 42.81  AS latpoint,  -70.81 AS longpoint, 
               10.0 AS r, 69.0 AS units
        ) AS p ON (1=1)
  WHERE MbrContains(GeomFromText (
        CONCAT('LINESTRING(',
              latpoint-(r/units),' ',
              longpoint-(r /(units* COS(RADIANS(latpoint)))),
              ',', 
              latpoint+(r/units) ,' ',
              longpoint+(r /(units * COS(RADIANS(latpoint)))),
              ')')),  coordinates)

Wenn Sie nach Punkten im Umkreis von 20 km suchen möchten, ändern Sie diese Zeile der Abfrage

               20.0 AS r, 69.0 AS units

dazu zum Beispiel

               20.0 AS r, 111.045 AS units

r ist der Umkreis, in dem Sie suchen möchten. units sind die Entfernungseinheiten (Meilen, km, Furlongs, was immer Sie wollen) pro Breitengrad auf der Erdoberfläche.

Diese Abfrage verwendet zusammen mit MbrContains eine Breiten-/Längenbegrenzung um Punkte auszuschließen, die definitiv zu weit von Ihrem Startpunkt entfernt sind, verwenden Sie dann die Großkreis-Entfernungsformel, um die Entfernungen für die verbleibenden Punkte zu berechnen. Eine Erklärung all dessen finden Sie hier . Wenn Ihre Tabelle die MyISAM-Zugriffsmethode verwendet und einen räumlichen Index hat, MbrContains wird diesen Index ausnutzen, um Ihnen eine schnelle Suche zu ermöglichen.

Schließlich wählt die obige Abfrage alle Punkte innerhalb des Rechtecks ​​aus. Um dies auf nur die Punkte im Kreis einzugrenzen und sie nach Nähe zu ordnen, verpacken Sie die Abfrage wie folgt:

 SELECT id, coordinates, name
   FROM (
         /* the query above, paste it in here */
        ) AS d
  WHERE d.distance <= d.r
  ORDER BY d.distance ASC