Bagaimana secara berulang loop melalui persimpangan poligon induk untuk mendapatkan poligon (anak) terkecil tanpa tumpang tindih?

11

Saya berjuang dengan masalah selama beberapa hari dan menyadari banyak orang juga terjebak ketika topiknya adalah persimpangan di PostGIS (v2.5). Itu sebabnya saya memutuskan untuk menanyakan pertanyaan umum yang lebih rinci dan umum.

Saya memiliki tabel berikut:

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

Ini terlihat seperti ini:

Mulailah

Saya ingin mendapatkan semua poligon anak berdasarkan persimpangan poligon induk. Untuk hasilnya, itu diharapkan:

  • Poligon anak tanpa tumpang tindih di antara mereka.
  • Kolom yang berisi jumlah nilai poligon induknya,
  • Kolom yang berisi jumlah poligon induk dari satu kategori
  • Kolom yang berisi jumlah kategori lain
  • Kolom yang berisi kategori poligon anak, berdasarkan aturan berikut: -Jika SEMUA poligon induk berasal dari satu kelas, poligon anak juga memiliki kelas ini. Selain itu, kategori poligon anak adalah kategori ketiga.

Jadi akan terlihat seperti itu:

keluaran

Jadi, pada akhirnya, tabel output yang dihasilkan (untuk contoh ini) akan memiliki 7 baris (semua 7, tidak tumpang tindih, poligon anak), yang berisi kolom category, sum_value, ct_overlap_cat1,ct_overlap_cat2

Kode berikut ini saya mulai, memberi saya persimpangan individual, membandingkan satu orangtua dengan yang lain.

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

Bagaimana cara mengulang secara berulang melalui hasil kode yang disebutkan ini, bahwa, terlepas dari jumlah poligon yang tumpang tindih, saya selalu mendapatkan poligon terkecil (anak) (Gbr. 2)?

Matt_Geo
sumber

Jawaban:

8

Coba ini:

Unduh AddG PostGIS dari tautan ini: https://github.com/pedrogit/postgisaddons

Instal dengan menjalankan file postgis_addons.sql untuk mendapatkan fungsi ST_SplitAgg ().

Uji dengan menjalankan file postgis_addons_test.sql.

Inilah kueri Anda:

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
Pierre Racine
sumber
Saya telah melihat addit Anda git repo sebelumnya. Barang-barang inspirstionsl.
John Powell
Wow solusi bagus. Anda melakukan pekerjaan yang sangat luar biasa untuk membuat addons ini, juga. Sebelum saya mengklik untuk memberikan jawaban ini, saya hanya satu untuk memastikan tentang satu hal yang mengganggu saya. Menjalankan kode yang Anda berikan, 'poligon 5' (dari gambar kedua pertanyaan) tampaknya tidak mengenali tumpang tindih dengan poligon lain ('ct_overlap_cat2 = 1'; 'ct_overlap_cat2 = 0'). Oleh karena itu, poligon ini pada akhirnya diklasifikasikan sebagai 'cat1' dan 'jumlah = 2', bukan 'cat3' dan 'jumlah = 7'. Saya menghadapi sedikit kesulitan untuk men-debug masalah kecil ini. Bisakah kamu membantuku?
Matt_Geo
1
Satu-satunya masalah dengan solusi ini adalah bahwa pernyataan kasus dikodekan dengan keras. Pada prinsipnya, itu mungkin harus dapat menangani sejumlah kategori yang sewenang-wenang.
John Powell
@Matt_Geo Saya mendapatkan 6 poligon di tabel yang dihasilkan. Poligon segitiga terbagi menjadi tiga. Satu dengan jumlah = 2, satu dengan jumlah = 7 dan satu dengan jumlah = 8 seperti pada angka yang Anda inginkan.
Pierre Racine
1
Bagaimana jika Anda mengganti ST_Centroid (geom) dengan ST_Area (geom)?
Pierre Racine
1

Saya kira jika Anda menggunakan tipe geometri poligon bukan MultiPolygon semuanya akan jatuh ke tempatnya:

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

Hasilnya adalah 9 entri yang sesuai dengan opsi persimpangan berbeda dalam contoh yang diberikan oleh Anda.

Cyril Mikhalchenko
sumber