sql >> Databasteknik >  >> RDS >> Mysql

Hur kan jag hantera MySQL-polygonöverlappningsfrågor?

SQL-fiol

Skapa tabell med polygonkolumn

Observera att för att använda rumsliga index kan du inte använda InnoDB. Du kan använda geometrin utan rumsliga index, men prestandan försämras som vanligt.

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;

Få in 3 rutor och en triangel

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

Välj allt som skär en liten fyrkant i det nedre vänstra hörnet (lila kvadrat #1)

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

Välj allt som skär triangeln från det nedre vänstra till det nedre högra hörnet till det övre högra hörnet) (ruta #1 och #2 och triange #4.)

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

Väljer allt i kvadrat som är utanför vår bild (ingenting)

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

Redigera #1:

Jag läste frågan igen och jag tror att du har de rumsliga relationerna lite förvirrade. Om det du vill är att hitta allt som får plats helt inuti en kvadrat (polygon), måste du använda Contains/ST_Contains. Se rumsliga funktioner i MySQL-dokumentationen för att ta reda på vilken funktion som gör jobbet åt dig. Observera följande skillnad mellan ST/MBR-funktioner:

Väljer allt som är helt inuti en kvadrat (#0 underifrån) (ruta #1, #2, triangel #4)

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

Väljer allt som är helt inuti en kvadrat (#0 underifrån) och delar inga kanter (ruta #2, triangel #4)

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

Redigera #2:

Mycket trevligt tillägg från @StephanB (SQL-fiol )

Välj eventuella överlappande objekt

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
;

(observera bara att du bör ta bort AND s1.id < s2.id om du arbetar med CONTAINS , som CONTAINS(a,b) <> CONTAINS(b,a) medan Intersects(a,b) = Intersects(b,a) )

På följande bild (icke uttömmande lista):

  • 2 skär #6.

  • 6 skär #2

  • 0 skär #1, #2, #3, #4, #5

  • 1 skär #0, #5

  • 0 innehåller #1, #3, #4 och #5 (#1, #3, #4 och #5 är inom #0)

  • 1 innehåller #5 (#5 är inom #1)

  • 0 st_innehåller #3, #4 och #5

  • 1 st_innehåller #5

Redigering #3:Söka på avstånd/Arbeta i (med) cirklar

MySQL har inte direkt stöd för cirkel som en geometri, men du kan använda rumslig funktion Buffer(geometry,distance) att komma runt det. Vad Buffer() gör, skapar en buffert av nämnda avstånd runt geometrin. Om du börjar med geometripunkt är bufferten verkligen en cirkel.

Du kan se vad buffert faktiskt gör genom att bara anropa:

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

(Resultatet är ganska långt, så jag kommer inte att posta det här) Det skapar faktiskt polygon som representerar bufferten - i det här fallet (och min MariaDB) blir resultatet en polygon på 126 punkter, vilket ungefär är en cirkel. Med en sådan polygon kan du arbeta som du skulle arbeta med vilken annan polygon som helst. Så det borde inte finnas något prestationsstraff.

Så om du vill välja alla polygoner som faller in i en cirkel du kan skölja och upprepa föregående exempel (detta hittar bara ruta #3)

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

Välj alla polygoner som skär en cirkel

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

När du arbetar med former som skiljer sig från rektanglar bör du använda ST_* funktioner. Fungerar utan ST_ använd en avgränsande rektangel. Så det föregående exemplet väljer triangeln #4 även om den inte är i cirkeln.

Som Buffer() skapar ganska stora polygoner, kommer det definitivt att bli en viss prestationsstraff över att använda ST_Distance() metod. Tyvärr kan jag inte kvantifiera det. Du måste göra en del benchmarking.

Ett annat sätt att hitta objekt efter avstånd är att använda ST_Distance() fungera.

Välj alla element från tabellen och beräkna deras avstånd från punkten POINT(6 15)

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

Du kan använda ST_Distance i WHERE klausul också.

Välj alla element vars avstånd från POINT(0 0) är mindre eller lika med 10 (väljer #1, #2 och #3)

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

Även om avståndet beräknas från närmaste punkt till närmaste punkt. Gör det likt ST_Intersect . Så exemplet ovan kommer att välja #2 även om det inte passar helt inuti cirkeln.

Och ja, det andra argumentet (0) för GeomFromText(text,srid) , spelar ingen roll, du kan lugnt ignorera det. Jag har plockat upp det från något prov och det fastnade liksom i mitt svar. Jag har utelämnat det i mina senare redigeringar.

btw. phpMyAdmin stöd för spatial extension är inte felfritt, men det hjälper en hel del att se vad som finns i din databas. Hjälpte mig med dessa bilder jag har bifogat.




  1. MYSQL-ordning genom både stigande och fallande sortering

  2. Säkra dina Mongo-kluster med SSL

  3. Android :Hur man går med i barnbordet med föräldrabordet Sqlite

  4. Pilnotation