จะสร้างบรรทัดเพื่อให้เห็นความแตกต่างระหว่างคุณลักษณะรูปหลายเหลี่ยมใน PostGIS ได้อย่างไร


15

ฉันมีตาราง PostGIS polygon_bพร้อมคุณสมบัติรูปหลายเหลี่ยม นอกจากนี้ยังมีตารางpolygon_aที่มีรูปหลายเหลี่ยมเหมือนกันpolygon_bแต่มีการเปลี่ยนแปลงเล็กน้อย ตอนนี้ฉันต้องการสร้างเส้นเพื่อให้เห็นความแตกต่างระหว่างคุณลักษณะรูปหลายเหลี่ยม

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

ฉันคิดว่าST_ExteriorRingและST_Differenceจะทำงาน แต่ประโยคที่ดูเหมือนว่าจะค่อนข้างยุ่งยาก

CREATE VIEW line_difference AS SELECT
row_number() over() AS gid,
g.geom::geometry(LineString, yourSRID) AS geom
FROM 
    (SELECT
    (ST_Dump(COALESCE(ST_Difference(ST_ExteriorRing(polygon_a.geom), ST_ExteriorRing(polygon_b.geom))))).geom AS geom
    FROM polygon_a, polygon_b
    WHERE 
    -- ?
    ) AS g;

มีใครช่วยฉันบ้าง

แก้ไข 1

ตามที่โพสต์โดย 'เอียง' ฉันได้ลองแล้วST_Overlaps(polygon_a.geom, polygon_b.geom) AND NOT ST_Touches(polygon_a.geom, polygon_b.geom)แต่ผลลัพธ์ไม่เป็นไปตามที่คาดไว้

CREATE VIEW line_difference AS SELECT
row_number() over() AS gid,
g.geom::geometry(LineString, your_SRID) AS geom
FROM 
    (SELECT
    (ST_Dump(COALESCE(ST_Difference(ST_ExteriorRing(polygon_a.geom), ST_ExteriorRing(polygon_b.geom))))).geom AS geom
    FROM polygon_a, polygon_b
    WHERE 
    ST_Overlaps(polygon_a.geom, polygon_b.geom) AND NOT ST_Touches(polygon_a.geom, polygon_b.geom))
     AS g;

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

แก้ไข 2

workupload.com/file/J0WBvRBb (ชุดข้อมูลตัวอย่าง)


ฉันได้ลองเปลี่ยนรูปหลายเหลี่ยมเป็นหลายบรรทัดก่อนที่จะใช้ ST_Difference แต่ผลลัพธ์ยังแปลกอยู่

CREATE VIEW multiline_a AS SELECT
row_number() over() as gid,
ST_Union(ST_ExteriorRIng(polygon_a.geom))::geometry(multilinestring, 4326) AS geom
FROM
polygon_a;

CREATE VIEW multiline_b AS SELECT
row_number() over() as gid,
ST_Union(ST_ExteriorRIng(polygon_b.geom))::geometry(multilinestring, 4326) AS geom
FROM
polygon_b;

CREATE VIEW line_difference AS SELECT
row_number() over() as gid,
g.geom
FROM
    (SELECT
    (ST_Dump(COALESCE(ST_Difference(multiline_a.geom, multiline_b.geom)))).geom::geometry(linestring, 4326) AS geom
    FROM
    multiline_a, multiline_b)
As g;

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


ดูเหมือนคำถามโทโพโลยี คุณต้องการระบุกลุ่มที่ไม่ครอบคลุมโดยเลเยอร์อื่น ฉันไม่ได้ทำงานกับโทโพโลยีของ PostGIS มากและไม่สามารถให้คำตอบได้โดยตรง แต่ฉันขอแนะนำให้คุณดูเพิ่มเติม
โทมัส

น่าสนใจคุณมีชุดข้อมูลตัวอย่างสำหรับการดาวน์โหลดหรือไม่?
huckfinn

คำตอบ:


10

นี่คือเทคนิคใหม่บางอย่างโดยใช้:

  • EXCEPTเพื่อลบรูปทรงเรขาคณิตออกจากตารางทั้งสองที่เหมือนกันดังนั้นเราจึงสามารถมุ่งเน้นเฉพาะรูปทรงเรขาคณิตที่ไม่ซ้ำกันในแต่ละตาราง ( A_onlyและB_only)
  • ST_Snap เพื่อรับการเข้ารหัสที่แน่นอนสำหรับผู้ประกอบการซ้อน
  • ใช้ST_SymDifferenceโอเปอเรเตอร์การซ้อนทับเพื่อค้นหาความแตกต่างแบบสมมาตรระหว่างชุดเรขาคณิตสองชุดเพื่อแสดงความแตกต่าง อัปเดต : ST_Differenceแสดงผลลัพธ์เดียวกันสำหรับตัวอย่างนี้ คุณสามารถลองฟังก์ชั่นเพื่อดูว่าพวกมันได้อะไร

สิ่งนี้ควรจะได้รับสิ่งที่คุณคาดหวัง:

-- CREATE OR REPLACE VIEW polygon_SymDifference AS
SELECT row_number() OVER () rn, *
FROM (
  SELECT (ST_Dump(ST_SymDifference(ST_Snap(A, B, tol), ST_Snap(B, A, tol)))).*
  FROM (
    SELECT ST_Union(DISTINCT A_only.geom) A, ST_Union(DISTINCT B_only.geom) B, 1e-5 tol
    FROM (
      SELECT ST_Boundary(geom) geom FROM polygon_a
      EXCEPT SELECT ST_Boundary(geom) geom FROM polygon_b
    ) A_only,
    (
      SELECT ST_Boundary(geom) geom FROM polygon_b
      EXCEPT SELECT ST_Boundary(geom) geom FROM polygon_a
    ) B_only
  ) s
) s;

 rn |                                        geom
----+-------------------------------------------------------------------------------------
  1 | LINESTRING(206.234028204842 -92.0360704110685,219.846021625456 -92.5340701703592)
  2 | LINESTRING(18.556700448873 -36.4496098325257,44.44438533894 -40.5104231486146)
  3 | LINESTRING(-131.974995802602 -38.6145334122719,-114.067738329597 -39.0215165366584)
(3 rows)

สามบรรทัด


ในการคลายคำตอบนี้ให้มากขึ้นขั้นตอนแรกที่มีST_Boundaryขอบเขตของรูปหลายเหลี่ยมแต่ละรูปแทนที่จะเป็นรูปภายนอก ตัวอย่างเช่นหากมีหลุมสิ่งเหล่านี้จะถูกติดตามโดยขอบเขต

ส่วนEXCEPTคำสั่งจะใช้ในการลบรูปทรงเรขาคณิตออกจาก A ซึ่งเป็นส่วนหนึ่งของ B และแถวจาก B ที่เป็นส่วนหนึ่งของ A ซึ่งจะช่วยลดจำนวนแถวที่เป็นส่วนหนึ่งของ A เท่านั้นและเป็นส่วนหนึ่งของ B เท่านั้น เช่นรับ A_only:

SELECT ST_Boundary(geom) geom FROM polygon_a
EXCEPT SELECT ST_Boundary(geom) geom FROM polygon_b

นี่คือ 6 แถวของ A_only และ 3 แถวของ B_only: A_only B_only

ถัดไปST_Union(DISTINCT A_only.geom)ใช้ในการรวม linework เข้ากับรูปทรงเรขาคณิตเดียวโดยทั่วไปคือ MultiLineString

ST_Snap ใช้เพื่อจัดเรียงโหนดจากเรขาคณิตหนึ่งไปยังอีก ตัวอย่างเช่นST_Snap(A, B, tol)จะใช้เรขาคณิต A และเพิ่มโหนดเพิ่มเติมจากเรขาคณิต B หรือย้ายไปยังเรขาคณิต B หากพวกเขาอยู่ในtolระยะทาง อาจมีหลายวิธีในการใช้ฟังก์ชั่นเหล่านี้ แต่ความคิดคือการรับพิกัดจากแต่ละรูปทรงเรขาคณิตที่แน่นอนซึ่งกันและกัน ดังนั้นรูปทรงเรขาคณิตทั้งสองหลังจากหักมุมมีลักษณะดังนี้:

การจัดชิด B หัก

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


คำตอบที่ดี ฉันสงสัยว่าคุณใช้อะไรเพื่อดูผลลัพธ์ของข้อความค้นหาตัวกลางของคุณ มันไม่ได้ดูเหมือน qgis ทันทีและบางทีฉันอาจเป็นสิ่งที่ทำให้เร็วขึ้นเล็กน้อย?
RoperMaps

1
ฉันใช้ JTS Testbuilder เพื่อดูและประมวลผลรูปทรงเรขาคณิต มันเป็นเอ็นจิ้นรูปทรงเรขาคณิตที่เกี่ยวข้องกับ GEOS และ Shapely แต่มี GUI ที่ใช้ Java
Mike T

มีวิธีใดที่จะเพิกเฉย / ข้าม 'การตัดกันแบบไม่มีการซ้อนระหว่าง LINESTRING' หรือไม่? รูปหลายเหลี่ยมอินพุตทั้งหมดดูเหมือนว่าใช้ได้ (ตรวจสอบกับตัวตรวจสอบรูปทรงเรขาคณิตของ QGIS)
eclipsed_by_the_moon

1
'ST_Boundary (ST_SnapToGrid (geom, 0.001))' แทน 'ST_Boundary (geom)' แก้ปัญหาได้
eclipsed_by_the_moon

6

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

โหนดรูปหลายเหลี่ยม

โพลี

โหนดของเซ็กเมนต์ "แตกต่าง" รูปหลายเหลี่ยม B

ความแตกต่าง

โชคไม่ดีที่การแสดงนี้มีความแตกต่างในโครงสร้างเซกเมนต์เท่านั้น แต่ฉันหวังว่ามันจะเป็นจุดเริ่มต้นและใช้งานได้เช่นนี้:

หลังจากกระบวนการดาวน์โหลดและคลายซิปฉันได้นำเข้าชุดข้อมูลโดยใช้ PostgrSQL 9.46, PostGIS 2.1 ภายใต้ Debian Linux Jessie พร้อมคำสั่ง

$ createdb gis-se
$ psql gis-se < /usr/share/postgis-2.1/postgis.sql
$ psql gis-se < /usr/share/postgis-2.1/spatial_ref_sys.sql
$ shp2pgsql -S polygon_a | psql gis-se
$ shp2pgsql -S polygon_b | psql gis-se

สมมติว่าเซ็กเมนต์ของรูปหลายเหลี่ยม A ไม่ได้อยู่ใน B และรองจาก vera ฉันพยายามสร้างความแตกต่างระหว่างเซ็กเมนต์ของชุดรูปหลายเหลี่ยมทั้งสองโดยไม่สนใจสมาชิกภาพเซ็กเมนต์กับรูปหลายเหลี่ยมในแต่ละกลุ่ม สำหรับเหตุผลเกี่ยวกับการสอนฉันกำหนดสิ่ง SQL ในหลายมุมมอง

ที่สอดคล้องกับโพสต์ GIS-SEนี้ฉันแยกส่วนรูปหลายเหลี่ยมออกเป็นตารางเซ็กเมนต์segments_aและsegments_b

-- Segments of the polygon A
CREATE VIEW segments_a AS SELECT sp, ep
FROM
   -- extract the endpoints for every 2-point line segment for each linestring
   (SELECT
      ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) as sp,
      ST_PointN(geom, generate_series(2, ST_NPoints(geom)  )) as ep
    FROM
    -- extract the individual linestrings
     (SELECT (ST_Dump(ST_Boundary(geom))).geom
      FROM polygon_a
     ) AS linestrings
    -- be sure that nothing is scrambled
    ORDER BY sp, ep
) AS segments;

ตารางส่วนรูปหลายเหลี่ยม A:

SELECT 
  st_astext(sp) AS sp, 
  st_astext(ep) AS ep 
FROM segments_a 
LIMIT 3;
                    sp                     |                 ep
-------------------------------------------+--------------------------------------------
POINT(-292.268907321861 95.0342877387557)  | POINT(-287.118411917425 99.4165242769195)
POINT(-287.118411917425 99.4165242769195)  | POINT(-264.62129248575 93.2470010145007)
POINT(-277.459563916327 -44.5629543976138) | POINT(-292.268907321861 95.03428773875

ขั้นตอนเดียวกันถูกนำไปใช้กับรูปหลายเหลี่ยม B.

-- Segments of the polygon B
CREATE VIEW segments_b AS SELECT sp, ep
FROM
   -- extract the endpoints for every 2-point line segment for each linestring
   (SELECT
      ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) as sp,
      ST_PointN(geom, generate_series(2, ST_NPoints(geom)  )) as ep
    FROM
    -- extract the individual linestrings
     (SELECT (ST_Dump(ST_Boundary(geom))).geom
      FROM polygon_b
     ) AS linestrings
    -- be sure that nothing is scrambled
    ORDER BY sp, ep
) AS segments;

ตารางส่วนรูปหลายเหลี่ยม B

SELECT
  st_astext(sp) AS sp, 
  st_astext(ep) AS ep 
FROM segments_b 
LIMIT 3;
                    sp                     |                    ep
-------------------------------------------+-------------------------------------------
POINT(-292.268907321861 95.0342877387557)  | POINT(-287.118411917425 99.4165242769195)
POINT(-287.118411917425 99.4165242769195)  | POINT(-264.62129248575 93.2470010145007)
POINT(-277.459563916327 -44.5629543976138) | POINT(-292.268907321861 95.0342877387557)
...                        

segments_diff_{a,b}ฉันสามารถสร้างมุมมองตารางความแตกต่างที่มีชื่อว่า ความแตกต่างได้มาจากการไม่เกิดขึ้นของจุดเริ่มต้นหรือจุดสิ้นสุดที่เรียงลำดับในชุดเซกเมนต์ A และ B

CREATE VIEW segments_diff_a AS
SELECT st_makeline(b.sp, b.ep) as geom
FROM segments_b as b
LEFT JOIN segments_a as a ON (a.sp=b.sp and a.ep = b.ep)
-- filter segments without corresponding stuff in polygon A
WHERE a.sp IS NULL;

บีบีซี

และสิ่งที่สมบูรณ์:

CREATE VIEW segments_diff_b AS
SELECT st_makeline(a.sp, a.ep) as geom
FROM segments_a as a
LEFT JOIN segments_b as b ON (a.sp=b.sp and a.ep = b.ep)
-- filter segments without corresponding stuff in polygon B
WHERE b.sp IS NULL;

segs ต่าง

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

CREATE VIEW segments_bi AS 
SELECT distinct sp, ep
FROM (
 SELECT
      ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) as sp,
      ST_PointN(geom, generate_series(2, ST_NPoints(geom)  )) as ep
 FROM (
   SELECT st_difference(b.seg, a.seg) as geom FROM 
      segments_diff_a as a, segments_diff_b as b 
      WHERE st_intersects(a.seg, b.seg)
    ) as cut
  ) as segments
  WHERE sp IS NOT NULL AND ep IS NOT NULL
ORDER BY sp, ep;

แต่ด้วยผลลัพธ์ที่แปลก ...

เวอร์ชั่นตัด


ขอบคุณสำหรับความพยายามของคุณ. ผลลัพธ์ก็แปลกดี ฉันแค่สงสัยว่า ST_HausdorffDistance () อาจช่วยตอบคำถาม: gis.stackexchange.com/questions/180593/…
Lunar Sea

หืม st_haudorffdistance ช่วยให้คุณมีความคล้ายคลึงกันไม่ใช่ส่วนที่ต้องการ (ลูกศรสีแดงชี้ไปที่)
huckfinn

มันเป็นเพียงความคิด ST_HausdorffDistance สามารถใช้เปรียบเทียบรูปทรงเรขาคณิตของทั้งสองตาราง รูปหลายเหลี่ยมนั้นไม่เท่ากับเชิงพื้นที่ที่ฉันจะสร้างเส้น ฉันไม่รู้จะทำยังไง
Sea Lunar

ดูเหมือนว่าจะเป็นเรื่องของความแม่นยำและโทโพโลยี ( gis.stackexchange.com/a/182838/26213และwebhelp.esri.com/arcgisdesktop/9.2/ … )
huckfinn

1

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

ST_Overlaps (geoma, geomb) และ! ST_Touches (geoma, geomb)

การปฏิเสธจากการสัมผัสเป็นเพราะคุณสมบัติยังทับซ้อนกันหากมีเพียงเส้นขอบของพวกเขาที่แบ่งปันตำแหน่งจุดยอดเดียวกัน

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