วาดเส้นระหว่างจุดที่ระยะทางเฉพาะใน PostGIS?


9

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

ฉันหวังว่าจะใช้PostGISฟังก์ชั่นในการทำสิ่งนี้ แต่ฉันเปิดรับข้อเสนอแนะนี่เป็นข้อมูลจาก.shpไฟล์

แก้ไข 1: อัปเดตรูปภาพเพื่อแสดงวิธีแก้ปัญหาในอุดมคติ

การวาดเส้นจะขึ้นอยู่กับระยะห่างระหว่างจุดนั้นอย่างหมดจดไม่มีอะไรที่ฉันสามารถใช้เพื่อจัดกลุ่มพวกเขาโดย เป็นการดีที่นี่จะเป็นคะแนนที่ระยะทางสูงสุดที่ระบุไว้ตามเส้นที่ฉาย? และโดยเส้นที่ฉายผมหมายถึงหาจุดที่ 1 จากนั้นจุดต่อไปที่ใกล้ที่สุดจากนั้นฉายเส้นและตรวจสอบว่ามีจุดใด ๆ ในบรรทัดนี้ที่ระยะทางสูงสุดกับจุดใดจุดหนึ่งที่อยู่บนเส้น


1
คุณวางแผนว่าจะใช้ซอฟต์แวร์ใด
ArMoraer

คุณกำลังพยายามที่จะเปลี่ยนสิ่งเหล่านี้เป็นทางเท้า?
DPSSpatial

ฉันหวังว่าจะใช้ฟังก์ชั่น PostGIS เพื่อทำสิ่งนี้ แต่ฉันเปิดรับข้อเสนอแนะนี่เป็นข้อมูลจากไฟล์. shp
Mahakala

1
คุณสามารถแสดงให้เห็นว่าคุณต้องการเชื่อมต่อกับรูปวาดของคุณหรือในรูปวาดอื่น ๆ อย่างแน่นอนหรือไม่? แต่ละครั้งมีเพียงสองคะแนนเท่านั้นหรือไม่ หรือสาม ระยะห่างระหว่างจุดที่ควรเชื่อมต่ออยู่เสมอเหมือนกันหรือเป็น "เพียง" ต่ำกว่าขีด จำกัด ที่กำหนดหรือไม่
Peter HorsbøllMøller

1
ขอขอบคุณทั้ง @ dbaston และ MarHoff ฉันไม่มีเวลาที่จะทดสอบความคิดของคุณจนถึงปลายเดือนเมษายนฉันหวังว่าฉันจะสามารถแบ่งเงินระหว่างคุณได้ ดังนั้นฉันจะยอมรับคำตอบของเขา ขอบคุณทุกคนที่ใช้เวลาตอบ! ชุมชนที่ยอดเยี่ยมที่จะเป็นส่วนหนึ่งของ :-)
Mahakala

คำตอบ:


8

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

สิ่งที่ต้องทำก่อน : เตรียมเลเยอร์ postgis ด้วยจุดของคุณและอีกจุดหนึ่งด้วยวัตถุ Multi-linestring เดียวที่มีถนนของคุณ สองเลเยอร์จะต้องอยู่ใน CRS เดียวกัน นี่คือรหัสสำหรับชุดข้อมูลทดสอบที่ฉันสร้างโปรดแก้ไขตามที่จำเป็น (ทดสอบกับ postgres 9.2 และ postgis 2.1)

WITH RECURSIVE
points as (SELECT id, st_transform((st_dump(wkb_geometry)).geom,2154) as geom, my_comment as com FROM mypoints),
roads as (SELECT st_transform(ST_union(wkb_geometry),2154) as geom from highway),

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

นี่คือขั้นตอน :

  1. สร้างสำหรับแต่ละจุดรายการของเพื่อนบ้านทุกคนและระยะทางของพวกเขาที่ตรงกับเกณฑ์เหล่านี้สาม

    • ระยะทางต้องไม่เกินเกณฑ์ที่ผู้ใช้กำหนด (ซึ่งจะหลีกเลี่ยงการเชื่อมโยงไปยังจุดแยก) ป้อนคำอธิบายรูปภาพที่นี่
      graph_full as (
      SELECT a.id, b.id as link_id, a.com, st_makeline(a.geom,b.geom) as geom, st_distance(a.geom,b.geom) as distance
      FROM points a
      LEFT JOIN points b ON a.id<>b.id
      WHERE st_distance(a.geom,b.geom) <= 15
      ),
    • เส้นทางตรงต้องไม่ข้ามถนน ป้อนคำอธิบายรูปภาพที่นี่
      graph as (
      SELECt graph_full.*
      FROM graph_full RIGHT JOIN
      roads ON st_intersects(graph_full.geom,roads.geom) = false
      ),
    • ระยะทางจะต้องไม่เกินอัตราส่วนที่ผู้ใช้กำหนดจากระยะทางจากเพื่อนบ้านที่ใกล้ที่สุด (ซึ่งควรปรับให้เหมาะกับการทำให้เป็นดิจิตอลมากกว่าปกติระยะทางคงที่) ส่วนนี้ยากเกินกว่าที่จะนำไปใช้จริง

    เรียกตารางนี้ว่า "กราฟ"

  2. เลือกจุดสิ้นสุดของบรรทัดโดยการเข้าร่วมกับกราฟและรักษาเฉพาะจุดที่มีรายการเดียวในกราฟ ป้อนคำอธิบายรูปภาพที่นี่

    eol as (
    SELECT points.* FROM
    points  JOIN
    (SELECT id, count(*) FROM graph 
    GROUP BY id
    HAVING count(*)= 1) sel
    ON points.id = sel.id),

    เรียกตารางนี้ว่า "eol" (ท้ายบรรทัด)
    ง่ายไหม ว่ารางวัลสำหรับการทำกราฟที่ยอดเยี่ยม แต่สิ่งที่ค้างไว้จะบ้าไปในขั้นตอนต่อไป

  3. ตั้งค่าแบบสอบถามแบบเรียกซ้ำซึ่งจะวนจากเพื่อนบ้านเป็นเพื่อนบ้านเริ่มต้นจากแต่ละ eol ป้อนคำอธิบายรูปภาพที่นี่

    • เริ่มต้นแบบสอบถามแบบเรียกซ้ำโดยใช้ตาราง eol และเพิ่มตัวนับสำหรับความลึกตัวรวบรวมสำหรับเส้นทางและตัวสร้างรูปทรงเรขาคณิตเพื่อสร้างเส้น
    • ย้ายไปที่การวนซ้ำครั้งถัดไปโดยเปลี่ยนเป็นเพื่อนบ้านที่ใกล้ที่สุดโดยใช้กราฟและตรวจสอบว่าคุณไม่เคยย้อนกลับโดยใช้เส้นทาง
    • หลังจากการวนซ้ำเสร็จสิ้นจะเก็บเฉพาะเส้นทางที่ยาวที่สุดสำหรับแต่ละจุดเริ่มต้น (หากชุดข้อมูลของคุณมีการตัดกันที่อาจเกิดขึ้นระหว่างบรรทัดที่คาดว่าส่วนนั้นจะต้องมีเงื่อนไขเพิ่มเติม)
    recurse_eol (id, link_id, depth, path, start_id, geom) AS (--initialisation
    SELECT id, link_id, depth, path, start_id, geom FROM (
        SELECT eol.id, graph.link_id,1 as depth,
        ARRAY[eol.id, graph.link_id] as path,
        eol.id as start_id,
        graph.geom as geom,
        (row_number() OVER (PARTITION BY eol.id ORDER BY distance asc))=1 as test
        FROM eol JOIn graph ON eol.id = graph.id 
        ) foo
    WHERE test = true
    
    UNION ALL ---here start the recursive part
    
    SELECT id, link_id, depth, path, start_id, geom  FROM (
        SELECT graph.id, graph.link_id, r.depth+1 as depth,
        path || graph.link_id as path,
        r.start_id,
        ST_union(r.geom,graph.geom) as geom,
        (row_number() OVER (PARTITION BY r.id ORDER BY distance asc))=1 as test
        FROM recurse_eol r JOIN graph ON r.link_id = graph.id AND NOT graph.link_id = ANY(path)) foo
    WHERE test = true AND depth < 1000), --this last line is a safe guard to stop recurring after 1000 run adapt it as needed

    เรียกตารางนี้ว่า "recurse_eol"

  4. เก็บเฉพาะบรรทัดที่ยาวที่สุดสำหรับแต่ละจุดเริ่มต้นและลบเส้นทางที่ซ้ำกันทุกตัวอย่างตัวอย่าง: เส้นทาง 1,2,3,5 และ 5,3,2,1 เป็นบรรทัดเดียวกันที่ค้นพบโดยมีสอง differents "end of line"

    result as (SELECT start_id, path, depth, geom FROM
    (SELECT *,
    row_number() OVER (PARTITION BY array(SELECT * FROM unnest(path) ORDER BY 1))=1 as test_duplicate,
    (max(depth) OVER (PARTITION BY start_id))=depth as test_depth
    FROM recurse_eol) foo
    WHERE  test_depth = true AND test_duplicate = true)
    
    SELECT * FROM result
  5. ตรวจสอบข้อผิดพลาดที่เหลือด้วยตนเอง (จุดแยกเส้นที่เหลื่อมกันถนนที่มีรูปร่างแปลก ๆ )


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

อย่าลังเลที่จะถามฉันทั้งหมดว่ารหัสนี้ต้องการความคิดเห็นเพิ่มเติม นี่คือแบบสอบถามแบบเต็ม:

WITH RECURSIVE
points as (SELECT id, st_transform((st_dump(wkb_geometry)).geom,2154) as geom, my_comment as com FROM mypoints),
roads as (SELECT st_transform(ST_union(wkb_geometry),2154) as geom from highway),

graph_full as (
    SELECT a.id, b.id as link_id, a.com, st_makeline(a.geom,b.geom) as geom, st_distance(a.geom,b.geom) as distance
    FROM points a
    LEFT JOIN points b ON a.id<>b.id
    WHERE st_distance(a.geom,b.geom) <= 15
    ),

graph as (
    SELECt graph_full.*
    FROM graph_full RIGHT JOIN
    roads ON st_intersects(graph_full.geom,roads.geom) = false
    ),

eol as (
    SELECT points.* FROM
    points  JOIN
        (SELECT id, count(*) FROM graph 
        GROUP BY id
        HAVING count(*)= 1) sel
    ON points.id = sel.id),


recurse_eol (id, link_id, depth, path, start_id, geom) AS (
    SELECT id, link_id, depth, path, start_id, geom FROM (
        SELECT eol.id, graph.link_id,1 as depth,
        ARRAY[eol.id, graph.link_id] as path,
        eol.id as start_id,
        graph.geom as geom,
        (row_number() OVER (PARTITION BY eol.id ORDER BY distance asc))=1 as test
        FROM eol JOIn graph ON eol.id = graph.id 
        ) foo
    WHERE test = true

UNION ALL
    SELECT id, link_id, depth, path, start_id, geom  FROM (
        SELECT graph.id, graph.link_id, r.depth+1 as depth,
        path || graph.link_id as path,
        r.start_id,
        ST_union(r.geom,graph.geom) as geom,
        (row_number() OVER (PARTITION BY r.id ORDER BY distance asc))=1 as test
        FROM recurse_eol r JOIN graph ON r.link_id = graph.id AND NOT graph.link_id = ANY(path)) foo
    WHERE test = true AND depth < 1000),

result as (SELECT start_id, path, depth, geom FROM
    (SELECT *,
    row_number() OVER (PARTITION BY array(SELECT * FROM unnest(path) ORDER BY 1))=1 as test_duplicate,
    (max(depth) OVER (PARTITION BY start_id))=depth as test_depth
    FROM recurse_eol) foo
WHERE  test_depth = true AND test_duplicate = true)

SELECT * FROM result

สวัสดี @MarHoff ขอบคุณสำหรับคำตอบของคุณฉันมีบางอย่างที่จะไปหลังจาก ฉันไม่ได้คาดหวังวิธีการแก้ปัญหาอย่างเต็มรูปแบบเพียงตัวชี้ที่จะหาคำตอบ ฉันต้องการที่จะเข้าใจมากขึ้นและฉันจะขุดต่อไปและอาจมีคำถามเพิ่มเติมในภายหลัง ฉันต้องเข้าใจขั้นตอนวิธีการของคุณและจะพาฉันบางเวลาเลยล่ะค่ะ :)
Mahakala

มีสคริปต์ที่ใช้งานได้ดูตัวอย่างได้ที่นี่qgiscloud.com/MarHoff/test_qgiscloud_bisข้อแม้เล็ก ๆ สำหรับการทำซ้ำยังคงอยู่ ... ไม่มีรางวัลมากมายไม่มีแรงกดดันมากฉันเดาดังนั้นฉันจะปล่อยให้เป็นอิสระเมื่อฉันทำได้ ปริศนานี้สนุกแม้ว่า
MarHoff

ขอบคุณ @MarHoff ถ้าฉันทำได้ฉันจะแบ่งเงินรางวัลนี้ฉันไม่สามารถเห็นว่าฉันจะให้รางวัลกับคุณได้อย่างไร แต่ขอบคุณมากที่มองสิ่งนี้และหลักฐานของคุณ หน้าตา :) แท้
Mahakala

เสร็จสิ้น ขอบคุณสำหรับปริศนาและขอโทษที่คุยโว หากคำตอบอื่นทำเพื่อคุณแล้วมันก็โอเคโดยสิ้นเชิงบางครั้งก็ง่ายที่สุด ... คำตอบของฉันอาจจะคิดมากนิดหน่อย แม้ว่าตัวอย่างที่ดีของการใช้ CTE + แบบสอบถามแบบเรียกซ้ำ + ฟังก์ชัน Windows + postgis ในแบบสอบถามเดียว)
MarHoff

8

เมื่อ @FelixIP ชี้ให้เห็นขั้นตอนแรกคือการหาจุดที่จะทำให้แต่ละบรรทัด คุณสามารถทำได้โดยโทรไปที่ST_ClusterWithinด้วยระยะห่างสูงสุด:

SELECT
  row_number() OVER () AS cid, 
  (ST_Dump(geom)).geom 
FROM (
  SELECT unnest(st_clusterwithin(geom, 0.05)) AS geom 
  FROM inputs) sq

จากนั้นคุณจะต้องใช้ฮิวริสติกบางอย่างเพื่อสร้างเส้นผ่านทุกจุดในแต่ละคลัสเตอร์ ตัวอย่างเช่นถ้าคุณสามารถสันนิษฐานได้ว่าสายที่ต้องการจะเป็น Y-เดียวคุณสามารถเรียงลำดับคะแนนในแต่ละคลัสเตอร์และอาหารพวกเขาเข้าไปในST_MakeLine การรวมกันทั้งหมดเข้าด้วยกันจะเป็นดังนี้:

SELECT 
  ST_MakeLine(geom ORDER BY ST_Y(geom)) AS geom
FROM (
  SELECT row_number() OVER () AS cid, 
  (ST_Dump(geom)).geom FROM (
    SELECT unnest(st_clusterwithin(geom, 0.05)) AS geom 
    FROM inputs) sq) ssq 
GROUP BY cid

วิธีที่จะไป แต่วิธี Y-monotone (หรือสลับระหว่าง X / Y-monotone) จะไม่ทำงานได้ดีถ้าชุดข้อมูลมีถนนโค้ง เป็นอย่างนั้นเหรอ? อัลกอริทึมการสั่งซื้อเป็นส่วนที่ยากที่สุดของคำถามนี้ IMHO
MarHoff

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

statut ปรับผมแค่คิดว่าเคล็ดลับที่ฉันต้องตรวจสอบ ...
MarHoff

มีวิธีที่แข็งแกร่งในการทำเช่นนี้หรือไม่ที่ไม่เกี่ยวข้องกับการพยายามเรียงลำดับคะแนนที่เป็นไปได้ทั้งหมดและการค้นหาสิ่งที่ให้ความยาวรวมสั้นที่สุด?
dbaston

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