2015-04-15 18 views
14

Ad esempio, ho una tabella di utenti. Gli utenti sono ciascuno un "quadrato" (o un cerchio) su una mappa.Come posso gestire le query di sovrapposizione poligono MySQL?

Desidero trovare utenti che si sovrappongano a un altro quadrato su una mappa. Questo è attualmente supportato in MySQL 5.6? (Che ne è delle versioni di sviluppo di MySQL?)

Si prega di notare che non sto cercando "trova utenti con un punto all'interno di questo quadrato". Sto cercando "trova utenti il ​​cui quadrato si sovrappone a questo quadrato (non necessariamente contenere, purché entrambi i quadretti tocchino - va bene)".

Sarebbe davvero di grande aiuto se qualcuno potesse fornire un esempio di INSERIMENTO di record e poi INTERROGAZIONE utilizzando poligoni ST_INTERSECT.

+0

https://dev.mysql.com/doc/refman/5.0/en/using-spatial-data.html – DhruvPathak

risposta

20

SQL fiddle Polygon demonstration

Crea una tabella con la colonna poligono

Si prega di notare, che per usare indici spaziali, non è possibile utilizzare InnoDB. È possibile utilizzare la geometria senza indici spaziali, ma le prestazioni si degradano 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; 

Prendi 3 quadrati e un triangolo inserito

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

Selezionare tutto ciò che si interseca piccola piazza nell'angolo in basso a sinistra (viola quadrato # 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 che va via m basso a sinistra per abbassare gli angoli retti in alto a destra) (piazze # 1 e # 2 e triange # 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 piazza 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 # 1:

ho riletto la questione e penso di avere le relazioni spaziali un po 'confuso. Se quello che vuoi è trovare tutto ciò che si adatta all'interno di un quadrato (poligono), allora devi usare Contains/ST_Contains. Si prega di vedere spatial functions in MySQL documentation per scoprire quale funzione fa il lavoro per voi. Si prega di notare quanto segue differenza tra le funzioni ST/MBR:

Seleziona tutto ciò che è completamente all'interno di un quadrato (# 0 dal basso) (quadrati # 1, # 2, triangolo 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 condivide spigoli (quadrato # 2, # triangolo 4)

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

Edit # 2:

Molto bella aggiunta da @StephanB (SQL fiddle)

Per elencare tutte le intersezioni unirsi al tavolo su se stesso e filtrare via l'intersezione inversa

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 
; 

(solo informiamo che è necessario rimuovere il AND s1.id < s2.id se si sta lavorando con CONTAINS, come CONTAINS(a,b) <> CONTAINS(b,a) mentre 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 # 1, # 3, # 4 e # 5 (# 1, # 3, # 4 e # 5 sono raggiungibili # 0)

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

  • 0 ST_Contains # 3, # 4 e # 5

  • 1 ST_Contains # 5

Spatial relations

Modifica # 3: Ricerca per distanza/Lavorare in (con) circola attorno

MySQL non supporta direttamente il cerchio come una geometria, ma è possibile utilizzare la funzione spaziale Buffer(geometry,distance) per aggirare il problema. Quello che fa Buffer() sta creando un buffer di detta distanza attorno alla geometria. Se inizi con il punto di geometria, il buffer è effettivamente un cerchio.

Si può vedere ciò che realmente fa tampone chiamando semplicemente:

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

(risultato è piuttosto lungo, quindi non mi post qui) Si crea in realtà un poligono che rappresenta il buffer - in questo caso (e il mio MariaDB) il risultato è un poligono di 126 punti, che si avvicina a 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 si vuole selezionare tutti i poligoni che rientrano in un circolo si può lavare e ripetere precedente esempio (questo sarà trovare solo la piazza # 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 si lavora con forme diverse da rettangoli, è necessario utilizzare il ST_* fu nctions. Le funzioni senza ST_ utilizzano un rettangolo di delimitazione. Quindi l'esempio precedente seleziona il triangolo # 4 anche se non è nel cerchio.

Poiché Buffer() crea poligoni abbastanza grandi, ci sarà sicuramente qualche penalizzazione delle prestazioni rispetto all'utilizzo del metodo ST_Distance(). Purtroppo non posso quantificarlo. Dovrai fare un po 'di benchmarking.

Adding circle to first example

altro modo di trovare oggetti per distanza utilizza la funzione ST_Distance().

selezionare tutti gli elementi della tavola e calcolare la loro distanza dal punto PUNTO (6 15)

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

È possibile utilizzare ST_Distance in WHERE clausola pure.

Seleziona tutti gli elementi la cui distanza dal punto (0 0) è inferiore 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 viene calcolata dal punto più vicino punto più vicino Rendendolo simile allo ST_Intersect. Quindi l'esempio sopra selezionerà # 2 anche se non si adatta interamente al cerchio.

E sì, il secondo argomento (0) per GeomFromText(text,srid), non svolge alcun ruolo, è possibile ignorarlo tranquillamente. L'ho preso da un campione ed è rimasto bloccato nella mia risposta. L'ho lasciato fuori nelle mie successive modifiche.

btw. Il supporto phpMyAdmin per l'estensione spaziale non è perfetto, ma aiuta un po 'a vedere cosa c'è nel tuo database. Mi ha aiutato con queste immagini che ho allegato.

+1

Grande risposta:) Per elencare tutte le intersezioni, unire la tabella a se stessa e filtrare l'intersezione inversa: http : //sqlfiddle.com/#! 9/455a7/1/4 –

+0

@StephanB Bel suggerimento. Grazie. Ti dispiacerebbe aggiungere questo alla mia risposta? – Fox

+0

Continua, mai inteso rivaleggiare con la tua risposta in ogni caso: D –

Problemi correlati