คุณสามารถใช้เคียวรีแบบเรียกซ้ำเพื่อสำรวจเพื่อนบ้านที่ใกล้ที่สุดของแต่ละจุดเริ่มต้นจากจุดสิ้นสุดแต่ละบรรทัดที่ตรวจพบที่คุณต้องการสร้าง
สิ่งที่ต้องทำก่อน : เตรียมเลเยอร์ 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),
นี่คือขั้นตอน :
สร้างสำหรับแต่ละจุดรายการของเพื่อนบ้านทุกคนและระยะทางของพวกเขาที่ตรงกับเกณฑ์เหล่านี้สาม
- ระยะทางต้องไม่เกินเกณฑ์ที่ผู้ใช้กำหนด (ซึ่งจะหลีกเลี่ยงการเชื่อมโยงไปยังจุดแยก)
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),
เรียกตารางนี้ว่า "eol" (ท้ายบรรทัด)
ง่ายไหม ว่ารางวัลสำหรับการทำกราฟที่ยอดเยี่ยม แต่สิ่งที่ค้างไว้จะบ้าไปในขั้นตอนต่อไป
ตั้งค่าแบบสอบถามแบบเรียกซ้ำซึ่งจะวนจากเพื่อนบ้านเป็นเพื่อนบ้านเริ่มต้นจากแต่ละ 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"
เก็บเฉพาะบรรทัดที่ยาวที่สุดสำหรับแต่ละจุดเริ่มต้นและลบเส้นทางที่ซ้ำกันทุกตัวอย่างตัวอย่าง: เส้นทาง 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
ตรวจสอบข้อผิดพลาดที่เหลือด้วยตนเอง (จุดแยกเส้นที่เหลื่อมกันถนนที่มีรูปร่างแปลก ๆ )
อัปเดตตามที่สัญญาไว้ฉันยังไม่สามารถระบุได้ว่าเหตุใดข้อความค้นหาซ้ำบางครั้งจึงไม่ให้ผลลัพธ์ที่เหมือนกันเมื่อเริ่มต้นจาก 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