본문 바로가기

RDMS/PostgreSQL

[PostgreSQL] PostGIS 설치 및 사용 - 컴도리돌이

728x90
728x90

주어진 좌표가 어떤 행정구역에 포함되는지 알아야 하는 상황이 생겼습니다. 여러 방법이 있지만 그중에서 PostgreSQL에 있는 확장 라이브러리인 PostGIS가 속도 및 정확성에 성능이 매우 뛰어난다고 소문이 났기에 저도 한번 PostGIS를 사용하면서 어떤지 맛 좀 보려고 해당 포스팅을 작성하려고 합니다.

 

PostgreSQL에서 GIS 데이터를 다루기 위해 PostGIS 확장이 필요해요. 먼저 PostGIS를 설치하고 활성화한 후, 다음과 같은 테이블을 설계할 수 있어요.

-- PostGIS 확장 활성화
CREATE EXTENSION IF NOT EXISTS postgis;

-- 경계선 데이터를 저장할 테이블
CREATE TABLE boundaries (
    id SERIAL PRIMARY KEY,
    region_key VARCHAR(20) NOT NULL,
    boundary GEOMETRY(MULTILINESTRING, 4326) NOT NULL
);

-- 특정 지역에 대한 UNIQUE 키 제약 추가
CREATE UNIQUE INDEX idx_region_key ON boundaries (region_key);

 

여기서 boundary 칼럼은 WGS 84 좌표계(4326)를 사용하는 MULTILINESTRING 형식의 지리 데이터를 저장합니다. 이 데이터는 특정 지역의 경계 정보를 나타냅니다.

 

만약 다음과 같은 에러가 발생한다면, PostgreSQL에서 PostGIS 확장을 활성화하려고 할 때 발생하는 것으로, 이는 시스템에 PostGIS가 설치되어 있지 않다는 것을 의미합니다. 

[2024-12-31 14:07:06] [0A000] ERROR: extension "postgis" is not available
[2024-12-31 14:07:06] Detail: Could not open extension control file "/usr/share/postgresql/17/extension/postgis.control": No such file or directory.
[2024-12-31 14:07:06] Hint: The extension must first be installed on the system where PostgreSQL is running.

 

저는 PostgreSQL이 Docker 컨테이너에서 실행 중이기 때문에, PostgreSQL 컨테이너에 PostGIS를 설치하였습니다.

 

docker exec -it your_container_name bash
apt update
apt install postgis postgresql-17-postgis-3
exit

이제 PostGIS를 적용한 테이블을 기준으로 특정 좌표가 경곗값에 포함되는지 확인해 볼게요. 🤓

먼저, 행정구역 코드 값과 경계 값을 담을 region_key, boundary로 구성된 테이블을 생성해 봅니다. 테이블 생성 쿼리는 아래와 같습니다.

CREATE TABLE boundaries (
    id SERIAL PRIMARY KEY,
    region_key VARCHAR(20) NOT NULL,
    boundary GEOMETRY(MULTILINESTRING, 4326) NOT NULL
);

 

여기서 id는 고유 식별자를 나타내며, region_key는 특정 지역을 구분하는 키 값입니다. boundary는 해당 지역의 지리적 경계를 저장하는 칼럼으로, GEOMETRY 데이터 타입과 WGS 84 좌표계(4326)를 사용합니다. 이 테이블을 활용하면 특정 좌표가 경계 값에 포함되는지 확인할 수 있습니다.

 

이제 경계 데이터를 테이블에 삽입해 보겠습니다. 아래는 기흥구를 나타내는 예제 데이터입니다.

INSERT INTO
    public.boundaries (
                      id , region_key , boundary
                      )
VALUES
    (
    1 , '41463000' , '0105000020E6100000010 ...' -- 경계 데이터 생략
    );

 

위의 쿼리는 boundaries 테이블에 id 1, region_key '41463000', 그리고 지리적 경계 값을 삽입합니다. 이제 이 데이터를 기반으로 특정 좌표가 경계 값에 포함되는지 확인할 수 있습니다. '41463000'는 기흥구를 의미하는 행정 구역 코드입니다. 아래는 경계 좌표를 네이버 지도 API를 통해서 경계를 기흥구에 대한 경계를 구분해서 표시한 이미지입니다. 🧑‍💻

 

그리고 위에서 (127.1467242,37.282881), (127.116694237,37.32652053) 두 좌표에 대한 마크표시를 하였습니다. 경계 값 밖에 있는 좌표 값은 수지구에 있는 한 장소이고, 경계 값 안에 있는 값은 기흥구 동백동에 위치한 좌표입니다. 해당 좌표를 ST_Contains 함수를 사용하여 경계와 좌표를 비교해 볼게요. 아래 쿼리를 사용하였습니다. 

SELECT
    st_contains( st_convexhull( boundary ),st_setsrid( st_point( 127.1467242,37.282881 ),4326 ) )     AS "기흥구 위치 좌표",
    st_contains( st_convexhull( boundary ),st_setsrid( st_point( 127.116694237,37.32652053 ),4326 ) ) AS "수지구 위치 좌표",
    region_key
FROM
    boundaries b;

 

이 쿼리는 두 개의 좌표 (기흥구와 수지구)를 ST_contains 함수로 검증합니다. ST_Contains는 boundary 칼럼에 저장된 지리적 경계와 좌표를 비교하여 해당 좌표가 경계 안에 포함되는지 여부를 반환합니다. st_convexhull은 boundary의 외곽을 계산하며, st_setsrid는 좌표에 SRID(좌표계)를 설정해 줍니다. 

 

  • st_convexhull: 경계의 외곽을 계산.
  • st_setsrid: 좌표에 SRID(좌표계)를 설정.

 

특정 좌표가 경계 값에 포함되는지 여부를 아래와 같이 확인할 수 있습니다. 

 

728x90
728x90