Używasz ST_Difference do usunięcia nakładających się funkcji?

11

Próbuję użyć ST_Difference do utworzenia zestawu wielokątów (processing.trimmedparcelsnew), które nie zawierają żadnego obszaru objętego innym zestawem wielokątów (test.single_geometry_1) przy użyciu PostGis 2.1 (i Postgres SQL 9.3). Oto moje zapytanie:

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;

Ale powstałe wielokąty nie zostały przycięte, zamiast tego wydają się być podzielone tam, gdzie przecinają się z drugą warstwą. Próbowałem właśnie uruchomić selekcję bez umieszczania wyniku w tabeli i wszystkim innym, o czym mogę myśleć, ale wydaje mi się, że nie mogę uruchomić tej funkcji.

Dołączyłem zdjęcie wyniku

wprowadź opis zdjęcia tutaj


Po komentarzach próbowałem dodać klauzulę WHERE. Chcę usunąć działki, które nie mają przecięć, a przecinające się obszary innych działek zostały usunięte (warstwa test.single_geometry reprezentuje zanieczyszczenie, które chcę usunąć z moich działek). Próbowałem przecięcia, ale oczywiście chcę nie przecięcia, więc teraz próbuję rozłączenia. Próbowałem również dodać orig do mojej tabeli, ale dokumentacja ST_Difference ( http://postgis.net/docs/ST_Difference.html ) mówi, że zwraca dokładną geometrię, której potrzebuję (geometria reprezentująca tę część geometrii A, która nie przecina się z geometrią B), więc jestem zdezorientowany, dlaczego zamiast tego chcę mieć oryginalny wielokąt w mojej tabeli. W każdym razie oto mój zmodyfikowany kod:

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

W odpowiedzi na odpowiedź dbaston próbowałem teraz:

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;

Wynikiem tego jest tylko kopia test.multi_geometry_1. Chociaż teraz podział już nie występuje.

Próbowałem wcześniejszej wersji, ale ponownie otrzymuję kopię 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;

Zaczynam się zastanawiać, czy coś jeszcze robię źle? Oświadczenie jest następujące:

DROP TABLE IF EXISTS processing.parcels_trimmed_no_coalesce;

I uruchamiam zapytania z okna zapytań SQL PostgreSQL i Openjump.

Oświadczenie, którego używam do zobaczenia tabeli to:

SELECT * FROM processing.parcels_trimmed_no_coalesce;

W celu uproszczenia ograniczyłem teraz tę kwerendę do:

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;

To wciąż powoduje powstanie tylko oryginalnych wielokątów (test.geometriestocut), gdy pożądanym wynikiem jest oryginalny przycięty względem test.geometriestocutagainst.

Targowisko
źródło
Nie podano WHEREklauzuli, więc w tabeli wynikowej może wystąpić rozwinięcie wielomianowe. Ile jest rzędów trimmedparcelsnew?
Vince
Jeśli chcesz różnicę tylko tam, gdzie się przecinają, możesz spróbować dodać GDZIE ST_Intersects (orig.geom, cont.geom). W przeciwnym razie różnicą między dwoma wielokątami, które się nie przecinają, jest pierwotny wielokąt.
John Powell,
W przyciętej paczce znajdują się 24 wiersze, chcę różnicę, nawet jeśli się nie przecinają, więc czy poprawiłbym, że muszę używać tabeli orig.geom zamiast różnicy?
Mart
Test rozłączny powinien spowodować rozwinięcie wielomianowe - każda funkcja cont pojawi się raz dla każdej funkcji orig , która się nie nakłada, a różnica nigdy nie zmieni geometrii wejściowej
Vince
Ok, dziękuję za wyjaśnienie, ale nadal nie jestem pewien, dlaczego oryginalny kod nie działa. Jeśli ST_Difference (orig.geom, cont.geom) zwraca geometrie w a, które nie przecinają się z b, to dlaczego tabela zawiera podzielone geometrie zamiast geometrii w a, które nie przecinają b.
Mart

Odpowiedzi:

14

Samozłączenie pozwala operować na relacji między parami dwóch funkcji. Ale nie sądzę, że jesteś zainteresowany parami: dla każdej funkcji chcesz operować relacją między tą funkcją a wszystkimi innymi funkcjami w zestawie danych. Możesz to zrobić za pomocą wyrażenia podkwerendowego:

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;

W wynikach możesz jednak zobaczyć coś dziwnego. Paczki, które nie mają żadnych zakładek, są całkowicie usuwane! To dlatego, że ST_Unionagregacja na pustym zestawie rekordów będzie NULLi tak ST_Difference(geom, NULL)będzie NULL. Aby to złagodzić, musisz zawinąć ST_Differencepołączenie w 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;

Oznacza to, że jeśli wynikiem ST_Differencejest NULL, wyrażenie połączone zostanie ocenione do oryginalnej geometrii.

Powyższe zapytanie całkowicie usunie nakładające się obszary z domeny. Jeśli zamiast tego chcesz wybrać zwycięzcę, możesz a.id < b.idzamiast tego zrobić inne kryterium a.id != b.id.

dbaston
źródło
Dzięki za odpowiedź, niestety nie mogę sprawić, żeby to zadziałało, zamiast tego mam oryginalny wielokąt (a). Zmodyfikuję moje pytanie, podając więcej informacji. Dzięki jeszcze raz.
Mart
2

Miałem ten sam problem co ty. Nie wiem, czy już znalazłeś rozwiązanie swojego problemu, ale zmodyfikowałem zaakceptowaną odpowiedź powyżej i dostałem to, czego chciałem.

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;
Dag
źródło
1

Używam ST_DifferenceAgg () z PostGIS Addons . Musisz połączyć dwie tabele razem, mieć unikalny identyfikator i indeks w kolumnie geometrii. Oto krótki przykład:

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

Spowoduje to scalenie zachodzących na siebie części z największym zachodzącym wielokątem. Jeśli chcesz zachować zachodzące na siebie części, spójrz na przykład ST_splitAgg ().

Pierre Racine
źródło