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.