SQL Server: seleziona tutti i poligoni nidificati all'interno di un poligono più grande


9

Questa è una domanda apparentemente semplice sulla geometria di SQL Server che pensavo potesse avere una soluzione pronta all'uso, ma non ho avuto fortuna a trovarne una.

Il mio intento è selezionare tutti i record all'interno di una tabella con poligoni nidificati (contenuti) all'interno di un poligono più grande di un'altra tabella. Avevo previsto funzioni STWithine STContainscome soluzioni di cui avevo bisogno, ma sfortunatamente, entrambi identificano solo i poligoni interni all'interno di quelli nidificati nel poligono più grande, non quei poligoni nidificati che toccano il confine del poligono più grande. Vedi l'immagine per esempio.Risultato da entrambe le funzioni STWithin e STContains

Un'opzione alternativa che ha funzionato alle mie esigenze era STIntersection. Il problema con questa funzione, tuttavia, è che restituisce solo la colonna geometrica! Vorrei invece ottenere l'ID record. Qualcuno ha un suggerimento su come questo può essere fatto?

STWithin:

select a.bg10 from
gis.usa_10_block_group a
join gis.usa_10_mkt_definition b
on a.shape.STWithin(b.shape) = 1
where b.mktname = 'Loop'

STContains:

select a.bg10 from
gis.usa_10_block_group a
join gis.usa_10_mkt_definition b
on b.shape.STContains(a.shape) = 1
where b.mktname = 'Loop'

STIntersection:

select a.shape.STIntersection(b.shape)
from gis.usa_10_block_group a
join gis.usa_10_mkt_definition b
on a.shape.STIntersects(b.shape) = 1
where b.mktname = 'Loop'

Modificare:

Un suggerimento era di omettere STIntersectione utilizzare esclusivamente STIntersectscome segue:

STIntersects:

select a.bg10
from gis.usa_10_block_group a
join gis.usa_10_mkt_definition b
on a.shape.STIntersects(b.shape) = 1
where b.mktname = 'Loop'

Il problema con questo approccio è che STIntersectssembra selezionare tutti i poligoni all'interno o all'esterno e toccare il poligono più grande, non solo quelli strettamente all'interno. Vedi l'immagine per esempio.Risultato dalla funzione STIntersects


Potresti provare a fare un buffer minimo sul tuo poligono contenente e quindi usare STContainso STWithin. Non è proprio un bel trucco, ma ti darà i risultati che desideri. L'altra opzione sarebbe quella di eseguire gli STIntersects con un confronto tra l'area di intersezione e l'area dei poligoni.
MickyT,

Ho iniziato a lavorare su un confronto di area ma sono entrato in una
tana di

Risposte:


8

In teoria le domande che hai fatto dovrebbero restituire i poligoni che hai detto non sono stati restituiti. Questo mi fa sospettare che potresti riscontrare problemi di errore in virgola mobile che SQL Server ha con sé tipi di dati spaziali. Da qui il mio commento sul buffering del poligono di delimitazione con una quantità minima.
Quindi qualcosa come il seguente dovrebbe ottenere i risultati desiderati.

SELECT a.bg10 
FROM gis.usa_10_block_group a
    JOIN gis.usa_10_mkt_definition b
        ON a.shape.STWithin(b.shape.STBuffer(0.0001)) = 1
WHERE b.mktname = 'Loop'

Ecco un breve esempio del comportamento previsto di alcuni dei metodi spaziali.

SELECT Geometry::STGeomFromText(WKT,0), Description
    , Geometry::STGeomFromText('POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))',0).STIntersects(Geometry::STGeomFromText(WKT,0)) Intersects
    , Geometry::STGeomFromText('POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))',0).STContains(Geometry::STGeomFromText(WKT,0)) Contained
    , Geometry::STGeomFromText('POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))',0).STOverlaps(Geometry::STGeomFromText(WKT,0)) Overlaps
    , Geometry::STGeomFromText('POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))',0).STTouches(Geometry::STGeomFromText(WKT,0)) Touches
FROM (VALUES
    ('POLYGON((0 0, 20 0, 20 20, 0 20, 0 0))'            ,'Interior corner')
    ,('POLYGON((90 90, 100 90, 100 100, 90 100, 90 90))' ,'Interior corner')
    ,('POLYGON((20 20, 40 20, 40 40, 20 40, 20 20))'     ,'Interior')
    ,('POLYGON((50 0, 70 0, 70 20, 50 20, 50 0))'        ,'Interior edge')
    ,('POLYGON((50 80, 70 80, 70 100, 50 100, 50 80))'   ,'Interior edge')
    ,('POLYGON((80 50, 100 50, 100 70, 80 70, 80 50))'   ,'Interior edge')
    ,('POLYGON((90 0, 110 0, 110 20, 90 20, 90 0))'      ,'Overlap')
    ,('POLYGON((100 50, 120 50, 120 70, 100 70, 100 50))','Exterior edge')
    )P(WKT,Description)
UNION ALL 
SELECT Geometry::STGeomFromText('POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))',0),'Bounding Area',null,null,null,null

risultati

Description     Intersects Contained Overlaps Touches
--------------- ---------- --------- -------- -------
Interior corner 1          1         0        0
Interior corner 1          1         0        0
Interior        1          1         0        0
Interior edge   1          1         0        0
Interior edge   1          1         0        0
Interior edge   1          1         0        0
Overlap         1          0         1        0
Exterior edge   1          0         0        1
Bounding Area   NULL       NULL      NULL     NULL

Funziona benissimo! Ho dovuto ridurre la dimensione del buffer a 0,001, ma il concetto ha funzionato. Sospetto che il problema sia che le geometrie della tabella gis.usa_10_mkt_definition non derivino dalla stessa topologia di gis.usa_10_block_group, spiegando il motivo per cui si discosta dal risultato atteso che hai menzionato. Ho testato l'uso di STWithin usando due tabelle che condividono la stessa topologia e non era necessario alcun buffer.
user1185790,

2

La query di intersezione dovrebbe assomigliare a questa (supponendo che si desideri ripristinare tutti i record da 'a'):

select a.* --get all columns from table 'a'
from gis.usa_10_block_group a
join gis.usa_10_mkt_definition b
on a.shape.STIntersects(b.shape) = 1
where b.mktname = 'Loop'

Se si desidera solo le aree di a che intersecano b (ad es. Ritaglio da a a b), aggiungere quindi la STIntersection

select a.bg10
, a.STIntersection(b.geom) --clipped geometry from a against b
    from gis.usa_10_block_group a
    join gis.usa_10_mkt_definition b
    on a.shape.STIntersects(b.shape) = 1
    where b.mktname = 'Loop'

Ma questo non ti dà i poligoni che si trovano all'interno di b ancora ...

Questo tipo di poligono in poligono è molto ardito con i confini e la loro coincidenza - per essere "All'interno", i confini di a non possono essere coincidenti con i confini di b - lo stesso vale per "Contiene".

Con queste definizioni, quanti poligoni in a sono effettivamente all'interno di b ...?

Quindi vuoi bufferizzare b prima di selezionare i poligoni in a che sono dentro? O fai un buffer negativo su un?

Non sono sicuro di quale sia la risposta esatta qui ...


Vedi modifica per una spiegazione completa del perché questo non è proprio quello che sto
cercando

Vedo cosa stai cercando di fare ... lavorando su qualcosa ora ...
DPSSpatial
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.