재귀 쿼리 를 사용 하여 빌드하려는 감지 된 각 끝점에서 시작하여 각 점의 가장 가까운 이웃을 탐색 할 수 있습니다 .
전제 조건 : 도로가 포함 된 단일 멀티 라인 스트링 객체를 사용하여 포인트와 포인트가있는 포스트 기스 레이어를 준비합니다. 두 계층은 동일한 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은 서로 다른 두 개의 "줄 끝"에 의해 발견 된 동일한 줄입니다.
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