Hay nói, là một tài tài của, qua, qua, qua một tài khác, qua giữ, qua một tài khác
Bạn có thể sử dụng truy vấn đệ quy để khám phá hàng xóm gần nhất của từng điểm bắt đầu từ mỗi đầu dòng được phát hiện mà bạn muốn xây dựng.
Điều kiện tiên quyết : chuẩn bị một lớp postgis với các điểm của bạn và một lớp khác với một đối tượng Multi-linestring duy nhất chứa đường của bạn. Hai lớp phải nằm trên cùng một CRS. Đây là mã cho tập dữ liệu thử nghiệm mà tôi đã tạo, vui lòng sửa đổi nó nếu cần. (Đã thử nghiệm trên postgres 9.2 và 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),
Dưới đây là các bước :
Tạo cho mỗi điểm danh sách của mọi hàng xóm và khoảng cách của họ đáp ứng ba tiêu chí.
- Khoảng cách không được vượt quá ngưỡng do người dùng xác định (điều này sẽ tránh liên kết đến điểm bị cô lập)
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
),
- Đường đi trực tiếp không được qua đường
graph as (
SELECt graph_full.*
FROM graph_full RIGHT JOIN
roads ON st_intersects(graph_full.geom,roads.geom) = false
),
Khoảng cách không được vượt quá tỷ lệ do người dùng xác định khoảng cách từ hàng xóm gần nhất (điều này sẽ phù hợp với số hóa không đều hơn khoảng cách cố định) Phần này thực sự quá khó để thực hiện, bám vào bán kính tìm kiếm cố định
Hãy gọi bảng này là "biểu đồ"
Chọn điểm cuối của dòng bằng cách nối vào biểu đồ và chỉ giữ lại điểm có chính xác một mục trong biểu đồ.
eol as (
SELECT points.* FROM
points JOIN
(SELECT id, count(*) FROM graph
GROUP BY id
HAVING count(*)= 1) sel
ON points.id = sel.id),
Hãy gọi bảng này là "eol" (cuối dòng) có
dễ không? rằng phần thưởng cho việc thực hiện một biểu đồ tuyệt vời nhưng mọi thứ sẽ tiếp tục phát điên ở bước tiếp theo
Thiết lập một truy vấn đệ quy sẽ chuyển từ hàng xóm sang hàng xóm bắt đầu từ mỗi eol
- Khởi tạo truy vấn đệ quy bằng bảng eol và thêm bộ đếm cho độ sâu, bộ tổng hợp cho đường dẫn và hàm tạo hình học để xây dựng các dòng
- Chuyển sang lần lặp tiếp theo bằng cách chuyển sang hàng xóm gần nhất bằng biểu đồ và kiểm tra xem bạn có bao giờ đi lùi bằng đường dẫn không
- Sau khi lặp lại xong, chỉ giữ đường dẫn dài nhất cho mỗi điểm bắt đầu (nếu tập dữ liệu của bạn bao gồm giao điểm tiềm năng giữa các dòng mong đợi mà phần đó sẽ cần nhiều điều kiện hơn)
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
Hãy gọi bảng này là "recurse_eol"
Chỉ giữ dòng dài nhất cho mỗi điểm bắt đầu và xóa mọi đường dẫn trùng lặp chính xác Ví dụ: đường dẫn 1,2,3,5 VÀ 5,3,2,1 là cùng một dòng được phát hiện bởi hai điểm khác biệt "cuối dòng"
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
Kiểm tra thủ công các lỗi còn lại (các điểm bị cô lập, các đường chồng chéo, đường có hình dạng kỳ lạ)
Được cập nhật như đã hứa, tôi vẫn không thể hiểu tại sao đôi khi truy vấn đệ quy không cho kết quả chính xác khi bắt đầu từ eol đối diện của cùng một dòng để một số trùng lặp có thể vẫn còn trong lớp kết quả như bây giờ.
Hãy hỏi tôi hoàn toàn hiểu rằng mã này cần thêm ý kiến. Đây là truy vấn đầy đủ:
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