Jak rekurencyjnie zapętlać przecięcia nadrzędnych wielokątów, aby uzyskać najmniejsze (podrzędne) wielokąty bez nakładania się?


11

Przez kilka dni borykam się z problemem i zdałem sobie sprawę, że wiele osób również utknęło, gdy tematem są skrzyżowania w PostGIS (v2.5). Dlatego postanowiłem zadać bardziej szczegółowe i ogólne, wspólne pytanie.

Mam następującą tabelę:

DROP TABLE IF EXISTS tbl_foo;
CREATE TABLE tbl_foo (
    id bigint NOT NULL,
    geom public.geometry(MultiPolygon, 4326),
    att_category character varying(15),
    att_value integer
);
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (1, ST_SetSRID('MULTIPOLYGON (((0 6, 0 12, 8 9, 0 6)))'::geometry,4326) , 'cat1', 2 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (2, ST_SetSRID('MULTIPOLYGON (((5 0, 5 12, 9 12, 9 0, 5 0)))'::geometry,4326), 'cat1', 1 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (3, ST_SetSRID('MULTIPOLYGON (((4 4, 3 8, 4 12, 7 14,10 12, 11 8, 10 4, 4 4)))'::geometry,4326) , 'cat2', 5 );

To wygląda tak:

początek

Chcę uzyskać wszystkie wielokąty potomne na podstawie przecięcia wielokątów macierzystych. Do wyniku należałoby się spodziewać:

  • Wielokąty potomne bez nakładania się między nimi.
  • Kolumna zawierająca sumę wartości ich macierzystych wielokątów,
  • Kolumna zawierająca liczbę wielokątów macierzystych jednej kategorii
  • Kolumna zawierająca liczbę innej kategorii
  • Kolumna zawierająca kategorię wielokąta potomnego, w oparciu o następującą regułę: -Jeśli WSZYSTKIE wielokąty macierzyste pochodzą z jednej klasy, wielokąt potomny również ma tę klasę. W przeciwnym razie kategoria wielokąta potomnego jest trzecią kategorią.

Wygląda to tak:

wynik

Tak, w końcu, stół wyjściowy generowany (w tym przykładzie) będzie miał 7 wierszy (wszystkie 7, nie pokrywających, wielokątów dziecko), zawierające kolumny category, sum_value, ct_overlap_cat1,ct_overlap_cat2

Poniższy kod, który uruchomiłem, podaje mi poszczególne skrzyżowania, porównując jednego rodzica z drugim.

SELECT
(ST_Dump(
    ST_SymDifference(a.geom, b.geom) 
)).geom
FROM tbl_foo a, tbl_foo b
WHERE a.ID < b.ID AND ST_INTERSECTS(a.geom, b.geom)
UNION ALL
SELECT
ST_Intersection(a.geom, b.geom) as geom
FROM tbl_foo a, tbl_foo b
WHERE a.ID < b.ID AND ST_INTERSECTS(a.geom, b.geom);

Jak rekurencyjnie zapętlać wynik tego wspomnianego kodu, aby niezależnie od liczby nakładających się wielokątów zawsze otrzymywałem jego „najmniejsze” (potomne) wielokąty (ryc. 2)?

Odpowiedzi:


8

Spróbuj tego:

Pobierz dodatki PostGIS z tego linku: https://github.com/pedrogit/postgisaddons

Zainstaluj, uruchamiając plik postgis_addons.sql, aby uzyskać funkcję ST_SplitAgg ().

Przetestuj, uruchamiając plik postgis_addons_test.sql.

Oto twoje zapytanie:

WITH  result_table AS (
    WITH  parts AS (
      SELECT a.att_value val,
             CASE WHEN a.att_category = 'cat1' THEN 1 ELSE 0 END cat1,
             CASE WHEN a.att_category = 'cat2' THEN 1 ELSE 0 END cat2,
             unnest(ST_SplitAgg(a.geom, b.geom, 0.00001)) geom
      FROM tbl_foo a,
           tbl_foo b
      WHERE ST_Equals(a.geom, b.geom) OR
            ST_Contains(a.geom, b.geom) OR
            ST_Contains(b.geom, a.geom) OR
            ST_Overlaps(a.geom, b.geom)
      GROUP BY a.id, a.att_category , ST_AsEWKB(a.geom), val
    )
    SELECT CASE WHEN sum(cat2) = 0 THEN 'cat1'
                WHEN sum(cat1) = 0 THEN 'cat2'
                ELSE 'cat3'
           END category, 
           sum(val*1.0) sum_value, 
           sum(cat1) ct_overlap_cat1, 
           sum(cat2) ct_overlap_cat2, 
           ST_Union(geom) geom
    FROM parts
    GROUP BY ST_Area(geom)
)
SELECT category, sum_value, ct_overlap_cat1, ct_overlap_cat2,
(ST_Dump(result_table.geom)).geom as geom
FROM result_table

Patrzyłem już na twoje dodatki git repo. Inspirstionsl rzeczy.
John Powell,

Wow, świetne rozwiązanie. Wykonałeś również niesamowitą robotę, tworząc te dodatki. Zanim kliknę, by przyznać tę odpowiedź, muszę tylko upewnić się co do jednej rzeczy, która mnie wkurza. Po uruchomieniu podanego kodu „wielokąt 5” (z drugiej cyfry pytania) wydaje się nie rozpoznawać nakładania się na inny wielokąt („ct_overlap_cat2 = 1”; „ct_overlap_cat2 = 0”). Dlatego ten wielokąt ostatecznie zostaje sklasyfikowany jako „cat1” i „sum = 2”, zamiast „cat3” i „sum = 7”. Mam problem z debugowaniem tego małego problemu. Czy mógłbyś mi pomóc?
Matt_Geo,

1
Jedynym problemem związanym z tym rozwiązaniem jest to, że instrukcje przypadków są zakodowane na stałe. Zasadniczo prawdopodobnie powinna być w stanie obsłużyć dowolną liczbę kategorii.
John Powell,

@Matt_Geo Otrzymuję 6 wielokątów w tabeli wynikowej. Trójkątny wielokąt zostaje podzielony na trzy części. Jeden z sumą = 2, jeden z sumą = 7, a drugi z sumą = 8, tak jak na życzeniu.
Pierre Racine,

1
Co się stanie, jeśli zamienisz ST_Centroid (geom) na ST_Area (geom)?
Pierre Racine,

1

Podejrzewam, że jeśli użyjesz geometrii wielokąta zamiast MultiPolygon, wszystko się ułoży:

DROP TABLE IF EXISTS tbl_foo;
CREATE TABLE tbl_foo (
    id bigint NOT NULL,
    geom public.geometry(Polygon, 4326),
    att_category character varying(15),
    att_value integer
);

INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (1, ST_SetSRID('POLYGON ((0 6, 0 12, 8 9, 0 6))'::geometry,4326) , 'cat1', 2 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (2, ST_SetSRID('POLYGON ((5 0, 5 12, 9 12, 9 0, 5 0))'::geometry,4326), 'cat1', 1 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (3, ST_SetSRID('POLYGON ((4 4, 3 8, 4 12, 7 14,10 12, 11 8, 10 4, 4 4))'::geometry,4326) , 'cat2', 5 );

Wynikiem jest 9 wpisów, które odpowiadają różnym opcjom przecięcia w podanym przez Ciebie przykładzie.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.