PostGIS에서 연결된 라인 스트링을 그룹화합니까?


12

일련의 속성을 기반으로 선택한 거리 표가 있습니다 (라고 가정하십시오 speed_limit < 25). 지역적으로 인접한 거리 그룹이 있습니다. 연결된 선 스트링 세트를 GeometryCollections로 그룹화하고 싶습니다. 아래 이미지에는 두 개의 GeometryCollection이 있습니다. 하나는 빨간색 선이 있고 다른 하나는 파란색 선이 있습니다.

여기에 이미지 설명을 입력하십시오

나는 다음과 같은 라인을 따라 몇 가지 "디졸브, 분리"쿼리를 실행하려고 시도했다.

SELECT (ST_Dump(st_union)).geom
FROM 
    (SELECT ST_Union(geom) FROM roads) sq

내가 시도한 모든 것을 사용하여 단일 피쳐 ( ST_Union) 또는 원래 형상 ( ST_Dumpof ST_Union)으로 끝납니다 .

어쩌면 어떤 종류의 WITH RECURSIVE마술로 이것을 할 수 있습니까?


"(ST_Dump (st_union)). geom"으로 잘못 표시되는 부분
Martin F

ST_Union (geom)의 별명을 지정하지 않았으므로 새 Geom의 이름은 st_union이되도록 함수 이름을 상속했습니다. 즉, 그것은 재미 조금 보이는 이유
LR1234567

답변:


19

예를 들어. 다음은 두 개의 연결된 모서리 그룹이있는 간단한 테이블입니다.

drop table lines;
create table lines ( id integer primary key, geom geometry(linestring) );
insert into lines (id, geom) values ( 1, 'LINESTRING(0 0, 0 1)');
insert into lines (id, geom) values ( 2, 'LINESTRING(0 1, 1 1)');
insert into lines (id, geom) values ( 3, 'LINESTRING(1 1, 1 2)');
insert into lines (id, geom) values ( 4, 'LINESTRING(1 2, 2 2)');
insert into lines (id, geom) values ( 11, 'LINESTRING(10 10, 10 11)');
insert into lines (id, geom) values ( 12, 'LINESTRING(10 11, 11 11)');
insert into lines (id, geom) values ( 13, 'LINESTRING(11 11, 11 12)');
insert into lines (id, geom) values ( 14, 'LINESTRING(11 12, 12 12)');
create index lines_gix on lines using gist(geom);

자, 여기에 모서리의 id가 주어지면 만지는 모든 모서리가 누적되는 재귀 함수가 있습니다.

CREATE OR REPLACE FUNCTION find_connected(integer) returns integer[] AS
$$
WITH RECURSIVE lines_r AS (
  SELECT ARRAY[id] AS idlist, geom, id
  FROM lines 
  WHERE id = $1
  UNION ALL
  SELECT array_append(lines_r.idlist, lines.id) AS idlist, 
         lines.geom AS geom, 
         lines.id AS id
  FROM lines, lines_r
  WHERE ST_Touches(lines.geom, lines_r.geom)
  AND NOT lines_r.idlist @> ARRAY[lines.id]
)
SELECT 
  array_agg(id) AS idlist
  FROM lines_r
$$ 
LANGUAGE 'sql';

따라서 각 그룹이 누적 된 후 아직 그룹의 일부가 아닌 가장자리의 ID를 찾아야합니다. 비극적으로 두 번째 재귀 쿼리가 필요합니다.

WITH RECURSIVE groups_r AS (
  (SELECT find_connected(id) AS idlist, 
          find_connected(id) AS grouplist, 
          id FROM lines WHERE id = 1)
  UNION ALL
  (SELECT array_cat(groups_r.idlist,find_connected(lines.id)) AS idlist,
         find_connected(lines.id) AS grouplist,
         lines.id
  FROM lines, groups_r
  WHERE NOT idlist @> ARRAY[lines.id]
  LIMIT 1)
)
SELECT id, grouplist
FROM groups_r;   

함께 모은 씨앗 ID와 누적 된 각 그룹으로 멋진 세트를 반환합니다. ID 배열을 쿼리로 다시 변환하여 매핑을위한 지오메트리를 만드는 연습으로 남겨 두었습니다.

 id |   grouplist   
----+---------------
  1 | {1,2,3,4}
 11 | {11,12,13,14}
(2 rows)

PostgreSQL에서 해싱을 지원하는 기하학 유형 (ID 배열을 포함하지 않는 더 간단한 RCTE를 작성할 때 "모든 열 데이터 유형은 해시 가능해야합니다") 오류가 발생하면이 코드가 더 간단 할 수 있다고 생각합니다. 나에게 약간의 향상 요청.
Paul Ramsey

이것은 정말 멋진 접근법입니다. 더 큰 테스트 세트에 적용 할 때 몇 가지 이상한 결과가 나타납니다. 문제를 간단한 예로 줄일 수 있는지 살펴 보겠습니다. 100 라인 : 85 클러스터, 최대 클러스터 = 3, 0.03 초 //// 200 라인 : 144 클러스터, 최대 클러스터 = 9, 0.08 초 //// 300 라인 : 180 클러스터, 최대 클러스터 = 51, 0.16 초 /// / 400 라인 : 188 클러스터, 최대 클러스터 = 41, 0.27 초 //// 500 라인 : 176 클러스터, 최대 클러스터 = 112, 0.56 초 //// 600 라인 : 143 클러스터, 최대 클러스터 = 449, 1.0 초 // // 650 줄 : 133 개 클러스터, 가장 큰 클러스터 = 7601, 6.8 초
dbaston

이것을 테스트 데이터에 추가하면 grouplist배열 에서 ID가 중복됩니다 insert into lines (id, geom) values ( 15, 'LINESTRING(0 0, 10 10)');. array_agg(id)기능 반환으로 변경 array_agg(DISTINCT id)하면 문제가 해결되는 것 같습니다.
dbaston

이것은 좋은 해결책이므로 이제 연결된 선을 볼 수 있도록 도형을 테이블에 어떻게 저장할 수 있습니까?
zakaria mouqcit

6

다음은 임시 테이블을 사용하여 클러스터를 증분 집계하는 방법입니다. 나는 실제로 임시 테이블 접근 방식을 신경 쓰지 않지만 줄 수가 증가함에 따라 상당히 잘 작동하는 것 같습니다 (내 입력에 1.2 M 줄이 있습니다).

DO
$$
DECLARE
this_id bigint;
this_geom geometry;
cluster_id_match integer;

id_a bigint;
id_b bigint;

BEGIN
DROP TABLE IF EXISTS clusters;
CREATE TABLE clusters (cluster_id serial, ids bigint[], geom geometry);
CREATE INDEX ON clusters USING GIST(geom);

-- Iterate through linestrings, assigning each to a cluster (if there is an intersection)
-- or creating a new cluster (if there is not)
FOR this_id, this_geom IN SELECT id, geom FROM lines LOOP
  -- Look for an intersecting cluster.  (There may be more than one.)
  SELECT cluster_id FROM clusters WHERE ST_Intersects(this_geom, clusters.geom)
     LIMIT 1 INTO cluster_id_match;

  IF cluster_id_match IS NULL THEN
     -- Create a new cluster
     INSERT INTO clusters (ids, geom) VALUES (ARRAY[this_id], this_geom);
  ELSE
     -- Append line to existing cluster
     UPDATE clusters SET geom = ST_Union(this_geom, geom),
                          ids = array_prepend(this_id, ids)
      WHERE clusters.cluster_id = cluster_id_match;
  END IF;
END LOOP;

-- Iterate through the clusters, combining clusters that intersect each other
LOOP
    SELECT a.cluster_id, b.cluster_id FROM clusters a, clusters b 
     WHERE ST_Intersects(a.geom, b.geom)
       AND a.cluster_id < b.cluster_id
      INTO id_a, id_b;

    EXIT WHEN id_a IS NULL;
    -- Merge cluster A into cluster B
    UPDATE clusters a SET geom = ST_Union(a.geom, b.geom), ids = array_cat(a.ids, b.ids)
      FROM clusters b
     WHERE a.cluster_id = id_a AND b.cluster_id = id_b;

    -- Remove cluster B
    DELETE FROM clusters WHERE cluster_id = id_b;
END LOOP;
END;
$$ language plpgsql;

완벽하게 작동
zakaria mouqcit

@zakariamouqcit 다행이 당신을 위해 일했다! ST_ClusterIntersectingPostGIS 에서 함수를 작성하기 전에이 답변을 작성했습니다 . 데이터가 메모리에 들어갈만큼 작 으면 성능이 더 우수한 솔루션을 확인하는 것이 좋습니다.
dbaston

이 질문을 검색하면 여기로 데려 왔습니다. 반복 및 st_clusterintersecting을 시도했지만 st_clusterDBScan이 가장 적합한 것으로 나타났습니다. 다른 사람도 여기로 데려 오는 경우 postgis.net/docs/manual-dev/ST_ClusterDBSCAN.html
D_C

합의, ST_ClusterDBSCAN는 2.3 PostGIS와 갈 수있는 가장 좋은 방법은 거의 항상
dbaston
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.