Mysql
 sql >> Database >  >> RDS >> Mysql

Come posso gestire le query di sovrapposizione dei poligoni MySQL?

Fiddle SQL

Crea tabella con colonna poligonale

Tieni presente che per utilizzare gli indici spaziali, non puoi utilizzare InnoDB. Puoi utilizzare la geometria senza indici spaziali, ma le prestazioni peggiorano come al solito.

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;

Ottieni 3 quadrati e un triangolo inseriti

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

Seleziona tutto ciò che interseca il quadratino nell'angolo in basso a sinistra (quadrato viola n. 1)

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

Seleziona tutto ciò che interseca il triangolo, dagli angoli in basso a sinistra a quelli in basso a destra fino all'angolo in alto a destra) (quadrati #1 e #2 e triangolo #4.)

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

Seleziona tutto in quadrato che è al di fuori della nostra immagine (niente)

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

Modifica n. 1:

Ho riletto la domanda e penso che tu abbia le relazioni spaziali un po' confuse. Se quello che vuoi è trovare tutto ciò che si adatta interamente all'interno di un quadrato (poligono), allora devi usare Contains/ST_Contains. Consulta le funzioni spaziali nella documentazione di MySQL per scoprire quale funzione fa il lavoro per te. Si noti la seguente differenza tra le funzioni ST/MBR:

Seleziona tutto ciò che è completamente all'interno di un quadrato (#0 dal basso) (quadrati n. 1, n. 2, triangolo n. 4)

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

Seleziona tutto ciò che è completamente all'interno di un quadrato (#0 dal basso) e non condivide bordi (quadrato n. 2, triangolo n. 4)

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

Modifica n. 2:

Aggiunta molto bella da @StephanB (SQL fiddle )

Seleziona eventuali oggetti sovrapposti

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
;

(nota solo che dovresti rimuovere AND s1.id < s2.id se stai lavorando con CONTAINS , come CONTAINS(a,b) <> CONTAINS(b,a) while Intersects(a,b) = Intersects(b,a) )

Nella figura seguente (elenco non esaustivo):

  • 2 interseca #6.

  • 6 interseca #2

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

  • 1 interseca #0, #5

  • 0 contiene n. 1, n. 3, n. 4 e n. 5 (n. 1, n. 3, n. 4 e n. 5 sono entro n. 0)

  • 1 contiene #5 (#5 è entro #1)

  • 0 st_contiene #3, #4 e #5

  • 1 st_contiene #5

Modifica n. 3:Ricerca per distanza/Lavorare in (con) cerchi

MySQL non supporta direttamente il cerchio come geometria, ma puoi usare la funzione spaziale Buffer(geometry,distance) per aggirarlo. Cosa Buffer() fa, sta creando un buffer di detta distanza attorno alla geometria. Se inizi con un punto geometrico, il buffer è effettivamente un cerchio.

Puoi vedere cosa fa effettivamente il buffer chiamando solo:

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

(il risultato è piuttosto lungo, quindi non lo posterò qui) In realtà crea un poligono che rappresenta il buffer - in questo caso (e il mio MariaDB) il risultato è un poligono di 126 punti, che approssima un cerchio. Con un tale poligono puoi lavorare come faresti con qualsiasi altro poligono. Quindi non ci dovrebbero essere penalità per le prestazioni.

Quindi, se vuoi selezionare tutti i poligoni che cadono in un cerchio puoi risciacquare e ripetere l'esempio precedente (questo troverà solo il quadrato n. 3)

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

Seleziona tutti i poligoni che si intersecano con un cerchio

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

Quando lavori con forme diverse dai rettangoli, dovresti usare ST_* funzioni. Funziona senza ST_ usa un rettangolo di delimitazione. Quindi l'esempio precedente seleziona il triangolo #4 anche se non è nel cerchio.

Come Buffer() crea poligoni piuttosto grandi, ci sarà sicuramente una penalizzazione delle prestazioni rispetto all'utilizzo di ST_Distance() metodo. Purtroppo non riesco a quantificarlo. Dovrai fare un po' di benchmarking.

Un altro modo per trovare oggetti in base alla distanza è usare ST_Distance() funzione.

Seleziona tutti gli elementi dalla tabella e calcola la loro distanza dal punto POINT(6 15)

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

Puoi usare ST_Distance in WHERE anche la clausola.

Seleziona tutti gli elementi la cui distanza da POINT(0 0) è minore o uguale a 10 (seleziona #1, #2 e #3)

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

Sebbene la distanza sia calcolata dal punto più vicino al punto più vicino. Rendendolo simile a ST_Intersect . Quindi l'esempio sopra selezionerà #2 anche se non si adatta completamente all'interno del cerchio.

E sì, il secondo argomento (0) per GeomFromText(text,srid) , non svolge alcun ruolo, puoi tranquillamente ignorarlo. L'ho raccolto da un campione e si è bloccato nella mia risposta. L'ho omesso nelle mie modifiche successive.

a proposito. phpMyAdmin il supporto per l'estensione spaziale non è impeccabile, ma aiuta parecchio a vedere cosa c'è nel tuo database. Mi ha aiutato con queste immagini che ho allegato.