Bitişik çokgenlerin tümünü birleştirme


22

Bir parsel (poligon) katmanında bitişiklik testleri yapmak ve belirli ölçütlere uymaları durumunda (boyutta olabilirler) birleştirmek istiyorum. Aşağıdaki resimde, poligon 1,2,3 ve 4'ü birleştirmek istiyorum, fakat 5'i değil .

İki sorunum var:

  1. ST_TOUCHESSadece köşeler dokunduğunda ve bir çizgi parçasına dokunmadığında TRUE değerini döndürür. Paylaşılan satır segmentlerini kontrol etmek için ST_RELATE'e ihtiyacım olduğunu düşünüyorum.
  2. İdeal olarak, TÜM komşu çokgenleri bire birleştirmek isterim, ancak iki turda 1,2,3 ve 4'ü (ve muhtemelen gerçek veriler üzerinde daha fazlasını) bir turda nasıl birleştireceğinden emin değilim.

Şu an sahip olduğum yapı, kendimi birleştirmeye dayanıyor ST_TOUCHES.

görüntü tanımını buraya girin

Oyuncak verileri

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 ;

seçim

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

İşte çıktı:

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

Çokgen id = 3 öğesinin id = 1 ile bir puan paylaştığını ve bu nedenle pozitif sonuç olarak döndürüldüğünü unutmayın. WHERE yan tümcesini değiştirirsem ST_Touches( t1.geom, t2.geom ) AND t1.geom && t2.geom AND ST_Relate(t1.geom, t2.geom ,'T*T***T**');hiç kayıt alamam.

  1. Yani ilk , nasıl bir çizgi parçasını paylaşmak emin sadece parsel düşünülmektedir sağlamak ST_Relate belirtebilirim.

  2. Ve sonra, bir turda 1,2,3,4 poligonları nasıl birleştiririm, yukarıdaki çağrıdan gelen sonuçları daraltırken, 1'den 2'ye bitişikliğin tersinin aynı olduğunu kabul ederken?

Güncelleştirme

Bunu wheremaddeye eklersem, açıkça sadece çokgenler alıyorum ve çokgenler almıyorum, bu yüzden amaçlarıma göre yanlış pozitifler çıkarıyor - köşe dokunuşları göz ardı ediliyor.

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

Bu ideal olmasa da ( ST_RELATEdaha genel bir çözüm olarak topoloji kontrollerini kullanmayı tercih ederim ), ileriye dönük bir yoldur. Öyleyse bunları aldatma ve sendikalaşma meselesi olmaya devam ediyor. Muhtemelen, sadece dokunan çokgenler için bir sekans oluşturabilirsem, bunun üzerinde birleşebilirdim.

Güncelleme II

Bu, çizgileri paylaşan (ancak köşeleri değil) çokgenleri seçmek için çalışıyor gibi görünüyor ve bu nedenle yukarıdaki MULTIPOLYGONtestten daha genel bir çözüm . Benim şimdiki cümlesi şunun gibi görünüyor:

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

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

Şimdi geriye kalan, hala bir çift poligondan daha fazlası için bir araya getirme, ancak ölçütlere uyan keyfi bir sayı için tek seferde nasıl yapılacağıdır.


2
ST_Relate'in doğru yol olduğuna eminim. Tek noktadan kesişme noktalarını hariç tutmak için kesişme noktalarının uzunluğunun sıfırdan büyük olduğunu kontrol ederek benzer bir sorunu çözdüm. Bir hack ama işe yarıyor.
John Powell

Bitişik çokgenleri diziler halinde gruplandırmanın bir yolu varsa, daha sonra ST_IntersectionArray[işlev] [1] 'i ST_Union [1] ile çalışacak şekilde değiştirebilirsiniz: gis.stackexchange.com/a/60295/36886
raphael

2
Gruplama birlikte bitişik çokgenler ile ilgili olarak, ben burada yazdım aşağıdan yukarıya kümeleme algoritması (değiştirebilir gis.stackexchange.com/a/115715/36886 adjacency ziyade alanı için teste) ve daha sonra ortaya çıkan cluster_ids gruplama sırasında ST_Union kullanmak
raphael

3
Ayrıca ihtiyacınız olanı yapabilecek ST_ClusterIntersectimg da var. Postgis 2.2'ye ihtiyacınız var
John Powell

Yanıtlar:


3

Örneğinizin aslında bir raster olduğunu düşünmeme yardım edemedim ve "belirli ölçütler (boyut olabilir)" temelinde birleştirmek istediğinizi belirtmiş olsanız da, bunu raster dönüşümü ile çekim yapmak istiyorum.

Özel örneğiniz için bu işe yarar:

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)

Olan şey şu ki, poligonlarınız mükemmel bir şekilde hizalanmış hücreler olduğundan, hoş bir şekilde raster (10x20 hücre boyutu) haline dönüşeceklerdir. Dumpaspolygons burada tüm bitişik hücreleri bir araya getirerek size yardımcı olur ve orijinal poligonlarla karşılaştırarak birleştirilmemiş poly'lerin kimliğini geri alabilirsiniz.

Bunu açıkladıktan sonra, bunun nasıl ölçekleneceğini ve veri kümenizin ne kadar büyük olacağını çok merak ediyorum: D


Zekice fikir. Bu bir oyuncak örneği olsa da - gerçek verilerim rasterlerle düzgün bir şekilde eşleşmeyen bir parsel katmanıdır.
ako

3

Aşağıda, kaputun altındaki çoklu geçişlerle prosedürel tarzda bunun nasıl yapılacağına bir örnek.

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

LIMIT 1Aşağıdaki seçimin çalışma şeklini değiştirerek daha fazla sütun taşıyabilir ve birleştirme için ilave kriterler uygulayabilmelisiniz :

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;

Şunu çalıştır:

SELECT reduce_joined_testpoly();

Uygun sendikalar, çokgen yok:

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

2

İşte referans için başka (çalışmıyor) stratejisi (tek dokunma noktası vakasını dışlayamadığım). Sadece 'tek geçiş' alan diğer cevabımdan daha hızlı olmalı.

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;

(Biri kendi grubunda ID = 5 geometri alabilirse, başka bir cevabı değiştirmekten çekinmeyin)

Kullanmak olurdu kimlikleri vb listesini geri almak için st_containsaşağıdaki cevap ayrıntılı olarak testpoly masaya yeniden katılmak için: /programming//a/37486732/6691 ama işe o alamadım bazı nedenlerden dolayı çokgenler için.


2

Burada biraz ayarlanmış orijinal sorgunuzu kullanarak hızlı bir bıçaklamak:

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;

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.