Utilizzo di ST_Difference per rimuovere le funzioni sovrapposte?


11

Sto cercando di usare ST_Difference per creare un set di poligoni (processing.trimmedparcelsnew) che non contengono alcuna area coperta da un altro set di poligoni (test.single_geometry_1) usando PostGis 2.1 (e Postgres SQL 9.3). Ecco la mia domanda:

CREATE TABLE processing.trimmedparcelsnew AS
SELECT
    orig.id, ST_Difference(orig.geom, cont.geom) AS difference
FROM 
    test.single_geometry_1 cont,
    test.multi_geometry_1 orig;

Ma i poligoni risultanti non sono stati tagliati, ma sembrano essere stati divisi nel punto in cui si intersecano con l'altro livello. Ho provato a eseguire la selezione senza inserire il risultato in una tabella e tutto ciò a cui riesco a pensare, ma non riesco a far funzionare questa funzione.

Ho allegato una foto del risultato

inserisci qui la descrizione dell'immagine


Dopo i commenti, ho provato ad aggiungere una clausola WHERE. Voglio i pacchi che non hanno intersezioni e le aree intersecanti degli altri pacchi rimossi (il livello test.single_geometry rappresenta la contaminazione che desidero rimuovere dai miei pacchi). Ho provato un incrocio ma ovviamente voglio davvero i non incroci, quindi ora sto provando un disgiunto. Ho anche provato ad aggiungere l'orig alla mia tabella ma la documentazione per ST_Difference ( http://postgis.net/docs/ST_Difference.html ) dice che restituisce la geometria esatta di cui ho bisogno (una geometria che rappresenta quella parte della geometria A che non si interseca con la geometria B), quindi sono confuso sul motivo per cui vorrei il poligono originale nella mia tabella. Comunque, ecco il mio codice modificato:

CREATE TABLE processing.trimmedparcelsnew AS
SELECT
    orig.id, ST_Difference(orig.geom, cont.geom) AS difference, orig.geom AS geom
FROM 
    test.single_geometry_1 cont,
    test.multi_geometry_1 orig
WHERE ST_Disjoint(orig.geom, cont.geom);

A seguito della risposta di dbaston, ora ho provato:

CREATE TABLE processing.parcels_trimmed AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM test.single_geometry_1 b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM test.multi_geometry_1 a;

Il risultato è solo una copia di test.multi_geometry_1. Anche se ora la divisione non si verifica più.

Ho provato la versione precedente, ma ho appena ricevuto una copia di test.multi_geometry_1:

CREATE TABLE processing.parcels_trimmed_no_coalesce AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM test.single_geometry_1 b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM test.multi_geometry_1 a;

Sto iniziando a chiedermi se c'è qualcos'altro che sto facendo di sbagliato? La dichiarazione procedurale è:

DROP TABLE IF EXISTS processing.parcels_trimmed_no_coalesce;

E sto eseguendo le query dalla finestra delle query SQL di PostgreSQL e Openjump.

L'affermazione che uso per vedere la tabella è:

SELECT * FROM processing.parcels_trimmed_no_coalesce;

Nell'interesse della semplificazione ho ridotto questa query a solo:

SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM test.geometriestocutagainst b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM test.geometriestocut a;

Ciò comporta ancora solo i poligoni originali (test.geometriestocut) quando il risultato desiderato è l'originale tagliato rispetto a test.geometriestocutagainst.


Non hai specificato una WHEREclausola, quindi potresti avere un'espansione polinomiale nella tabella risultante. Quante righe ci sono trimmedparcelsnew?
Vince il

Se vuoi solo la differenza nel punto in cui si intersecano, puoi provare ad aggiungere WHERE ST_Intersects (orig.geom, cont.geom). Altrimenti, la differenza di due poligoni che non si intersecano, è il poligono originale.
John Powell,

Ci sono 24 file nel pacco tagliato nuovo, voglio la differenza anche quando non si intersecano, quindi correggo che devo usare orig.geom nella tabella piuttosto che differenza?
Mart

Un test disgiunto dovrebbe produrre un'espansione polinomiale - ogni caratteristica in cont apparirà una volta per ogni caratteristica in orig che non si sovrappone e la differenza non cambierà mai una geometria di input
Vince

Ok grazie per il chiarimento, ma non sono ancora sicuro del perché il codice originale non funzioni. Se ST_Difference (orig.geom, cont.geom) restituisce le geometrie in a che non si intersecano con b, perché la tabella contiene le geometrie divise anziché le geometrie in a che non si intersecano b.
Mart

Risposte:


14

Un self-join consente di operare sulla relazione tra coppie di due funzioni. Ma non penso che tu sia interessato alle coppie: per ogni caratteristica, vuoi operare sulla relazione tra quella caratteristica e tutte le altre caratteristiche nel tuo set di dati. Puoi farlo con un'espressione di subquery:

CREATE TABLE parcels_trimmed AS
SELECT id, ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                FROM parcels b
                                WHERE ST_Intersects(a.geom, b.geom)
                                  AND a.id != b.id))
FROM parcels a;

Tuttavia, potresti vedere qualcosa di strano nei risultati. I pacchi che non hanno sovrapposizioni vengono eliminati del tutto! Questo perché l' ST_Unionaggregato su un recordset vuoto sarà NULLed ST_Difference(geom, NULL)è NULL. Per risolvere il problema, devi racchiudere la ST_Differencechiamata in un COALESCE:

CREATE TABLE parcels_trimmed AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM parcels b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM parcels a;

Ciò significa che se il risultato di ST_Differenceè NULL, l'espressione coalizzata valuterà la geometria originale.

La query sopra rimuoverà completamente le aree sovrapposte dal tuo dominio. Se invece vuoi scegliere un vincitore, puoi farlo a.id < b.id, o qualche altro criterio, invece di a.id != b.id.


Grazie per la risposta, sfortunatamente ho problemi a far funzionare tutto questo per me, invece mi ritrovo con il poligono originale (a). Modificherò la mia domanda con maggiori informazioni. Grazie ancora.
Mart

2

Ho avuto il tuo stesso problema. Non so se hai già trovato una soluzione al tuo problema, ma ho modificato la risposta accettata sopra e ho ottenuto quello che volevo.

CREATE TABLE parcels_trimmed AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Collect(b.geom) 
                                         FROM parcels b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         )), a.geom)
FROM parcels a;

1

Uso ST_DifferenceAgg () dagli addon PostGIS . Devi unire le due tabelle insieme, avere un identificatore univoco e un indice sulla colonna della geometria. Ecco un breve esempio:

WITH overlappingtable AS (
  SELECT 1 id, ST_GeomFromText('POLYGON((0 1, 3 2, 3 0, 0 1), (1.5 1.333, 2 1.333, 2 0.666, 1.5 0.666, 1.5 1.333))') geom
  UNION ALL
  SELECT 2 id, ST_GeomFromText('POLYGON((1 1, 3.8 2, 4 0, 1 1))')
  UNION ALL
  SELECT 3 id, ST_GeomFromText('POLYGON((2 1, 4.6 2, 5 0, 2 1))')
  UNION ALL
  SELECT 4 id, ST_GeomFromText('POLYGON((3 1, 5.4 2, 6 0, 3 1))')
  UNION ALL
  SELECT 5 id, ST_GeomFromText('POLYGON((3 1, 5.4 2, 6 0, 3 1))')
)
SELECT a.id, ST_DifferenceAgg(a.geom, b.geom) geom
FROM overlappingtable a,
     overlappingtable b
WHERE a.id = b.id OR -- Make sure to pass at least once the polygon with itself
      ((ST_Contains(a.geom, b.geom) OR -- Select all the containing, contained and overlapping polygons
        ST_Contains(b.geom, a.geom) OR
        ST_Overlaps(a.geom, b.geom)) AND
       (ST_Area(a.geom) < ST_Area(b.geom) OR -- Make sure bigger polygons are removed from smaller ones
        (ST_Area(a.geom) = ST_Area(b.geom) AND -- If areas are equal, arbitrarily remove one from the other but in a determined order so it's not done twice.
         a.id < b.id)))
GROUP BY a.id
HAVING ST_Area(ST_DifferenceAgg(a.geom, b.geom)) > 0 AND NOT ST_IsEmpty(ST_DifferenceAgg(a.geom, b.geom));

Ciò unirà le parti sovrapposte con il più grande poligono sovrapposto. Se vuoi mantenere separata la parte sovrapposta, guarda l'esempio ST_splitAgg ().

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.