GIS

PostGIS: pgRouting 기능 실습하기

유병혁 2023. 5. 16. 22:11

안녕하세요? 이번 글은 pgRouting 기능을 실습해 보겠습니다. pgRouting은 PostgreSQL/PostGIS 공간 DBMS의 확장 프로그램으로, 네트워크 내에서 목적지까지 갈 수 있는 여러 경로 중 한 가지 경로를 설정해주는 과정, 경로 지정, 즉 라우팅(Routing) 기능을 제공합니다.

 

pgRouting Project — Open Source Routing Library

pgRouting Project pgRouting extends the PostGIS / PostgreSQL geospatial database to provide geospatial routing functionality. Advantages of the database routing approach are: Data and attributes can be modified by many clients, like QGIS through JDBC, ODBC

pgrouting.org

pgRouting 실습을 위해서 등산로(1:25000) 테이블인 trail_kr을 다뤄보겠습니다. 앞서 저는 gongju라는 이름의 공간 데이터베이스를 생성하고 PostGIS Shapefile Import/Export Manager를 통해 등산로 데이터들을 gongju db에 테이블로 추가했습니다. pgAdmin 4에서 Query Tool을 실행하고 gongju 공간 데이터베이스에서 다음 명령문으로 pgRouting을 설치 및 활성화합니다.

CREATE EXTENSION pgrouting;

pgRouting의 버전은 3.4.2임을 확인합니다.

SELECT * FROM pgr_version();

자, 일단 "trail_kr" 테이블에서 첫 번째 레코드를 반환해 보겠습니다. 여기서 *은 모든 열을 선택하는 역할을, LIMIT 1은 가져올 레코드의 수를 1개로 제한하는 역할을 합니다.

SELECT * FROM trail_kr LIMIT 1;

"trail_kr" 테이블에 pgr_createTopology 함수를 적용하면, 위치정보를 기반으로 네트워크 토폴로지(network topology)를 구축할 수 있습니다. 여기서 네트워크 토폴로지는 링크(또는 엣지)와 노드로 구성된 네트워크의 구조와 연결관계를 정의하는 것을 의미합니다.

 

"trail_kr" 테이블을 링크 테이블로 사용하려면 id, source, target, length와 같은 컬럼 값이 필요합니다. 여기서 id는 각 링크에 대한 고유 식별자 값, source는 링크의 시점 노드에 대한 식별자 값, target은 링크의 종점 노드에 대한 식별자 값, length는 링크의 길이(일반적으로 거리나 시간 등의 단위)를 나타냅니다.

 

다음과 같이 "trail_kr"에 source, target, length 컬럼을 추가합니다.

ALTER TABLE trail_kr ADD COLUMN source INTEGER;
ALTER TABLE trail_kr ADD COLUMN target INTEGER;
ALTER TABLE trail_kr ADD COLUMN length FLOAT;

다음 명령문을 실행하면 "trail_kr" 테이블을 기반으로 네트워크 토폴로지가 생성되어 네트워크의 노드와 엣지를 정의합니다. 여기서 "trail_kr" 은 토폴로지를 생성할 대상 테이블의 이름을, 0.0001은 토폴로지 생성 과정에서 노드를 병합할 때 사용되는 거리 허용 값을, geom은 토폴로지 생성에 사용될 지오메트리 컬럼의 이름을, gid는 각 링크의 고유 식별자 값을 의미합니다. 자, 이제 네트워크 토폴로지가 생성됩니다!

SELECT pgr_createTopology('trail_kr', 0.0001, 'geom', 'gid');

생성된 "trail_kr_vertices_pgr" 테이블은 토폴로지의 노드 정보를 포함합니다.

"trail_kr" 테이블에서 gid, source, target 컬럼 값을 1줄로 확인해 보겠습니다.

SELECT gid, source, target FROM trail_kr LIMIT 1;

이번에는 "trail_kr" 테이블의 length 컬럼에 엣지 길이를 입력해 보겠습니다.

UPDATE trail_kr SET length = ST_Length(geom);

이제 "trail_kr" 테이블에서 gid, source, target, length 컬럼 값을 확인합니다.

SELECT gid, source, target, length FROM trail_kr LIMIT 1;

"trail_kr" 테이블에서 가장 많은 등산로를 가지고 있는 산의 이름은 무엇일까요?! 확인해 보겠습니다.

SELECT mntn_nm
FROM trail_kr
GROUP BY mntn_nm
ORDER BY COUNT(*) DESC
LIMIT 1;

보문산에서 임의의 노드 2개를 출발지와 도착지로 설정하고 pgr_dijkstra 함수를 사용하여 최단 경로를 계산해 보겠습니다. directed := false는 방향성이 없는 네트워크를 가정합니다.

  • 출발지 ID: 28815
  • 도착지 ID: 29170
SELECT * FROM pgr_dijkstra(
    'SELECT gid as id, source, target, length AS cost FROM trail_kr',
	28815, 29170,
	directed := false
);

위의 쿼리는 최단 경로를 구성하는 링크의 모든 정보를 반환하는데요, 이 정보와 원래의 링크 테이블을 결합하면 지오메트리 정보도 함께 결과로 반환할 수 있습니다. 최단 경로의 링크 식별자(edge)와 "trail_kr"의 고유 식별자(gid)를 기준으로 합니다.

SELECT seq, edge, geom FROM pgr_dijkstra(
    'SELECT gid as id, source, target, length AS cost FROM trail_kr',
	28815, 29170,
	directed := false
) AS pgr
JOIN trail_kr ON pgr.edge = trail_kr.gid;

이번에는 각각의 경로를 하나로 결합하여 표현해 보겠습니다. 결과는 다음과 같습니다.

SELECT ST_Transform(ST_Union(geom), 4326) as route
FROM pgr_dijkstra(
    'SELECT gid as id, source, target, length AS cost FROM trail_kr',
    28815, 29170,
    directed := false
) AS pgr
JOIN trail_kr ON pgr.edge = trail_kr.gid;

이번에는 임의의 좌표 2개를 출발지와 도착지로 설정한 후 pgr_dijkstra 함수를 사용하여 최단 경로를 계산해 보겠습니다.

  • 출발지 좌표: 58250.667802,412084.461966 [EPSG:5187]
  • 도착지 좌표: 56027.587742,411363.233703 [EPSG:5187]
WITH start_node AS (
    SELECT id
    FROM trail_kr_vertices_pgr
    ORDER BY ST_Distance(the_geom, ST_SetSRID(ST_MakePoint(58250.667802, 412084.461966), 5187)) LIMIT 1
), end_node AS (
    SELECT id
    FROM trail_kr_vertices_pgr
    ORDER BY ST_Distance(the_geom, ST_SetSRID(ST_MakePoint(56027.587742, 411363.233703), 5187)) LIMIT 1
), pgr AS (
    SELECT *
    FROM pgr_dijkstra(
        'SELECT gid AS id, source, target, length AS cost FROM trail_kr',
        (SELECT id FROM start_node),
        (SELECT id FROM end_node),
        directed := false
    )
)
SELECT ST_Transform(ST_Union(geom), 4326) as route
FROM pgr
JOIN trail_kr ON pgr.edge = trail_kr.gid;

결과는 다음과 같습니다.