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

Wie kann ich mit MySQL-Polygonüberlappungsabfragen umgehen?

SQL-Fiddle

Tabelle mit Polygonspalte erstellen

Bitte beachten Sie, dass Sie InnoDB nicht verwenden können, um räumliche Indizes zu verwenden. Sie können die Geometrie ohne räumliche Indizes verwenden, aber die Leistung nimmt wie gewohnt ab.

CREATE TABLE IF NOT EXISTS `spatial` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `poly` geometry NOT NULL,
  UNIQUE KEY `id` (`id`),
  SPATIAL INDEX `poly` (`poly`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

Lassen Sie 3 Quadrate und ein Dreieck einfügen

INSERT INTO `spatial` (`poly`) VALUES (GeomFromText('POLYGON((0 0,10 0,10 10,0 10,0 0))',0));
INSERT INTO `spatial` (`poly`) VALUES (GeomFromText('POLYGON((10 50,50 50,50 10,10 10,10 50))',0));
INSERT INTO `spatial` (`poly`) VALUES (GeomFromText('POLYGON((1 15,5 15,5 11,1 11,1 15))',0));
INSERT INTO `spatial` (`poly`) VALUES (GeomFromText('POLYGON((11 5,15 5,15 1,11 5))',0));

Wählen Sie alles aus, was das kleine Quadrat in der unteren linken Ecke schneidet (violettes Quadrat Nr. 1)

SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Intersects(`poly`,
            GEOMFROMTEXT('POLYGON((0 0,2 0,2 2,0 2,0 0))', 0 )
        )
;

Wählen Sie alles aus, was das Dreieck schneidet (von der unteren linken Ecke zur unteren rechten Ecke zur oberen rechten Ecke) (Quadrate #1 und #2 und Dreieck #4.)

SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Intersects(`poly`,
            GEOMFROMTEXT('POLYGON((0 0,50 50,50 0,0 0))', 0 )
        )
;

Wählt alles im Quadrat aus, das sich außerhalb unseres Bildes befindet (nichts)

SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Intersects(`poly`,
            GEOMFROMTEXT('POLYGON((100 100,200 100,200 200,100 200,100 100))', 0 )
        )
;

Änderung Nr. 1:

Ich habe die Frage noch einmal gelesen und ich denke, Sie haben die räumlichen Beziehungen ein wenig verwirrt. Wenn Sie alles finden möchten, was vollständig in ein Quadrat (Polygon) passt, müssen Sie Contains/ST_Contains verwenden. Siehe räumliche Funktionen in der MySQL-Dokumentation um herauszufinden, welche Funktion die Arbeit für Sie erledigt. Bitte beachten Sie den folgenden Unterschied zwischen ST/MBR-Funktionen:

Wählt alles aus, was sich vollständig innerhalb eines Quadrats befindet (#0 von unten) (Quadrate Nr. 1, Nr. 2, Dreieck Nr. 4)

SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        Contains(
          GEOMFROMTEXT('POLYGON((0 0,20 0,20 20,0 20,0 0))', 0 ),
          `poly`
        )
;

Wählt alles aus, was sich vollständig innerhalb eines Quadrats (#0 von unten) befindet und keine Kanten teilt (Quadrat Nr. 2, Dreieck Nr. 4)

SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Contains(
          GEOMFROMTEXT('POLYGON((0 0,20 0,20 20,0 20,0 0))', 0 ),
          `poly`
        )
;

Änderung Nr. 2:

Sehr schöne Ergänzung von @StephanB (SQL-Fiddle ). )

Wählen Sie überlappende Objekte aus

SELECT s1.id,AsText(s1.poly), s2.id, AsText(s2.poly)
FROM  `spatial` s1, `spatial` s2
    WHERE 
        ST_Intersects(s1.poly, s2.poly)
    AND s1.id < s2.id
;

(Beachten Sie nur, dass Sie AND s1.id < s2.id entfernen sollten wenn Sie mit CONTAINS arbeiten , als CONTAINS(a,b) <> CONTAINS(b,a) während Intersects(a,b) = Intersects(b,a) )

Im folgenden Bild (nicht erschöpfende Liste):

  • 2 schneidet #6.

  • 6 schneidet #2

  • 0 schneidet #1, #2, #3, #4, #5

  • 1 schneidet #0, #5

  • 0 enthält #1, #3, #4 und #5 (#1, #3, #4 und #5 sind innerhalb von #0)

  • 1 enthält #5 (#5 ist innerhalb von #1)

  • 0 st_contains #3, #4 und #5

  • 1 st_contains #5

Edit #3:Suchen nach Entfernung/Arbeiten in (mit) Kreisen

MySQL unterstützt Kreise nicht direkt als Geometrie, aber Sie können die räumliche Funktion Buffer(geometry,distance) verwenden um es herum zu arbeiten. Welcher Buffer() tut, erzeugt einen Puffer dieser Distanz um die Geometrie herum. Wenn Sie mit einem Geometriepunkt beginnen, ist der Puffer tatsächlich ein Kreis.

Sie können sehen, was Buffer tatsächlich tut, indem Sie einfach aufrufen:

SELECT ASTEXT(BUFFER(GEOMFROMTEXT('POINT(5 5)'),3))

(Das Ergebnis ist ziemlich lang, also werde ich es hier nicht posten) Es erstellt tatsächlich ein Polygon, das den Puffer darstellt - in diesem Fall (und meiner MariaDB) ist das Ergebnis ein 126-Punkte-Polygon, das ungefähr einem Kreis entspricht. Mit einem solchen Polygon können Sie wie mit jedem anderen Polygon arbeiten. Es sollte also keine Leistungseinbußen geben.

Wenn Sie also alle Polygone auswählen möchten, die in einen Kreis fallen Sie können das vorherige Beispiel spülen und wiederholen (dies findet nur das Quadrat #3)

SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Contains(
          Buffer(GEOMFROMTEXT('POINT(6 15)'), 10),
          `poly`
        )
;

Alle Polygone auswählen, die sich mit einem Kreis schneiden

SELECT id,AsText(poly) FROM  `spatial` 
    WHERE 
        ST_Intersects(
          Buffer(GEOMFROMTEXT('POINT(6 15)'), 10),
          `poly`
        )
;

Wenn Sie mit anderen Formen als Rechtecken arbeiten, sollten Sie den ST_* verwenden Funktionen. Funktioniert ohne ST_ Verwenden Sie ein Begrenzungsrechteck. Das vorherige Beispiel wählt also das Dreieck Nr. 4 aus, obwohl es sich nicht im Kreis befindet.

Als Buffer() recht große Polygone erstellt, wird es definitiv zu Leistungseinbußen bei der Verwendung von ST_Distance() kommen Methode. Leider kann ich es nicht quantifizieren. Sie müssen ein Benchmarking durchführen.

Eine andere Möglichkeit, Objekte anhand der Entfernung zu finden, ist die Verwendung von ST_Distance() Funktion.

Wählen Sie alle Elemente aus der Tabelle aus und berechnen Sie ihre Entfernung vom Punkt POINT(6 15)

SELECT id, AsText(`poly`), 
    ST_Distance(poly, GeomFromText('POINT(6 15)')) 
    FROM `spatial`
;

Sie können ST_Distance verwenden in WHERE Klausel.

Alle Elemente auswählen, deren Abstand von POINT(0 0) kleiner oder gleich 10 ist (wählt #1, #2 und #3 aus)

SELECT id, AsText(`poly`), 
    ST_Distance(poly, GeomFromText('POINT(6 15)')) 
    FROM `spatial`
    WHERE ST_Distance(poly, GeomFromText('POINT(6 15)')) <= 10
;

Obwohl die Entfernung vom nächsten Punkt zum nächsten Punkt berechnet wird. Machen Sie es dem ST_Intersect ähnlich . Das obige Beispiel wählt also #2 aus, obwohl es nicht vollständig in den Kreis passt.

Und ja, das zweite Argument (0) für GeomFromText(text,srid) , spielt keine Rolle, Sie können es getrost ignorieren. Ich habe es aus einer Probe herausgeholt und es steckte irgendwie in meiner Antwort fest. Ich habe es in meinen späteren Bearbeitungen weggelassen.

übrigens. phpMyAdmin Die Unterstützung für die räumliche Erweiterung ist nicht fehlerfrei, aber es hilft ziemlich, zu sehen, was sich in Ihrer Datenbank befindet. Hat mir mit diesen Bildern geholfen, die ich angehängt habe.