แยกรูปหลายเหลี่ยมตามการแยกโดยใช้ PostGIS


36

ฉันมีตารางรูปหลายเหลี่ยมของ PostGIS ซึ่งมีบางจุดตัดกัน นี่คือสิ่งที่ฉันพยายามทำ:

  • สำหรับรูปหลายเหลี่ยมที่เลือกโดย id ให้รูปหลายเหลี่ยมทั้งหมดที่ตัดกัน โดยทั่วไปselect the_geom from the_table where ST_Intersects(the_geom, (select the_geom from the_table where source_id = '123'))
  • จากรูปหลายเหลี่ยมเหล่านี้ฉันต้องสร้างรูปหลายเหลี่ยมใหม่เพื่อให้จุดตัดกลายเป็นรูปหลายเหลี่ยมใหม่ ดังนั้นถ้ารูปหลายเหลี่ยม A ตัดกับรูปหลายเหลี่ยม B ฉันจะได้รูปหลายเหลี่ยม 3 รูปคือ A ลบ AB, AB และ B ลบ AB

ความคิดใด ๆ


1
อืมฉันคิดว่าเห็นว่าคุณกำลังทำอะไรอยู่ แต่กราฟิกง่าย ๆ อาจทำสิ่งมหัศจรรย์เพื่อช่วยฉัน (และคนอื่น ๆ ) เห็นสิ่งที่คุณต้องการ
เจสัน

เพิ่มบางคำตอบของฉัน
Adam Matan

คำตอบ:


29

เนื่องจากคุณบอกว่าคุณได้รับกลุ่มของรูปหลายเหลี่ยมที่ตัดกันสำหรับรูปหลายเหลี่ยมแต่ละรูปที่คุณสนใจคุณอาจต้องการสร้างสิ่งที่เรียกว่า "รูปหลายเหลี่ยมซ้อนทับ"

นี่ไม่ใช่สิ่งที่อดัมกำลังทำอยู่ หากต้องการดูความแตกต่างลองดูภาพของจุดตัด ABC นี้:

จุดตัด ABC

ฉันเชื่อว่าคำตอบของอดัมจะสร้างรูปหลายเหลี่ยม "AB" ที่ครอบคลุมทั้งพื้นที่ของ "AB! C" และ "ABC" รวมถึงรูปหลายเหลี่ยม "AC" ที่ครอบคลุม "AC! B" และ "ABC" และ " BC "รูปหลายเหลี่ยมนั่นคือ" BC! A "และ" ABC " ดังนั้นรูปหลายเหลี่ยม "AB", "AC" และ "BC" จะซ้อนทับพื้นที่ "ABC" ทั้งหมด

การซ้อนทับรูปหลายเหลี่ยมสร้างรูปหลายเหลี่ยมที่ไม่ทับซ้อนกันดังนั้น AB! C จะเป็นรูปหลายเหลี่ยมหนึ่งและ ABC จะเป็นรูปหลายเหลี่ยมหนึ่งรูป

การสร้างภาพซ้อนทับรูปหลายเหลี่ยมใน PostGIS นั้นค่อนข้างตรงไปตรงมา

โดยทั่วไปมีสามขั้นตอน

ขั้นตอนที่ 1 คือแยก linework [โปรดทราบว่าฉันใช้วงแหวนด้านนอกของรูปหลายเหลี่ยมมันจะซับซ้อนกว่านี้เล็กน้อยถ้าคุณต้องการจัดการรูอย่างถูกต้อง]:

SELECT ST_ExteriorRing(polygon_col) AS the_geom FROM my_table) AS lines

ขั้นตอนที่ 2 คือ "node" the linework (สร้างโหนดที่จุดตัดทุกจุด) บางไลบรารีเช่นJTSมีคลาส "Noder" คุณสามารถใช้ทำสิ่งนี้ได้ แต่ใน PostGIS ฟังก์ชันST_Unionทำเพื่อคุณ:

SELECT ST_Union(the_geom) AS the_geom FROM (...your lines...) AS noded_lines

ขั้นตอนที่ 3 คือการสร้างรูปหลายเหลี่ยมที่ไม่ทับซ้อนกันซึ่งอาจมาจากทุกบรรทัดที่ทำโดยฟังก์ชันST_Polygonize :

SELECT ST_Polygonize(the_geom) AS the_geom FROM (...your noded lines...)

คุณสามารถบันทึกผลลัพธ์ของแต่ละขั้นตอนเหล่านั้นลงในตาราง temp หรือคุณสามารถรวมพวกมันทั้งหมดไว้ในคำสั่งเดียว:

CREATE TABLE my_poly_overlay AS
SELECT geom FROM ST_Dump((
    SELECT ST_Polygonize(the_geom) AS the_geom FROM (
        SELECT ST_Union(the_geom) AS the_geom FROM (
            SELECT ST_ExteriorRing(polygon_col) AS the_geom FROM my_table) AS lines
        ) AS noded_lines
    )
)

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


โปรดทราบว่าST_ExteriorRingลดลงหลุมใด ๆ ST_Boundaryจะรักษาวงแหวนภายใน แต่มันจะสร้างรูปหลายเหลี่ยมที่อยู่ภายใน
jpmc26

การรวมกันของวงแหวนภายนอกเกิดปัญหาเมื่อมีรูปหลายเหลี่ยมมากเกินไป โชคไม่ดีที่โซลูชัน "ตรงไปตรงมา" นี้ใช้งานได้สำหรับการครอบคลุมขนาดเล็กเท่านั้น
Pierre Racine

13

หากฉันเข้าใจอย่างถูกต้องคุณต้องการที่จะ (A เป็นรูปทรงเรขาคณิตด้านซ้าย B เป็นสิ่งที่ถูกต้อง):

รูปภาพของA∪B http://img838.imageshack.us/img838/3996/intersectab1.png

และแยก:

∖ AB

รูปภาพของ A ∖ AB http://img830.imageshack.us/img830/273/intersectab2.png

AB

รูปภาพของ AB http://img828.imageshack.us/img828/7413/intersectab3.png

และ B ∖ AB

รูปภาพของ B ∖ AB http://img839.imageshack.us/img839/5458/intersectab4.png

นั่นคือ - รูปทรงเรขาคณิตที่แตกต่างกันสามแบบสำหรับทุกคู่ที่ตัดกัน

ก่อนอื่นเรามาสร้างมุมมองของรูปทรงเรขาคณิตที่ตัดกันทั้งหมด สมมติว่าชื่อตารางของคุณคือpolygons_tableเราจะใช้:

CREATE OR REPLACE VIEW p_intersections AS    -- Create a view with the 
SELECT t1.the_geom as t1_geom,               -- intersecting geoms. Each pair
       t2.the_geom as t2_geom                -- appears once (t2.id<t2.id)
    FROM polygons_table t1, polygons_table t2  
         WHERE t1.id<t2.id AND t1.the_geom && t2.the_geom 
                           AND intersects t1.the_geom, t2.the_geom;

ตอนนี้เรามีมุมมอง (ในทางปฏิบัติ, ตารางอ่านอย่างเดียว) ที่เก็บคู่ของ geoms ที่ตัดกันซึ่งแต่ละคู่จะปรากฏเพียงครั้งเดียวเนื่องจากt1.id<t2.idสภาพ

ตอนนี้ขอรวบรวมแยกคุณ - A∖AB, ABและB∖ABใช้ SQL ของUNIONทั้งสามคำสั่ง:

--AB:
SELECT ST_intersection(t1.the_geom, t2.the_geom) 
    AS geom 
    FROM p_intersections

UNION 

--AAB:
SELECT ST_Difference(t1.the_geom, t2.the_geom) 
    AS geom 
    FROM p_intersections

UNION

--BAB:
SELECT ST_Difference(t2.the_geom, t1.the_geom) 
    AS geom 
    FROM p_intersections;

หมายเหตุ:

  1. &&ผู้ประกอบการจะใช้เป็นตัวกรองก่อนที่จะintersectsดำเนินการเพื่อปรับปรุงประสิทธิภาพ
  2. ฉันเลือกที่จะสร้างVIEWข้อความค้นหาขนาดยักษ์แทนที่จะเป็นหนึ่งข้อความค้นหา เพื่อความสะดวกเท่านั้น
  3. หากคุณหมายถึงABการรวมกันไม่ใช่การแยกของAและB- ใช้ ST_Union แทนการ st_intersection ที่UNIONแบบสอบถามในสถานที่ที่เหมาะสม
  4. สัญญาณเป็นสัญญาณ Unicode สำหรับชุดที่แตกต่างกัน; ลบออกจากรหัสหากสับสนฐานข้อมูลของคุณ
  5. รูปภาพมารยาทของดีหมวดหมู่ชุดทฤษฎีมีเดียคอมมอนส์

ตั๋วบั๊กของฉันใน meta: meta.gis.stackexchange.com/questions/79/…
Adam Matan

คำอธิบายที่ดี! ผลลัพธ์จะเหมือนกับในโซลูชัน SCW แต่วิธีการของเขาควรเร็วขึ้น (ไม่คำนวณ / หรือเก็บ / แยกเพิ่มเติมของ A และ B)
stachu

ขอบคุณ! ฉันคิดว่าฉันไม่ได้เก็บข้อมูลเพิ่มเติมใด ๆ เพราะฉันเพียงสร้างมุมมอง SQL ไม่ใช่ตาราง
Adam Matan

ใช่นั่นเป็นความจริง แต่คุณคำนวณจุดตัด A และ B เพิ่มเติมซึ่งไม่จำเป็น
stachu

5
รูปภาพในคำตอบนี้ไม่ทำงานอีกต่อไป
Fezter

8

สิ่งที่คุณกำลังอธิบายคือวิธีที่ผู้ประกอบการ Unionทำงานใน ArcGIS แต่มันแตกต่างจาก Union หรือ Intersection เล็กน้อยในโลก GEOS คู่มือหุ่นดีมีตัวอย่างของวิธีการทำงานในชุด GEOS อย่างไรก็ตามวิกิ PostGIS มีตัวอย่างที่ดีในการใช้ linework ซึ่งควรทำเคล็ดลับสำหรับคุณ

หรือคุณสามารถคำนวณสามสิ่ง:

  1. ST_Intersection (geom_a, geom_b)
  2. ST_Difference (geom_a, geom_b)
  3. ST_Difference (geom_b, geom_a)

สิ่งเหล่านี้ควรเป็นรูปหลายเหลี่ยมสามรูปที่คุณพูดถึงในสัญลักษณ์แสดงหัวข้อย่อยที่สองของคุณ


2
ตัวอย่างวิกิของ PostGIS นั้นดี
fmark

ST_Intersects จะไม่ส่งคืนบูลีนหรือไม่หากพวกเขาตัดกันหรือไม่ ฉันคิดว่า ST_Interection จะใช้งานได้
เจสัน

ใช่พิมพ์ผิดในส่วนของฉัน - แก้ไขในตอนนี้ขอบคุณ Jason!
scw

-2

สิ่งที่ต้องการ:

INSERT INTO new_table ค่า (เลือก id, the_geom จาก old_table โดยที่ st_intersects (the_geom, (เลือก the_geom จาก old_table โดยที่ id = '123')) = true

แก้ไข: คุณต้องการจุดตัดของรูปหลายเหลี่ยมที่แท้จริง

INSERT INTO new_table ค่า ((เลือก id, ST_Intersection (the_geom, (เลือก the_geom จาก old โดยที่ id = 123))

ดูว่ามันออกมา

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