PostGIS에서 특정 거리의 점 사이에 선을 그리시겠습니까?


9

거리를 따라 점 데이터가 있으며 그 점을 간단한 컬러 선으로 바꾸고 싶습니다. 이 문제가 무엇인지, 또는이 문제를 해결하는 데 도움이되는 알고리즘이 있습니까? 거리를 따라 포인트를 선으로 바꾸고 싶습니다.

나는 이것을하기 위해 PostGIS함수 를 사용하기를 바랐 지만 제안에 열려있다 .shp. 이것은 파일 의 데이터이다 .

편집 1 :이 문제의 이상적인 솔루션을 보여주기 위해 그림을 업데이트했습니다.

선을 그리는 것은 순전히 그 지점 사이의 거리를 기준으로합니다. 그룹별로 그룹화 할 수있는 것은 없습니다. 이상적으로 이것은 투영 된 선을 따라 지정된 최대 거리의 점입니까? 그리고 투영 된 선은 첫 번째 점을 찾은 다음 가장 가까운 점을 찾은 다음 선을 투영 하고이 선의 점이 이미 선의 점과 최대 거리에 있는지 확인합니다.


1
어떤 소프트웨어를 사용 하시겠습니까?
ArMoraer

이것들을 보도로 바꾸려고합니까?
DPSSpatial

PostGIS 함수를 사용 하여이 작업을 수행하고 싶었지만 제안을 열었습니다.이 파일은 .shp 파일의 데이터입니다.
Mahakala

1
도면 또는 다른 도면에 연결하려는 점을 정확하게 보여줄 수 있습니까? 한 번에 두 점만입니까? 아니면 세? 연결해야하는 지점 사이의 거리가 항상 동일합니까? 아니면 특정 임계 값 아래에 "직접"있습니까?
Peter Horsbøll Møller

1
@ dbaston과 MarHoff 덕분에 4 월 말까지 아이디어를 테스트 할 시간이 없습니다. 바운티를 나눌 수 있기를 바랍니다.하지만 이것을 1 명에게 수여해야하며 dbaston은 나에게도 몇 가지 질문을주었습니다. 그래서 나는 그의 대답을 받아 들일 것입니다. 답변하는 데 시간이 걸린 모든 사람에게 감사드립니다! :-)의 일부가 될 훌륭한 커뮤니티
Mahakala

답변:


8

재귀 쿼리 를 사용 하여 빌드하려는 감지 된 각 끝점에서 시작하여 각 점의 가장 가까운 이웃을 탐색 할 수 있습니다 .

전제 조건 : 도로가 포함 된 단일 멀티 라인 스트링 객체를 사용하여 포인트와 포인트가있는 포스트 기스 레이어를 준비합니다. 두 계층은 동일한 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은 서로 다른 두 개의 "줄 끝"에 의해 발견 된 동일한 줄입니다.

    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- 모노톤 (또는 X / Y- 모노톤 간 전환) 접근 방식이 제대로 작동하지 않습니다. 그런가요? 주문 알고리즘은이 질문 IMHO의 가장 어려운 부분입니다.
MarHoff

@ MarHoff : 예 곡선 도로가 문제가되지만 대부분의 데이터를 자동으로 변환하려고 시도하고 나머지는 수동으로 수행해야합니다. 또는 솔루션을 찾기 위해 주제를 계속 파헤 치지 만 나머지 데이터를 수정하는 데 시간이 더 걸릴 수 있습니다. 결정하려면 결과를 평가해야합니다. 이것을 지적 해 주셔서 감사합니다!
Mahakala

Statut는 내가 체크 아웃해야 할 트릭을 생각했다 ...
MarHoff

가능한 모든 포인트 순서를 시도하지 않고 총 길이가 가장 짧은 방법을 찾는 것과 관련이없는 강력한 방법이 있습니까?
dbaston

이러한 점 세트가 항상 도로를 따르는 경우 점의 위치를 ​​도로 세그먼트 (ST_Line_Locate_Point)에 투영 한 다음 결과에 따라 점을 정렬합니다.
travis
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.