Gabungkan setiap dan semua poligon yang berdekatan

22

Saya ingin melakukan tes adjacency pada layer parcel (poligon) dan menggabungkan mereka jika mereka memenuhi kriteria tertentu (bisa ukuran). Per gambar di bawah ini, saya ingin menggabungkan poligon 1,2,3 dan 4, tetapi tidak 5.

Saya punya dua masalah:

  1. ST_TOUCHESmengembalikan TRUE jika hanya sudut sentuh dan bukan segmen garis. Saya rasa saya perlu ST_RELATE untuk memeriksa segmen garis yang dibagikan.
  2. Idealnya, saya ingin menggabungkan SEMUA poligon yang berdekatan menjadi satu, tetapi saya tidak yakin bagaimana skala melampaui dua - seperti dalam, menggabungkan 1,2,3 dan 4 (dan mungkin lebih pada data aktual) dalam satu putaran.

Struktur yang saya miliki sekarang didasarkan pada self join ST_TOUCHES.

masukkan deskripsi gambar di sini

Data mainan

CREATE TABLE testpoly AS 
SELECT 
1 AS id, ST_PolyFromText('POLYGON ((0 0, 10 0, 10 20, 00 20, 0 0 ))') AS geom UNION SELECT
2 AS id, ST_PolyFromText('POLYGON ((10 0, 20 0, 20 20, 10 20, 10 0 ))') AS geom UNION SELECT
3 AS id, ST_PolyFromText('POLYGON ((10 -20, 20 -20, 20 0, 10 0, 10 -20 ))') AS geom UNION SELECT
4 AS id, ST_PolyFromText('POLYGON ((20 -20, 30 -20, 30 0, 20 0, 20 -20 ))') AS geom  UNION SELECT 
5 AS id, ST_PolyFromText('POLYGON ((30 0, 40 0, 40 20, 30 20, 30 0 ))') AS geom ;

Pilihan

SELECT 
    gid, adj_gid,
    st_AStext(st_union(l2.g1,l2.g2)) AS geo_combo
from (
    --level 2
    SELECT
      t1.id AS gid,
      t1.geom AS g1,
      t2.id AS adj_gid,
      t2.geom AS g2
     from
      testpoly  t1,
      testpoly  t2
     where
      ST_Touches( t1.geom, t2.geom ) 
      AND t1.geom && t2.geom 
) 
l2

Berikut hasilnya:

+-----+---------+-------------------------------------------------------------------------------+
| gid | adj_gid | geo_combo                                                                     |
+-----+---------+-------------------------------------------------------------------------------+
| 1   | 2       | POLYGON((10 0,0 0,0 20,10 20,20 20,20 0,10 0))                                |
+-----+---------+-------------------------------------------------------------------------------+
| 1   | 3       | MULTIPOLYGON(((10 0,0 0,0 20,10 20,10 0)),((10 0,20 0,20 -20,10 -20,10 0)))   |
+-----+---------+-------------------------------------------------------------------------------+
| 2   | 1       | POLYGON((10 20,20 20,20 0,10 0,0 0,0 20,10 20))                               |
+-----+---------+-------------------------------------------------------------------------------+
| 2   | 3       | POLYGON((10 0,10 20,20 20,20 0,20 -20,10 -20,10 0))                           |
+-----+---------+-------------------------------------------------------------------------------+
| 2   | 4       | MULTIPOLYGON(((20 0,10 0,10 20,20 20,20 0)),((20 0,30 0,30 -20,20 -20,20 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 3   | 1       | MULTIPOLYGON(((10 0,20 0,20 -20,10 -20,10 0)),((10 0,0 0,0 20,10 20,10 0)))   |
+-----+---------+-------------------------------------------------------------------------------+
| 3   | 2       | POLYGON((20 0,20 -20,10 -20,10 0,10 20,20 20,20 0))                           |
+-----+---------+-------------------------------------------------------------------------------+
| 3   | 4       | POLYGON((20 -20,10 -20,10 0,20 0,30 0,30 -20,20 -20))                         |
+-----+---------+-------------------------------------------------------------------------------+
| 4   | 2       | MULTIPOLYGON(((20 0,30 0,30 -20,20 -20,20 0)),((20 0,10 0,10 20,20 20,20 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 4   | 3       | POLYGON((20 0,30 0,30 -20,20 -20,10 -20,10 0,20 0))                           |
+-----+---------+-------------------------------------------------------------------------------+
| 4   | 5       | MULTIPOLYGON(((30 0,30 -20,20 -20,20 0,30 0)),((30 0,30 20,40 20,40 0,30 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 5   | 4       | MULTIPOLYGON(((30 0,30 20,40 20,40 0,30 0)),((30 0,30 -20,20 -20,20 0,30 0))) |
+-----+---------+-------------------------------------------------------------------------------+

Perhatikan bahwa id poligon = 3 berbagi titik dengan id = 1 dan karenanya dikembalikan sebagai hasil positif. Jika saya mengubah klausa WHERE menjadi ST_Touches( t1.geom, t2.geom ) AND t1.geom && t2.geom AND ST_Relate(t1.geom, t2.geom ,'T*T***T**');saya tidak mendapatkan catatan sama sekali.

  1. Jadi pertama-tama , bagaimana cara menentukan ST_Relate untuk memastikan hanya paket yang berbagi segmen baris yang dipertimbangkan.

  2. Dan kemudian, bagaimana saya menggabungkan poligon 1,2,3,4 dalam satu putaran, meruntuhkan hasil dari panggilan di atas, sementara itu mengakui bahwa kedekatan 1 ke 2 sama dengan kebalikannya?

Memperbarui

Jika saya menambahkan ini ke whereklausa saya jelas hanya mendapatkan poligon dan bukan multipoligon, sehingga menyingkirkan positif palsu untuk tujuan saya - sentuhan sudut akan diabaikan.

GeometryType(st_union(t1.geom,t2.geom)) != 'MULTIPOLYGON'

Meskipun ini tidak ideal (saya lebih suka menggunakan pemeriksaan topologi dengan ST_RELATEsebagai solusi yang lebih umum), ini adalah jalan ke depan. Kemudian tetap tinggal masalah de-duping dan penyatuan ini. Mungkin, jika saya bisa menghasilkan urutan hanya untuk menyentuh poligon, saya bisa menyatukan itu.

Pembaruan II

Yang ini tampaknya bekerja untuk memilih jalur berbagi poligon (tetapi bukan sudut) dan dengan demikian merupakan solusi yang lebih umum daripada MULTIPOLYGONtes di atas . Klausa tempat saya sekarang terlihat seperti ini:

WHERE
              ST_Touches( t1.geom, t2.geom ) 
              AND t1.geom && t2.geom 

              -- 'overlap' relation
              AND ST_Relate(t1.geom, t2.geom)='FF2F11212') t2 

Sekarang yang masih tersisa adalah bagaimana melakukan penggabungan untuk lebih dari sekadar sepasang poligon, tetapi untuk angka arbitrer yang memenuhi kriteria, dalam sekali jalan.

ako
sumber
2
Saya yakin ST_Relate adalah cara yang benar. Saya memecahkan masalah yang sama dengan memeriksa bahwa panjang persimpangan lebih besar dari nol untuk mengecualikan persimpangan titik tunggal. Retasan, tapi berhasil.
John Powell
Jika ada cara untuk mengelompokkan poligon-poligon yang bersebelahan ke dalam array, Anda dapat memodifikasi ST_IntersectionArray[fungsi] [1] agar berfungsi dengan ST_Union [1]: gis.stackexchange.com/a/60295/36886
raphael
2
Mengenai pengelompokan bersama poligon yang berdekatan, Anda dapat memodifikasi algoritma pengelompokan bottom-up yang saya tulis di sini ( gis.stackexchange.com/a/115715/36886 ) untuk menguji kedekatan daripada ruang dan kemudian menggunakan ST_Union saat pengelompokan pada cluster_ids yang dihasilkan
raphael
3
Ada juga ST_ClusterIntersectimg yang mungkin melakukan apa yang Anda butuhkan. Anda perlu Postgis 2.2
John Powell

Jawaban:

3

Saya tidak tahan untuk berpikir bahwa contoh Anda sebenarnya adalah raster dan meskipun Anda menyebutkan bahwa Anda ingin menggabungkan berdasarkan "kriteria tertentu (bisa ukuran)" Saya ingin mencobanya dengan konversi raster.

Untuk contoh spesifik Anda ini akan berfungsi:

WITH rast AS (
  SELECT 
  ST_UNION(ST_AsRaster(geom,10, 20, '2BUI')) r
  FROM testpoly 
)
,p AS (
    SELECT (ST_DumpAsPolygons(r)).geom FROM rast
)
SELECT t.id,p.* 
FROM p
LEFT JOIN testpoly  t ON ST_Equals(p.geom, t.geom)

Apa yang terjadi adalah karena poligon Anda adalah sel-sel yang disejajarkan dengan sempurna, poligon akan dikonversi dengan baik menjadi raster (10x20 sel ukuran). The dumpaspolygons membantu Anda di sini dengan menggabungkan semua sel yang berdekatan menjadi satu dan dengan membandingkan dengan poligon asli Anda bahkan akan bisa mendapatkan id kembali untuk poli yang tidak bergabung.

Setelah menjelaskan ini, saya sangat ingin tahu bagaimana ini akan skala dan seberapa besar dataset Anda: D

memiringkan
sumber
Ide pintar Ini adalah contoh mainan - data saya yang sebenarnya adalah lapisan parcel yang tidak akan dipetakan dengan rapi ke raster.
tepatnya
3

Berikut adalah contoh bagaimana melakukan ini dalam gaya prosedural dengan beberapa lintasan di bawah kap.

CREATE TABLE joined_testpoly AS SELECT array[id] ids, geom FROM testpoly; 

Anda harus dapat membawa lebih banyak kolom dan menerapkan kriteria tambahan untuk bergabung dengan memodifikasi cara kerja LIMIT 1pilih di bawah ini:

CREATE OR REPLACE FUNCTION reduce_joined_testpoly()
RETURNS void
AS $$
DECLARE
  joined_row joined_testpoly%ROWTYPE;
BEGIN
  LOOP
     SELECT array_cat(a.ids, b.ids), st_union(a.geom, b.geom)
         INTO joined_row 
     FROM joined_testpoly a INNER JOIN joined_testpoly b
           on a.ids != b.ids
              and ST_Touches(a.geom, b.geom) and a.geom && b.geom 
              and ST_Relate(a.geom, b.geom)='FF2F11212'
         LIMIT 1;
     IF NOT FOUND THEN
           EXIT;
     END IF;
     INSERT INTO joined_testpoly VALUES (joined_row.ids, joined_row.geom);
     DELETE FROM joined_testpoly
         WHERE joined_testpoly.ids <@ joined_row.ids 
           AND joined_testpoly.ids != joined_row.ids;
  END LOOP;
  RETURN;
END;
$$ LANGUAGE plpgsql;

Jalankan hal itu:

SELECT reduce_joined_testpoly();

Serikat pekerja yang tepat, tidak ada multipoligon:

SELECT ids, st_geometrytype(geom), st_area(geom), st_numgeometries(geom) 
FROM joined_testpoly;
    ids    | st_geometrytype | st_area | st_numgeometries 
-----------+-----------------+---------+------------------
 {5}       | ST_Polygon      |     200 |                1
 {1,2,3,4} | ST_Polygon      |     800 |                1
Eoghan
sumber
2

Berikut ini adalah strategi (tidak berfungsi) untuk referensi (yang saya tidak bisa mengecualikan kasus titik sentuh tunggal). Itu harus lebih cepat daripada jawaban saya yang lain karena hanya membutuhkan 'satu lulus'.

SELECT st_numgeometries(g), (SELECT st_union(x.geom) FROM st_dump(g) x GROUP BY g)
FROM (
    SELECT unnest(st_clusterintersecting(geom)) g, id < 100 as other_arbitrary_grouping 
    FROM testpoly
    GROUP BY other_arbitrary_grouping) c;

(merasa bebas untuk mengubah dan memposting jawaban lain jika ada yang bisa mendapatkan id = 5 geometri di grup sendiri)

Untuk mendapatkan kembali daftar id dll. Anda harus menggunakan st_containsuntuk bergabung kembali di tabel testpoly sebagaimana dirinci dalam jawaban berikut: /programming//a/37486732/6691 tapi saya tidak bisa menggunakannya untuk bekerja untuk poligon karena alasan tertentu.

Eoghan
sumber
2

Berikut adalah langkah cepat menggunakan permintaan asli Anda yang sedikit disesuaikan:

with gr as (SELECT 
    gid, adj_gid,
    st_AStext(st_union(l2.g1,l2.g2)) AS geo_combo
from (
    --level 2
    SELECT
      t1.id AS gid,
      t1.geom AS g1,
      t2.id AS adj_gid,
      t2.geom AS g2
     from
      testpoly  t1,
      testpoly  t2
     where
      ST_Touches( t1.geom, t2.geom ) 
      AND ST_Relate(t1.geom,t2.geom, '****1****')
      AND t1.geom && t2.geom 
) 
l2) select ST_AsText(st_union(gr.geo_combo)) from gr;

Referensi: https://postgis.net/docs/using_postgis_dbmanagement.html#DE-9IM

cm1
sumber