รวมรูปหลายเหลี่ยมที่อยู่ติดกัน


22

ฉันต้องการทดสอบ adjacency บนเลเยอร์พัสดุ (รูปหลายเหลี่ยม) และรวมเข้าด้วยกันหากพวกเขาเหมาะสมกับเกณฑ์บางอย่าง (อาจเป็นขนาด) ตามภาพด้านล่างฉันต้องการรวมรูปหลายเหลี่ยม 1,2,3 และ 4 แต่ไม่ใช่ 5

ฉันมีสองปัญหา:

  1. ST_TOUCHESส่งกลับ TRUE ถ้าเพียงแค่สัมผัสมุมและไม่ใช่ส่วนของเส้น ฉันคิดว่าฉันต้องการ ST_RELATE เพื่อตรวจสอบเซ็กเมนต์บรรทัดที่แชร์
  2. เป็นการดีที่ฉันต้องการรวมรูปหลายเหลี่ยมที่อยู่ติดกันทั้งหมดไว้ในที่เดียว แต่ฉันไม่แน่ใจว่าจะขยายขนาดเกินสองได้อย่างไร - ในผสาน 1,2,3 และ 4 (และอาจมากกว่ากับข้อมูลจริง) ในรอบเดียว

ST_TOUCHESโครงสร้างที่ผมมีตอนนี้จะขึ้นอยู่กับตัวเองเข้าร่วมใน

ป้อนคำอธิบายรูปภาพที่นี่

ข้อมูลของเล่น

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 ;

การเลือก

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

นี่คือผลลัพธ์:

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

โปรดทราบว่ารูปหลายเหลี่ยม id = 3 แบ่งปันจุดที่มี id = 1 และดังนั้นจะถูกส่งกลับเป็นผลบวก ถ้าฉันเปลี่ยนส่วนคำสั่ง WHERE เป็นST_Touches( t1.geom, t2.geom ) AND t1.geom && t2.geom AND ST_Relate(t1.geom, t2.geom ,'T*T***T**');ฉันจะไม่บันทึกเลย

  1. ดังนั้นก่อนอื่นฉันจะระบุ ST_Relate ได้อย่างไรเพื่อให้แน่ใจว่ามีการพิจารณาเฉพาะพัสดุที่ใช้ร่วมกันในส่วนของเส้น

  2. แล้วฉันจะรวมรูปหลายเหลี่ยม 1,2,3,4 ในรอบเดียวได้อย่างไรการยุบผลลัพธ์จากการโทรด้านบนทั้งหมดในขณะที่การรับรู้ว่าการติด 1 ถึง 2 นั้นเหมือนกันกับการย้อนกลับ

ปรับปรุง

หากฉันเพิ่มสิ่งนี้ลงในwhereข้อฉันเห็นได้ชัดว่ามีรูปหลายเหลี่ยมเท่านั้นและไม่ใช่รูปหลายเหลี่ยมดังนั้นการกำจัดมุมมองเชิงบวกที่ผิดพลาดออกไปเพื่อจุดประสงค์ของฉัน - การสัมผัสมุมจะไม่ถูกนำมาใช้

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

ในขณะนี้ไม่เหมาะ (ฉันอยากจะใช้การตรวจสอบโทโพโลยีด้วยST_RELATEวิธีแก้ปัญหาทั่วไปเพิ่มเติม) มันเป็นวิธีข้างหน้า จากนั้นยังคงเป็นเรื่องของการยกเลิกการจับคู่และการรวมกลุ่มเหล่านี้ อาจเป็นไปได้ว่าถ้าฉันสามารถสร้างลำดับสำหรับรูปหลายเหลี่ยมเท่านั้นที่สัมผัสได้ฉันก็สามารถรวมมันได้

อัปเดต II

อันนี้ดูเหมือนว่าจะใช้งานได้สำหรับการเลือกรูปหลายเหลี่ยมที่ใช้ร่วมกัน (แต่ไม่ใช่มุม) และเป็นการแก้ปัญหาทั่วไปมากกว่าการMULTIPOLYGONทดสอบข้างต้น ข้อที่ฉันตอนนี้ดูเหมือนว่านี้:

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

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

ตอนนี้สิ่งที่เหลืออยู่ยังคงเป็นวิธีการรวมกันเป็นมากกว่ารูปหลายเหลี่ยมคู่หนึ่ง แต่สำหรับตัวเลขที่กำหนดเองจะเข้ากับเกณฑ์ในคราวเดียว


2
ฉันแน่ใจว่า ST_Relate เป็นวิธีที่ถูกต้อง ฉันแก้ไขปัญหาที่คล้ายกันโดยตรวจสอบว่าความยาวของทางแยกมีค่ามากกว่าศูนย์เพื่อไม่รวมทางแยกจุดเดียว แฮ็ค แต่ใช้งานได้
John Powell

ถ้ามีวิธีที่จะเป็นกลุ่มร่วมกันรูปหลายเหลี่ยมที่ต่อเนื่องกันลงในอาร์เรย์แล้วคุณสามารถปรับเปลี่ยนST_IntersectionArray[ฟังก์ชั่น] [1] การทำงานกับ ST_Union [1]: gis.stackexchange.com/a/60295/36886
ราฟาเอล

2
เกี่ยวกับการจัดกลุ่มรูปหลายเหลี่ยมที่ต่อเนื่องกันคุณสามารถแก้ไขอัลกอริธึมการจัดกลุ่มจากล่างขึ้นบนที่ฉันเขียนไว้ที่นี่ ( gis.stackexchange.com/a/115715/36886 ) เพื่อทดสอบ adjacency แทนที่จะใช้พื้นที่แล้วใช้ ST_Union ในขณะที่จัดกลุ่ม cluster_ids ผลลัพธ์
ราฟาเอล

3
นอกจากนี้ยังมี ST_ClusterIntersectimg ซึ่งอาจทำสิ่งที่คุณต้องการ คุณต้องใช้ Postgis 2.2
John Powell

คำตอบ:


3

ฉันอดไม่ได้ที่จะคิดว่าตัวอย่างของคุณเป็นแบบแรสเตอร์และถึงแม้ว่าคุณพูดว่าคุณต้องการผสานโดยใช้ "เกณฑ์บางอย่าง (อาจมีขนาด)" ฉันต้องการให้มันมีการแปลงแบบแรสเตอร์

สำหรับตัวอย่างเฉพาะของคุณสิ่งนี้จะใช้ได้:

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)

สิ่งที่เกิดขึ้นคือเมื่อรูปหลายเหลี่ยมของคุณจัดเรียงเซลล์อย่างสมบูรณ์พวกมันจะแปลงเป็นแรสเตอร์ (เซลล์ขนาด 10x20) dumpaspolygons ช่วยให้คุณที่นี่โดยการรวมเซลล์ที่อยู่ติดกันทั้งหมดเป็นหนึ่งและโดยการเปรียบเทียบกับรูปหลายเหลี่ยมเดิมคุณจะสามารถรับรหัสกลับมาสำหรับโพลีที่ไม่ได้ผสาน

ฉันได้อธิบายอย่างนี้แล้วฉันอยากรู้ว่าชุดข้อมูลของคุณจะใหญ่ขนาดไหนและมีขนาดเท่าใด: D


ความคิดที่ฉลาด นี่เป็นตัวอย่างของเล่น - ข้อมูลจริงของฉันคือเลเยอร์พัสดุซึ่งจะไม่แมปกับ rasters อย่างเรียบร้อย
ako

3

นี่คือตัวอย่างของวิธีการทำสิ่งนี้ในรูปแบบขั้นตอนที่มีการส่งหลายครั้งภายใต้ประทุน

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

คุณควรจะสามารถดำเนินการตามคอลัมน์มากขึ้นและใช้เกณฑ์พิเศษสำหรับการเข้าร่วมโดยแก้ไขวิธีการLIMIT 1เลือกด้านล่าง:

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;

ใช้สิ่งที่:

SELECT reduce_joined_testpoly();

สหภาพที่เหมาะสมไม่มีมัลติโพลิกอน:

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

นี่เป็นอีกกลยุทธ์ (ไม่ทำงาน) สำหรับการอ้างอิง (ที่ฉันไม่สามารถยกเว้นกรณีจุดสัมผัสเดียว) ควรเร็วกว่าคำตอบอื่น ๆ ของฉันเนื่องจากใช้เพียง 'หนึ่งรอบ'

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;

(อย่าลังเลที่จะแก้ไขและโพสต์คำตอบอื่นถ้าใครสามารถได้รับ id = 5 เรขาคณิตในกลุ่มของตัวเอง)

หากต้องการรับรายการรหัส ฯลฯ คุณต้องใช้st_containsเพื่อเข้าร่วมในตาราง testpoly อีกครั้งตามรายละเอียดในคำตอบต่อไปนี้: /programming//a/37486732/6691 แต่ฉันไม่สามารถใช้งานได้ สำหรับรูปหลายเหลี่ยมด้วยเหตุผลบางอย่าง


2

นี่คือการแทงอย่างรวดเร็วโดยใช้แบบสอบถามต้นฉบับของคุณปรับเปลี่ยนเล็กน้อย:

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;

การอ้างอิง: https://postgis.net/docs/using_postgis_dbmanagement.html#DE-9IM

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.