GDAL 을 사용한 GIS 데이터 포맷 변환/조회법!

식빵·2023년 11월 15일
1

GIS

목록 보기
2/7

이 글에서 사용되는 GDAL 의 버전은 3.3.0 입니다!


GDAL ?

GIS 에서 사용되는 postgis , shp , gdb , xlsx 등의 다양한 포맷간에
변환(transform) 작업을 도와주는 라이브러리입니다.

부가적으로 변환을 할 때 사용자들이 데이터의 메타정보를 조회하는 기능도 제공합니다.
예를 들어 데이터의 좌표계가 뭔지, 피쳐의 속성에는 뭐가 있는지 등을 알 수 있습니다.




설치법

방법1: gdal binary 로 설치하기

만약에 Window OS 를 사용중이시면,
제가 작성한 Window 에 gdal 설치하기 게시물을 참조하세요.
참고로 이 방법은 본인이 gdal java (gdal.jar) 사용할 시에 더 좋을 수 있습니다.


방법2: QGIS - OSGeo4W Shell 사용

만약 본인 PC 에 QGIS 가 이미 깔려있다면 이 방법도 좋습니다.
QGIS 프로그램을 깔때 OSGeo4W Shell 이 같이 다운로드되는데,
OSGeo4W Shell 을 실행하면 CLI 화면이 나오고,
해당 화면에서 gdal 명령어를 곧바로 사용할 수 있습니다.
골치 아프게 이것저것 하는 게 귀찮으면 이게 제일 좋습니다.


방법3: 환경변수에 GDAL 라이브러리 경로 추가하기

이 방법은 방법2: QGIS - OSGeo4W Shell 사용 의 응용입니다.

혹여 GDAL 라이브러리(ogr2ogr, ogrinfo 등)를 OSGeo4W Shell
통해서가 아니라 일반적인 pwsh 같은 다른 CLI 툴을 통해서 사용하고
싶다면 GDAL 라이브러리의 경로를 PATH 환경변수에 직접 세팅하셔도 됩니다.

라이브러리 경로는 주로 C:\Program Files\QGIS x.xx.xx\bin 에 위치하니
잘 찾아보시기 바랍니다.

다만 여기서 좀 주의할 점이 있습니다.
gdal 라이브러리는 단순히 QGIS 에만 있는 게 아니라,
다른 여러 프로그램에서도 사용됩니다. gdal 핵심 라이브러리인 ogr2ogr
전역으로 검색하면 저 같은 경우에는 4개나 나옵니다.

그렇기 때문에 환경변수 설정 시에 QGIS 에 있는 ogr2ogr 프로그램이 있는 경로가
Path 환경변수 우선순위를 더 높게 잡아주셔야 합니다.

이게 좀 부담스러우시면, 잠시 일회용으로 Path 경로를 추가하는 방식도 좋습니다.
pwsh 이면 아래처럼 할 수 있겠죠?

$env:path="C:\Program Files\QGIS x.xx.xx\bin;" + $env:path; 

# 이후 ogr2ogr 이 실제 QGIS 에 있는 걸 사용하는지 확인
# 입력: (get-command ogr2ogr).path
# 출력: C:\Program Files\QGIS 3.22.14\bin\ogr2ogr.exe

window 운영체제인 분들은 window powershell 키고 아래 명령어를 복붙해서
현재 컴퓨터에 얼마나 많은 ogr2ogr.exe 경로(= gdal 라이브러리 경로)가
있는지 확인할 수 있습니다.

where.exe ogr2ogr

#출력 예시:
# C:\Program Files\MySQL\MySQL Workbench 8.0 CE\ogr2ogr.exe
# C:\Program Files\PostgreSQL\13\bin\ogr2ogr.exe
# C:\Program Files\PostgreSQL\13\pgAdmin 4\runtime\ogr2ogr.exe
# C:\Program Files\QGIS 3.22.14\apps\gdal\share\bash-completion\completions\ogr2ogr
# C:\Program Files\QGIS 3.22.14\bin\ogr2ogr.exe




GDAL 의 다양한 프로그램

gdal 라이브러리에는 다양한 프로그램(=명령어)들이 존재합니다.
https://gdal.org/programs/index.html 에서 보면 어느정도 체감이 될겁니다.

그럼 이 모든 프로그램을 다 다루냐? 아닙니다!

이 글에서는 핵심인 데이터 변환 명령어인 ogr2ogr
그외에도 데이터의 메타정보를 조회하는 ogrinfo, gdalsrsinfo 를 알아볼 예정입니다.




ogr2ogr

벡터 데이터 변환의 핵심인 ogr2ogr 부터 알아보겠습니다.
ogr2ogr 로 다음과 같은 테스트 작업을 해봤습니다.

  • CSV 🡲 PostgreSQL 변환
  • SHP 🡲 PostgreSQL 변환
  • PostgreSQL 🡲 PostgreSQL 변환(= 데이터 이동)

해당 테스트에 사용된 명령어 작성 방식을 여러분들과 공유합니다.
그리고 여러분들은 본인이 갖고 있는 파일들로 해당 명령어들을
비슷하게 작성해보면서 스스로 익혀보시면 좋을 거 같습니다.

참고(1)
ogr2ogr 에는 정말 많은 옵션들이 있습니다.
그렇기 때문에 이 글에서 그 모든걸 Cover 하기에는 무리가 있었습니다.
다른 Option 들도 궁금하다면 https://gdal.org/programs/ogr2ogr.html#ogr2ogr
을 참고하시기 바랍니다.

참고(2)
ogr2ogr --formats 을 통해서 사용 가능한 포맷을 조회할 수 있습니다.

주의!
저는 이 프로그램을 Window OS + pwsh 환경에서 테스트했습니다.
그래서 줄바꿈을 할 때는 백틱( ` )을 사용했습니다.
혹여 bash 를 사용하시는 분들이라면 백틱 대신에 \ 를 사용하시기 바랍니다.



0. 모든 변환의 공통점

ogr2ogr 을 통한 변환을 할 때 공통적인 패턴이 있습니다.

예를 들어서 A 🡲 B 변환 작업을 하려고 하면,
ogr2ogr 의 명령어에서는 해당 변환 포맷들이 ogr2ogr B A 처럼
순서를 반대로 작성해야 된다는 겁니다!
처음에 모르면 헷갈리니 미리 말씀드립니다!



1. CSV 🡲 PostgreSQL

# nln 을 쓰면 overwrite 기능과 함께 쓰면 기존 데이터를 싹다 없애고, 다시 새로운 데이터를 넣을 수 있다.
# 만약 nln 은 있고, overwrite 가 없다면 데이터가 새로 appending 된다.

ogr2ogr -f PostgreSQL -overwrite `
-nln schema_name.table_name `
"PG:dbname='postgres' host='localhost' port='5432' user='postgres' password='postgres'" `
"D:\test_1\ogr_csv_test.csv"

# [nln + overwrite] 대신에 [SCHEMA=<스키마명> + OVERWRITE=YES] 을 사용해서 기능을 대체할 수도 있다.
# 하지만 nln 처럼 데이터 appending 은 되지 않는다. SCHEMA 파라미터는 기본적으로 해당 스키마에 테이블이 기존에 "없다는" 전제를 깔고 동작하기 때문이다.
ogr2ogr -f PostgreSQL -lco SCHEMA=schema_name -lco OVERWRITE=YES `
"PG:dbname='postgres' host='127.0.0.1' port='5432' user='postgres' password='postgres'" `
"D:\test_1\ogr_csv_test.csv"
  • 스키마 정보를 주는 nln 이나, SCHEMA 파라미터를 쓰지 않는다면 기본으로 public 스키마(=default 스키마) 로 import 됩니다.
  • csv 파일 경로에 한글이 있으면 안됩니다!

참고: 생성된 테이블에서 ogc_id 대신 나의 컬럼을 PK 로 만들기

위에서 알려준 대로 하면 생성된 테이블에는 ogc_id 라는 컬럼이 생기고, 해당 컬럼이
자동으로 PK 가 됩니다. 하지만 저희가 원하는 PK 컬럼이 따로 있다면 어떻게 할까요?
먼저 -lco FID=id 옵션을 줍니다. 이러면 id 라는 명칭의 컬럼이 pk 가 되죠.

그런데 이것만 하면 에러가 납니다. 이유는 id 로 사용되어야 하는 타입이
무조건 integer 타입이어야 하기 때문입니다. 이때는 -sql 이라는 특별한
세팅값을 줘서 casting 작업을 하고 테이블을 생성하면 됩니다.


ogr2ogr -f PostgreSQL -overwrite -nln schema.ogr_csv_test `
"PG:dbname='postgres' host='localhost' port='5432' user='postgres' password='postgres'" `
"D:\mappick_dms_batch_file\test_1\ogr_csv_test.csv" `
-sql "SELECT cast(id as bigint) as id, name from ogr_csv_test" `
-lco "FID=id"
  • 테스트를 해보니 id 로 사용할 수 있는 타입은... 아래 4가지 정도더군요.
    • smallint
    • integer
    • bigint
    • character
  • character 타입은... 되기는 하는데 에러가 보입니다(?).
    • 일단 되기는 합니다.
    • 그런데 콘솔에는 ERROR 1: Wrong field type for id 라는 에러가 보입니다!
    • 버그인지, 뭔지 잘 모르겠네요.
  • 테이블을 생성함과 동시에 pk 와 매칭되는 시퀀스를 자동으로 생성되는데,
    아무래도 자동으로 시퀀스를 생성해서 id 를 관리하기 때문에 정수형만 받아주는 거 같습니다.

참고: https://gis.stackexchange.com/questions/458376/ogr2ogr-csv-to-postgis-cast-columns-as-text

참고: cast 할 수 있는 타입
https://www.sqlite.org/lang_expr.html#castexpr


그런데 사실 위의 작업이 다 귀찮고 애매할 수 있습니다.
예를 들어서 여러 컬럼을 pk 로 사용해야 된다던가 하는 것 처럼요.

이럴 때는 조금 손이 가지만 아래와 같이 하면 딱 좋습니다.

  1. 데이터가 들어갈 테이블을 미리 만든다.
  2. 해당 테이블에 ogr2ogr 로 데이터를 밀어 넣는다.
    -sql, -lco "FID=??" 필요 X. 그냥 테이블 만들어진 대로 데이터 들어감.
  3. psql -c 를 통해서 pk 또는 index 등을 직접 생성한다.

이러면 ogc_id 를 생성하지 않고, 또한 geometry column 을 제외한 gist 인덱스도
만들지 않습니다. 이 상태에서 psql 을 통해서 인덱스 생성 쿼리를 날려주면 끝입니다.
저도 ogr2ogr 로 해결법을 찾으려다가 너무 힘들어서 결국 이렇게 하게 되더군요.



2. 중요한 nlt 옵션

이후 목차에서 SHP 🡲 PostgreSQL, PostgreSQL -> PostgreSQL
방식의 import 를 하는데, 이때 굉장히 중요한 옵션 1가지가 있어서 미리 말씀드립니다!
nlt 는 import 할 때의 geometry 의 타입을 지정하는 옵션입니다.

어떤 경우에는 자동으로 geometry 를 자동으로 잡아주지만,
어떤 경우에는 그렇지 않을 때도 있습니다.
그래서 명시적으로 표기해줘야 하는 경우가 종종 생깁니다.

nlt 를 통해서 지정 가능한 Geometry 타입은 아래와 같습니다.

  • NONE
  • GEOMETRY
  • POINT
  • LINESTRING
  • POLYGON
  • GEOMETRYCOLLECTION
  • MULTIPOINT
  • MULTIPOLYGON
  • MULTILINESTRING
  • CIRCULARSTRING
  • COMPOUNDCURVE
  • CURVEPOLYGON
  • MULTICURVE
  • GDAL 3.0.5 부터는 이래 2가지 선택지가 추가되었습니다.
    - CONVERT_TO_LINEAR
    - PROMOTE_TO_MULTI

참고 : -nlt 인자값은 대소문자를 가리지 않으니 소문자로 작성해도 됩니다.



3. SHP 🡲 PostgreSQL

참고: Postgresql 기능 사용을 위해서는 Postgresql 이 깔려있고,
설치 경로에 있는 psql 이 Path 환경변수에 노출된 상태여야 합니다.
또한 Path 환경변수의 여러 경로 중 GDAL 관련 경로보다 무조건
아래에 psql 설치경로가 있어야 합니다.

알아두면 좋은 옵션: -lco precision=YES/NO
이 옵션은 하지 않으면 기본 YES 로 설정되며, 이 YES/NO 의 차이를 알기 위해서는
먼저 해당 SHP 파일의 정보를 ogrinfo 로 조회해봐야 합니다. (아래참고)

ogrinfo -sql "select * from shape_file_nm limit 1" .\shape_file_nm.shp -geom=summary -q


## 출력 예시
Layer name: AL_11_D010
OGRFeature(AL_11_D010):0
  A0 (Integer) = 24922
  A11 (String) = 철근콘크리트구조
  A12 (Real) = 0.00000000000
  A22 (Date) = 2022/09/21
  POLYGON : 9 points
#

출력을 보면 데이터 타입이 Real 인 경우가 있습니다.
생소한 분들도 있겠지만 쉽게 생각해서 Double 타입이라고 생각하면 됩니다.
그런데 이런 타입들을 ogr2ogr 을 사용해서 postgresql 로 import 를 하면
기본으로 -lco precision=YES 가 적용되서 Numeric(10,9) 와 같이 Table 타입이
생성됩니다. 반면에 -lco precision=NO 를 하면 그냥 Double precision 타입으로
생성됩니다. 즉! 상세한 소수점 자리는 무시하게 되는 겁니다!
그외에도 VARCHAR 같은 경우에는 gdal 이 똑똑하게 VARCHAR(?) 처럼 길이를
지정해주는데, 이게 싫다면 -lco precision=NO 을 사용하면 그냥 VARCHAR 로
테이블의 컬럼이 생성됩니다.


example 1

ogr2ogr -overwrite -progress --config PG_USE_COPY YES -f PostgreSQL `
"PG:host=localhost port=5432 user=postgres password=postgres dbname=postgres schemas=public" `
D:\shape_files\LSMD_CONT_LDREG_48_202311.shp `
-lco GEOMETRY_NAME=geom -nlt MULTIPOLYGON `
--config SHAPE_ENCODING "EUC-KR" `
-nln LSMD_CONT_LDREG_48



example 2

ogr2ogr -overwrite --config PG_USE_COPY YES -progress -f PostgreSQL `
"PG:host=localhost port=5432 user=postgres password=postgres dbname=postgres schemas=public" `
D:\shape_files\LSMD_CONT_LDREG_11.shp `
-lco GEOMETRY_NAME=geom `
-nlt POINT `
-nln lsmd_cont_ldreg_seoul_table
  • lco GEOMETRY_NAME=geom 처럼 지오메트리 컬럼 명칭을 정할 수도 있습니다.



example 3: 여러 SHP import 하기

참고: https://gis.stackexchange.com/questions/136553/batch-load-multiple-shapefiles-to-postgis

linux 환경
for shp in $(ls *.shp);
do
  ogr2ogr -f "PostgreSQL" PG:dbname=databasename -append -a_srs 27700 -nln schema.table_name $shp
done

쉘 스크립트를 짠다면 아래처럼 할 수 있겠죠?

#!/bin/sh -e

# 쉘 스크립트 명 test.sh

# 사용법 1: shp 파일과 매칭되는 cpg 파일이 모두 있을 때
# ./test.sh "<shp 파일이 있는 디렉토리>" "<POSTGRES_IP_HOST>" "<POSTGRES_PORT>" "<DB_명칭>" "<SCHEMA_명칭>" "<user_id>" "<user_pw>"  "<생성할 테이블 명>" "<geometry 타입>"

# 사용법 2: shp 파일과 매칭되는 cpg 파일이 몇개 없을 때
# ./test.sh "<shp 파일이 있는 디렉토리>" "<POSTGRES_IP_HOST>" "<POSTGRES_PORT>" "<DB_명칭>" "<SCHEMA_명칭>" "<user_id>" "<user_pw>"  "<생성할 테이블 명>" "<geometry 타입>" "<SHAPE_FILE 인코딩>"

SOURCE_SHAPE_DIRECTORY=$1
POSTGRES_HOST_IP=$2
POSTGRES_PORT_NUM=$3
POSTGRES_DB=$4
POSTGRES_SCHEMA=$5
POSTGRES_USR_ID=$6 
POSTGRES_USR_PW=$7
POSTGRES_TABLE=$8
POSTGRES_GEOM_TYPE=$9
SHAPEFILE_ENCODING=${10}

echo "$SOURCE_SHAPE_DIRECTORY"
echo "$POSTGRES_HOST_IP"
echo "$POSTGRES_PORT_NUM"
echo "$POSTGRES_DB"
echo "$POSTGRES_SCHEMA"
echo "$POSTGRES_USR_ID" 
echo "$POSTGRES_USR_PW"
echo "$POSTGRES_TABLE"
echo "$POSTGRES_GEOM_TYPE"
echo "$SHAPEFILE_ENCODING"

IDX=0;
for file in "$SOURCE_SHAPE_DIRECTORY"/*.shp;
do
  if [ "$IDX" = "0" ]; then
  	echo "ogr2ogr -progress -f \"PostgreSQL\" \"PG:host=$POSTGRES_HOST_IP port=$POSTGRES_PORT_NUM dbname=$POSTGRES_DB schemas=$POSTGRES_SCHEMA user=$POSTGRES_USR_ID password=$POSTGRES_USR_PW\" \"$file\" -lco GEOMETRY_NAME=geom -nlt $POSTGRES_GEOM_TYPE -nln $POSTGRES_TABLE -lco precision=NO ${SHAPEFILE_ENCODING:+--config SHAPE_ENCODING $SHAPEFILE_ENCODING} -overwrite"
    ogr2ogr -progress -f "PostgreSQL" "PG:host=$POSTGRES_HOST_IP port=$POSTGRES_PORT_NUM dbname=$POSTGRES_DB schemas=$POSTGRES_SCHEMA user=$POSTGRES_USR_ID password=$POSTGRES_USR_PW" "$file" -lco GEOMETRY_NAME=geom -nlt $POSTGRES_GEOM_TYPE -nln $POSTGRES_TABLE -lco precision=NO ${SHAPEFILE_ENCODING:+--config SHAPE_ENCODING $SHAPEFILE_ENCODING} -overwrite
  else
  	echo "ogr2ogr -progress -f \"PostgreSQL\" \"PG:host=$POSTGRES_HOST_IP port=$POSTGRES_PORT_NUM dbname=$POSTGRES_DB schemas=$POSTGRES_SCHEMA user=$POSTGRES_USR_ID password=$POSTGRES_USR_PW\" \"$file\" -nlt $POSTGRES_GEOM_TYPE -nln $POSTGRES_TABLE ${SHAPEFILE_ENCODING:+--config SHAPE_ENCODING $SHAPEFILE_ENCODING} -append"
    ogr2ogr -progress -f "PostgreSQL" "PG:host=$POSTGRES_HOST_IP port=$POSTGRES_PORT_NUM dbname=$POSTGRES_DB schemas=$POSTGRES_SCHEMA user=$POSTGRES_USR_ID password=$POSTGRES_USR_PW" "$file" -nlt $POSTGRES_GEOM_TYPE -nln $POSTGRES_TABLE ${SHAPEFILE_ENCODING:+--config SHAPE_ENCODING $SHAPEFILE_ENCODING} -append
  fi
  IDX=$((IDX + 1));
done

그런데 자세히 보면 -overwrite-append 를 분기해서 작성했는데,
사실은 -append 하나만 사용해도 같은 결과물을 낼 수 있습니다.
심지어는 for 문도 필요없습니다!

ogr2ogr -progress -f "PostgreSQL" "PG:host=$POSTGRES_HOST_IP port=$POSTGRES_PORT_NUM dbname=$POSTGRES_DB schemas=$POSTGRES_SCHEMA user=$POSTGRES_USR_ID password=$POSTGRES_USR_PW" "$file" -lco GEOMETRY_NAME=geom -nlt $POSTGRES_GEOM_TYPE -nln $POSTGRES_TABLE -lco precision=NO ${SHAPEFILE_ENCODING:+--config SHAPE_ENCODING $SHAPEFILE_ENCODING} -append

대신에 이렇게하면 디렉토리 내에 있는 shp 파일을 한번 postgresql 로 import 하고,
2번째 shp 파일을 postgresql 에 import 할 때부터 WARNING 문구가 나옵니다 😅

WARNING 의 이유는 테이블이 이미 있는 상태에서 -append 옵션을 주면
lco(Layer Creation Options) 옵션이 무시되는데, 이것을 알려주기 위함입니다.


window 환경
for /R %f in (*.shp) do ogr2ogr -f "PostgreSQL" PG:dbname=databasename -append -nln schema.table_name "%f"

window batch 파일로 짠다면...?

window batch 파일은 처음 짜봐서 살짝 어설퍼 보일 수 있습니다 😂

@echo off

rem 사용법1 (인코딩 명시X, cpg 파일이 있는 경우) : .\multi_shp_to_post.bat "<shp 파일이 있는 디렉토리>" "<POSTGRES_IP_HOST>" "<POSTGRES_PORT>" "<DB_명칭>" "<SCHEMA_명칭>" "<user_id>" "<user_pw>"  "<생성할 테이블 명>" "<geometry 타입>"
rem 사용법2 (인코딩 명시O, cpg 파일이 없는 경우) : .\multi_shp_to_post.bat "<shp 파일이 있는 디렉토리>" "<POSTGRES_IP_HOST>" "<POSTGRES_PORT>" "<DB_명칭>" "<SCHEMA_명칭>" "<user_id>" "<user_pw>"  "<생성할 테이블 명>" "<geometry 타입>" "<인코딩, ex:EUC-KR>"

SET count=1
SET SOURCE_SHAPE_DIRECTORY=%1
SET POSTGRES_HOST_IP=%2
SET POSTGRES_PORT_NUM=%3
SET POSTGRES_DB=%4
SET POSTGRES_SCHEMA=%5
SET POSTGRES_USR_ID=%6
SET POSTGRES_USR_PW=%7
SET POSTGRES_TABLE=%8
SET POSTGRES_GEOM_TYPE=%9
shift
SET SHAPEFILE_ENCODING=%9

echo all arguments: %*
echo SOURCE_SHAPE_DIRECTORY="%SOURCE_SHAPE_DIRECTORY%"
echo POSTGRES_HOST_IP=%POSTGRES_HOST_IP%
echo POSTGRES_PORT_NUM=%POSTGRES_PORT_NUM%
echo POSTGRES_DB=%POSTGRES_DB%
echo POSTGRES_SCHEMA=%POSTGRES_SCHEMA%
echo POSTGRES_USR_ID=%POSTGRES_USR_ID%
echo POSTGRES_USR_PW=%POSTGRES_USR_PW%
echo POSTGRES_TABLE=%POSTGRES_TABLE%
echo POSTGRES_GEOM_TYPE=%POSTGRES_GEOM_TYPE%
echo SHAPEFILE_ENCODING=%SHAPEFILE_ENCODING%


@REM echo your input is ... [ %1 ]
echo:

if exist %SOURCE_SHAPE_DIRECTORY% (
    rem 참고. %~2 처럼 하면 외부의 "" 가 사라진다.
    for %%f in (%SOURCE_SHAPE_DIRECTORY%\*.shp) do call :subroutine %%f
    GOTO :EOF
) else (
    goto :NO_FOLDER_FOUND_ERROR
)

:subroutine
echo loop - %count% 
echo arguments: %*
SET SOURCE_SHAPE_FILE_PATH=%1
echo %SOURCE_SHAPE_FILE_PATH%
set SHAPE_ENCODING_CONFIG_STRING=

if not "%SHAPEFILE_ENCODING%" == "" (
    set SHAPE_ENCODING_CONFIG_STRING=--config SHAPE_ENCODING %SHAPEFILE_ENCODING%
)

if %count%==1 (
    echo ogr2ogr -progress -f "PostgreSQL" PG:"host=%POSTGRES_HOST_IP% port=%POSTGRES_PORT_NUM% user=%POSTGRES_USR_ID% password=%POSTGRES_USR_PW% dbname=%POSTGRES_DB% schemas=%POSTGRES_SCHEMA%" "%SOURCE_SHAPE_FILE_PATH%" -lco GEOMETRY_NAME=geom -nlt %POSTGRES_GEOM_TYPE% -nln %POSTGRES_TABLE% -lco precision=NO %SHAPE_ENCODING_CONFIG_STRING% -overwrite
    ogr2ogr -progress -f "PostgreSQL" PG:"host=%POSTGRES_HOST_IP% port=%POSTGRES_PORT_NUM% user=%POSTGRES_USR_ID% password=%POSTGRES_USR_PW% dbname=%POSTGRES_DB% schemas=%POSTGRES_SCHEMA%" "%SOURCE_SHAPE_FILE_PATH%" -lco GEOMETRY_NAME=geom -nlt %POSTGRES_GEOM_TYPE% -nln %POSTGRES_TABLE% -lco precision=NO %SHAPE_ENCODING_CONFIG_STRING% -overwrite
) else (
    echo ogr2ogr -progress -f "PostgreSQL" PG:"host=%POSTGRES_HOST_IP% port=%POSTGRES_PORT_NUM% user=%POSTGRES_USR_ID% password=%POSTGRES_USR_PW% dbname=%POSTGRES_DB% schemas=%POSTGRES_SCHEMA%" "%SOURCE_SHAPE_FILE_PATH%" -nlt %POSTGRES_GEOM_TYPE% -nln %POSTGRES_TABLE% %SHAPE_ENCODING_CONFIG_STRING% -append
    ogr2ogr -progress -f "PostgreSQL" PG:"host=%POSTGRES_HOST_IP% port=%POSTGRES_PORT_NUM% user=%POSTGRES_USR_ID% password=%POSTGRES_USR_PW% dbname=%POSTGRES_DB% schemas=%POSTGRES_SCHEMA%" "%SOURCE_SHAPE_FILE_PATH%" -nlt %POSTGRES_GEOM_TYPE% -nln %POSTGRES_TABLE% %SHAPE_ENCODING_CONFIG_STRING% -append
)

if errorlevel 1 (GOTO :OGR2OGR_ERROR)

set /a count+=1
echo:
GOTO :EOF

rem 참고. GOTO :EOF 는 현재 서브 루틴의 끝으로 간다는 의미다.

:NO_FOLDER_FOUND_ERROR
rem 참고. exit /b 1 은 현재 subroutine 을 종료, exit 1 은 전체 종료
echo [ERROR] "%SOURCE_SHAPE_DIRECTORY%" Folder Not Found!
exit /b 1

:OGR2OGR_ERROR
echo error while ogr2ogr command execution!
exit 1



example 4: SHP 파일 우선에서 특정 컬럼만 import 하기

ogr2ogr -overwrite --config PG_USE_COPY YES -progress -f PostgreSQL `
"PG:host=localhost port=5432 user=postgres password=postgres dbname=postgres" `
D:\shape_files\camel_test_data\half_and_half `
-lco GEOMETRY_NAME=geom `
-nlt POINT `
-nln coding_toast.half_total `
-sql "SELECT id as id, name as name From half_and_half"
  • geom 컬럼에 대한것은 -sql 에 작성하지 않는게 중요합니다!



example 5: SHP 파일에는 없지만, 테이블에는 있을 때

ogr2ogr -append --config PG_USE_COPY YES -progress -f PostgreSQL `
"PG:host=localhost port=5432 user=postgres password=postgres dbname=postgres" `
.\LSMD_CONT_LDREG_11_202311.shp `
-sql "SELECT sgg_oid as sgg_oid, 
jibun as jibun, 
bchk as bchk, 
pnu as pnu, 
col_adm_se as col_adm_se, 
'Y' as y_or_n,
'2024-01-03 15:20:11.463073 +09:00' as reg_dt 
From LSMD_CONT_LDREG_11_202311" `
-lco GEOMETRY_NAME=geom `
-nlt polygon `
-nln coding_toast.lsmd_cont_ldreg_11 `
--config SHAPE_ENCODING "EUC-KR"
  • 예시 컬럼 y_or_n (varchar), reg_dt (timestamp with time zone)
  • 날짜 string 을 만드는 법을 모르겠으면 그냥 postgresql db 에서
    select now() 쿼리 날렸을 때 나오는 문자열을 복붙하면 됩니다.
  • 참고로 날짜 string 이 되는 이유는 "이미 테이블이 생성되어 있고,
    해당 테이블의 컬럼이 날짜 타입"이여서 그렇습니다.
  • -append 가 아니라 -overwrite 이면 일반적인 문자열 타입의 column 생성됩니다.



example 6: primary key 는 반드시 default 로 시퀀스를 줘야한다.

create table only_one2
(
    fid        integer not null
        primary key,
    sgg_oid    numeric(9),
    jibun      varchar(100),
    bchk       varchar(1),
    pnu        varchar(19),
    col_adm_se varchar(5),
    geom       geometry(MultiPolygon, 5179)
);

위처럼 primary key 에 default 로 시퀀스 안주면 에러.



example 7: primary key 가 2개 이상일 때...

일단 결론부터 말하자면 두 primary key 는 반드시 숫자형이면서
default 값이 sequence 를 통해서 부여되는 형식이면 된다.

간단한 예시를 통해서 아래처럼 primary key 2개 있지만
하나는 sequence 가 부여됐고(fid), 다른 하나(fid2)는 아닌 테이블을 생성해봅니다.

이 상태에서 아래 명령어를 입력하면?

ogr2ogr -append --config PG_USE_COPY YES -progress -f PostgreSQL `
"PG:host=localhost port=5432 user=postgres password=postgres dbname=postgres schemas=coding_toast" `
.\only_one.shp `
-nlt MULTIPOLYGON `
-nln only_one `
--config SHAPE_ENCODING EUC-KR
# 참고로 shapefile 은 sgg_oid, jibun, bchk, pnu, col_adm_se 속성이 존재합니다.

이러면?

에러가 발생합니다.
이럴 때는 해당 fid2 에 시퀀스를 default 로 주면 됩니다.

alter table coding_toast.only_one
    alter column fid2 set default nextval('coding_toast.only_one_ogc_fid_seq'::regclass);

이후에 다시 명령어를 수행하면 잘 들어갑니다.



example 8: overwrite 로 import 시에 timestamp 만들기

shapefile 에는 없고, db table 생성 (=overwrite) 할 때는
timestamp column 이 생성되길 원하면 아래처럼 해보시길 바랍니다.

ogr2ogr -overwrite --config PG_USE_COPY YES -progress -f PostgreSQL `
"PG:host=localhost port=5432 user=postgres password=postgres dbname=postgres schemas=coding_toast" `
.\only_one.shp `
-nlt MULTIPOLYGON `
-lco GEOMETRY_NAME=geom `
-nln only_one `
-sql "select 
 sgg_oid as sgg_oid,
 jibun as jibun,
 bchk as bchk,
 pnu as pnu,
 col_adm_se as col_adm_se ,
 cast('2024-01-03 15:20:11.463073' as timestamp) as ss
from only_one
"

한계점 :
timestamp with time zone 으로 밖에 생성이 안됩니다.
timestamp without time zone 으로는 생성이 안되네요...





참고) SHP 파일의 한글 컬럼 대처법

사용한 데이터: https://www.vworld.kr/dtmk/dtmk_ntads_s002.do?svcCde=MK&dsId=20230915DS00004

아래처럼 select 절로 특정한 컬럼을 빼서 사용하고 싶다면 한글 컬럼명을 쌍따옴표로
둘러쌓으면 됩니다. -sql 절이 ""로 감싸져 있으므로 한번 escape 처리해줘야합니다.
저는 powershell 을 사용해서 백틱(`)으로 했습니다. bash(또는 cmd) 사용시에는
\ 를 사용해주세요.

ogr2ogr -overwrite -progress --config PG_USE_COPY YES `
-f PostgreSQL "PG:host=localhost port=5432 dbname=postgres schemas=coding_toast user=postgres password=postgres"`
".\376082_별도관리지역_A.shp" `
-sql "select
   `"산림보호`" as `"산림보호`"
    from `"376082_별도관리지역_A`"" `
-nlt MultiPolygon `
-lco GEOMETRY_NAME=geom `
-nln EZM_ECO_L `
--config SHAPE_ENCODING "EUC-KR"

여기서 주의해야 될 사항이 있습니다.
만약에 shape 파일의 인코딩(--config SHAPE_ENCODING "EUC-KR")을
정확히 명시하지 않으면 ERROR 1: Unrecognized field name 이라는 경고 문구만
딸랑 나오고 ogr2ogr 명령어가 실행되지 않습니다!

그런데 이런거 다 신경쓰기 싫고 그냥 ogr2ogr 로 import 하려는 거면
-sql 옵션을 사용하지 않으면 됩니다.





중요) SHP 🡲 PostgreSQL 업로드 시 속도저하 원인!

오늘(2024-01-04) GDAL 을 통해서 기능을 구현하던 도중에,
ShapeFile 🡲 PostgreSQL 로 데이터 업로드 시에 아주 Critical 한 문제를
마주쳤습니다. 항상 빠르게 업로드 되던 데이터들이 말도 안되게 느려지기 시작한겁니다!

분명 옵션으로 --config PG_USE_COPY 을 줘서 COPY 쿼리를 사용하면 빨라야되는데,
이상하게 INSERT 마냥 느린겁니다.

아래 쿼리를 돌려서 한번 확인해봤습니다.

select query
from pg_stat_activity
where application_name like '%GDAL%';

일반적이라면 아래처럼 COPY 를 사용한다고 나옵니다.


그런데 느린 쿼리에서는 아래처럼 INSERT 를 사용합니다!

참고로 COPY 쓰고 안 쓰고의 속도차이는 진짜 하늘과 땅 차이입니다.


이상해서 해당 ogr2ogr 명령어 옵션과 테이블의 DDL 을 번갈아서 봤습니다.


ogr2ogr 명령어

ogr2ogr -append --config PG_USE_COPY YES -progress `
-f PostgreSQL `
"PG:host=localhost port=5432 dbname=postgres schemas=nnd user=postgres password=postgres" `
"D:\shape_files\sample.shp"  `
-nlt MULTIPOLYGON `
-nln lsmd_cont_ldreg_11_time `
--config SHAPE_ENCODING "EUC-KR" `
-sql "select 
	sgg_oid as sgg_oid2,
    jibun as jibun2,
    bchk as bchk2,
    pnu as pnu2,
    col_adm_se as col_adm_se2,
    '111' as somecolumn 
from sample"

문제를 일으키는 테이블 DDL

create table nnd.lsmd_cont_ldreg_11_time
(
    ogc_fid    serial
        primary key,
    sgg_oid2    numeric(9),
    jibun2     varchar(100),
    bchk2       varchar(1),
    pnu2        varchar(19),
    col_adm_se2 varchar(5),
    somecolumn varchar(10) not null default '0123456789',
    some_date timestamptz not null default now(),
    geom       geometry(MultiPolygon, 5186)
);

뭐가 문제일까요?
이건 바로 some_date 컬럼에 있는 default now() 때문입니다.

잠시 COPY 가 정확히 어떤 방식으로 동작하는 지 돌아봅시다.
COPY 는 아래와 같은 식으로 쿼리가 짜집니다.

COPY "lsmd_cont_ldreg_11_time" ("geom", "sgg_oid2", ... 생략... ) 
FROM STDIN;

여기서 보면 STDIN 이 있죠? 이건 말 그대로 프로세스 stdIn 에서 값을 받아서
그대로 사용하겠다는 의미입니다. 저희 같은 경우에는 ShapeFile 의 속성정보와
'111' as somecolumn 처럼 저희가 static 하게 지정한 값들이 바로 stdIn 에
들어가게 되는 거죠.

즉 이말은 어떤 추가적인 연산이 필요로하지 않는 고정된 값들에 대해서는
COPY 가 동작할 수 있다는 의미입니다.

그런데 저희가 테이블에 지정한 default now() 의 경우는 어떤가요?
이건 STDIN 을 통해서 사용할 수 없는, 즉 정적이지 않은 값입니다!
반드시 DBMS 의 도움을 받아야 되기 때문에 INSERT 를 사용하는 겁니다!

gdal 은 upload 를 시작하기 전에 해당 테이블에 대한 default 정보를 한번
쓱 훑고 옵니다. 그때 now() 처럼 매 Feature 업로드마다 동적으로
연산이 필요하고 판단되는 경우에는 어쩔수 없이 INSERT 를 쓰게 됩니다.


그렇다면 저 테이블에 대해서는 COPY 가 동작하게 하려면 어떻게
ogr2ogr 명령어를 작성해야 될까요? 그건 바로 now() 연산이 사용되지 않도록
-sql 구문에서 해당 날짜 컬럼에 대해서 static 한 값을 부여하면 됩니다!

아래처럼 말이죠!

ogr2ogr -append --config PG_USE_COPY YES -progress `
-f PostgreSQL `
"PG:host=localhost port=5432 dbname=postgres schemas=nnd user=postgres password=postgres" `
"D:\shape_files\sample.shp"  `
-nlt MULTIPOLYGON `
-nln lsmd_cont_ldreg_11_time `
--config SHAPE_ENCODING "EUC-KR" `
-sql "select 
	sgg_oid as sgg_oid2,
    jibun as jibun2,
    bchk as bchk2,
    pnu as pnu2,
    col_adm_se as col_adm_se2,
    '111' as somecolumn,
    '2024-01-04 18:23:24.166074 +09:00' as some_date
from sample"

'2024-01-04 18:23:24.166074 +09:00' as some_date 처럼 어떤 static 한
값을 줌으로서 now() 연산이 발생하는 것을 사전에 막으면
다시 원래대로 COPY 연산을 합니다!




shp 주변에 인코딩 파일 또는 prj 파일이 없는 경우...

🤢 인코딩 파일 (*.cpg) 파일이 없는 경우

cpg 파일은 shp 의 인코딩을 명시하는 파일입니다.
Esri ShapeFile -> PostGIS import 시에 shp 주변에 cpg 파일이 없을 때는 반드시
--config SHAPE_ENCODING "EUC-KR" 처럼 명시적으로 인코딩을 지정해줘야 합니다.

ogr2ogr -overwrite -progress -f PostgreSQL `
PG:"host=localhost port=5432 user=postgres password=postgres dbname=postgres schemas=public" `
"쉐이프 파일 경로 작성" `
-lco GEOMETRY_NAME=geom `
-nlt MULTIPOLYGON `
-nln lsmd_cont_ldreg_test_11 `
-lco precision=NO `
--config SHAPE_ENCODING "EUC-KR" # SHAPE_ENCODING 설정! 중요!!!!

만약에 지정을 안하면 기본으로 import 할 때는 UTF8 인코딩을 사용합니다.
만약 실제 SHP 파일의 인코딩이 UTF8 아니고, cpg 파일도 없다면?
import 중에 아래와 같은 에러를 뱉어낼 겁니다.

ERROR 1: COPY statement failed.
ERROR:  invalid byte sequence for encoding "UTF8": 0xb5
CONTEXT:  COPY lsmd_cont_ldreg_test_11, line 1

ERROR 1: ERROR:  relation "lsmd_cont_ldreg_test_11" does not exist

ERROR 1: ERROR:  relation "lsmd_cont_ldreg_test_11" does not exist

ERROR 1: no COPY in progress

ERROR 1: Unable to write feature 99999 from layer LSMD_CONT_LDREG_11_202311.
ERROR 1: Terminating translation prematurely after failed
translation of layer LSMD_CONT_LDREG_11_202311 (use -skipfailures to skip errors)

주의사항: shapefile 에서는 *.cpg 말고도 *.cst 라는 인코딩 파일이 있습니다.
cpg 와 마찬가지로 단순한 텍스트 파일이고, 내부에 EUC-KR, UTF-8 처럼
단순한 문자열 하나만 달랑 씌여 있습니다.

다만 gdal 은 *.cst 파일은 인식을 못합니다!
그러니 *.cst 를 사용할 때는 UTF-8 이 아닌 이상 무조건
--config SHAPE_ENCODING "EUC-KR" 조건을 세팅해야겠죠?

shape 인코딩 관련해서 알아두면 좋은 글:
https://gis.stackexchange.com/questions/3529/which-character-encoding-is-used-by-the-dbf-file-in-shapefiles



🤢 prj 파일이 없는 경우

이번에는 좌표계 정보가 없어서 아마 데이터가 정상적으로 들어가더라도,
geometry 컬럼의 타입을 보면 아래처럼 SRID 가 지정이 안된 상태로 저장된
것을 확인할 수 있습니다.

geom  geometry(MultiPolygon)

이를 위해서는 -a_srs EPSG:5186 처럼 명시적으로 좌표계를 지정해줘야 합니다.
아래 예시처럼 옵션을 주면 됩니다!

ogr2ogr -f PostgreSQL `
PG:"dbname='postgres' host='localhost' port='5432' user='postgres' password='postgres'" `
./ctprvn.shp -lco GEOMETRY_NAME=geom `
-lco FID=gid -lco SPATIAL_INDEX=GIST `
-nlt PROMOTE_TO_MULTI -nln ctprvn_test `
-overwrite -progress `
-a_srs EPSG:5186  # 요렇게 명시적으로 표시해야 합니다!!!!!




4. PostgreSQL -> PostgreSQL

여기서 사용되는 옵션 중에 --config PG_USE_COPY YES 을 항상 주는데,
이렇게 해야 속도가 빠릅니다 ( 참고링크 ).


example 1

ogr2ogr -append --config PG_USE_COPY YES -progress -f "PostgreSQL" `
PG:"host=localhost port=5432 user=postgres password=postgres dbname=postgres schemas=public" `
PG:"host=192.168.100.5 port=5432 user=postgres password=postgres dbname=postgres schemas=public" `
-nln lsmd_cont_append_tt `
-sql "SELECT A.pnu AS pnu,
A.jibun AS jibun, A.bchk AS bchk,
A.sgg_oid AS sgg_oid,
A.col_adm_se AS col_adm_se,
A.geom AS geom FROM public.some_table A" `
-nlt multipolygon 
  • 192.168.100.5 IP (가상의 IP 입니다) 를 갖는 Database 의 테이블을 Local DataBase 로 복사하는 과정입니다.
  • 이번에는 -overwrite 대신에 -append 를 사용해봤습니다.
  • overwrite 은 데이터를 완전히 덮어쓰기를 하지만,
    append 는 기존 내용을 유지한 상태로, 새로운 내용을 덧붙입니다.
  • append 는 또한 해당 target 이 기존에 없다면 새로 만들고 데이터를 넣습니다.
  • -nlt 옵션을 안 주면 Geometry 타입이 "Geometry" 로 들어갑니다. 그러므로 nlt 옵션은 꼭 주셔야 합니다!
  • 참고로 -append 옵션을 사용하면서 이미 존재하는 테이블에 데이터를 넣는 상황에서
    lco (= Layer Creation Options) 옵션을 사용하면 무시됩니다! 아래와 같은 경고문이 나옵니다.
    - Warning 1: Layer creation options ignored since an existing layer is being appended to. 라는 에러가 보입니다!

example 2

ogr2ogr -overwrite --config PG_USE_COPY YES -progress -f "PostgreSQL" `
PG:"host=localhost port=5432 user=postgres password=postgres dbname=postgres schemas=public" `
PG:"host=192.168.100.5 port=5432 user=postgres password=postgres dbname=postgres schemas=public" `
-nln my_table `
-sql "SELECT A.id AS id, A.name AS name, 
A.geom AS geom FROM public.remote_table A" `
-nlt Point
  • nlt 를 통해서 정확히 어떤 Type 의 Geometry 타입을 사용하는지 지정할 수도 있습니다.
  • 가져온 데이터의 테이블을 어떤 명칭으로 지정할 대는 nln 을 쓰면 됩니다!



5. overwrite 대신 내용 지웠다가 새로 insert 하기

가끔은 overwrite 보다는 target 의 내용물들을 모두 지우고 insert 를
하고 싶을 때가 있습니다. 예를 들어서 Partition 테이블이 경우가 그렇습니다.

Partition 테이블은 지우게 되면, 다시 생성하고 나서 attach 를 해야되는
불편한 상황이 발생하기 때문이죠.

아무튼 이런 경우에 아주 유용한게 바로 -sql "truncate table <테이블명> 옵션입니다.


https://gis.stackexchange.com/questions/188078/use-ogr2ogr-to-delete-content-of-table-and-load-new-data-from-shapefile

# 일단 한번 truncate 하고...
ogrinfo `
PG:"host=localhost port=5432 user=postgres password=postgres dbname=postgres schemas=public" `
-sql "truncate table lsmd_cont_ldreg_test_29"

# 다시 append!
ogr2ogr -append --config PG_USE_COPY YES -progress -f "PostgreSQL" `
PG:"host=localhost port=5432 user=postgres password=postgres dbname=postgres schemas=public" `
PG:"host=localhost port=5432 user=postgres password=postgres dbname=postgres schemas=public" `
-nln lsmd_cont_ldreg_test_29 `
-sql "SELECT A.pnu AS pnu, A.jibun AS jibun,
A.bchk AS bchk, A.sgg_oid AS sgg_oid, A.col_adm_se AS col_adm_se,
A.geom AS geom FROM coding_toast.lsmd_cont_ldreg_test_29 A"

최근에는 이 방법도 된다고 한다.

ogr2ogr -append PG:dbname=foo abc.shp --config OGR_TRUNCATE YES



6. Esri ShapeFile -> Esri ShapeFile

example1 : 인코딩 변경하기

은근히~ 인코딩 변경할 일이 정말 많습니다. 알아두면 진짜 도움 많이 됩니다.

ogr2ogr file_utf8.shp .\file.shp -lco ENCODING=UTF-8
# 출처:
# https://gis.stackexchange.com/questions/260105/converting-encoding-using-ogr2ogr-on-macos
  • 위처럼 하면 file.shp 과 매칭된 파일의 피쳐 정보들의 인코딩 방식이 UTF-8 로
    변경되고, 인코딩 변경된 결과 파일이 file_utf8.shp 로 나오게 됩니다.

참고
만약에 인코딩 변경 이 아니라, 그냥 인코딩을 set 해주고 싶으신 거면
shp 파일 주변에 있는 cpg 파일에 작성된 인코딩을 변경해주면 됩니다.
혹시 cpg 파일이 없다면 수동으로 생성해주면 됩니다!
(아래 그림 참고)



example2 : 기존 SHP 에서 몇개의 Feature 만 뽑아내기

방법1:

ogr2ogr -f "ESRI Shapefile" .\only_three_point.shp .\half_and_half.shp -sql "select * from half_and_half limit 3"

# 참고로 cpg 파일까지는 복사를 해주지 않으니, 이건 따로 명령어를 작성해주자.
cp .\half_and_half.cpg .\only_three_point.cpg


# 다하고 나서 확인
ogrinfo -sql "select * from only_three_point" ".\only_three_point.shp"  -geom=summary -q -oo ENCODING=EUC-KR

방법2:

ogr2ogr -f "ESRI Shapefile" .\LSMD_CONT_LDREG_11_3_FEATURE.shp .\LSMD_CONT_LDREG_11_202312.shp -limit 3




ogrinfo

피쳐 갯수 조회

ogrinfo 를 사용하면 GIS 포맷 데이터의 다양한 메타정보를 조회할 수 있습니다.
여기서는 간단한 데이터 갯수 조회법만 작성하지만, 더 다양한 방법으로도 사용 가능합니다.

ogrinfo "D:/test/reverse_geocoding_test.csv" `
-sql "SELECT COUNT(*) AS SOURCE_COUNT FROM reverse_geocoding_test"

결과:

INFO: Open of `D:/test/reverse_geocoding_test.csv'
      using driver `CSV' successful.

Layer name: reverse_geocoding_test
Geometry: None
Feature Count: 1
Layer SRS WKT:
(unknown)
SOURCE_COUNT: Integer (0.0)
OGRFeature(reverse_geocoding_test):0
  SOURCE_COUNT (Integer) = 334506

전반적 메타 정보 조회

입력:

ogrinfo -so -al .\kid_okZone_seoul_EUC_KR.shp

출력:

INFO: Open of `.\kid_okZone_seoul_EUC_KR.shp'
      using driver `ESRI Shapefile' successful.

Layer name: kid_okZone_seoul_EUC_KR
Metadata:
  DBF_DATE_LAST_UPDATE=2023-11-21
Geometry: Point
Feature Count: 504
Extent: (182594.580036, 538480.829988) - (215322.251817, 563037.410001)
Layer SRS WKT:
PROJCRS["Korea 2000 / Central Belt 2010",
    BASEGEOGCRS["Korea 2000",
        DATUM["Geocentric datum of Korea",
            ELLIPSOID["GRS 1980",6378137,298.257222101,
                LENGTHUNIT["metre",1]]],
        PRIMEM["Greenwich",0,
            ANGLEUNIT["degree",0.0174532925199433]],
        ID["EPSG",4737]],
    CONVERSION["Korea Central Belt 2010",
        METHOD["Transverse Mercator",
            ID["EPSG",9807]],
        PARAMETER["Latitude of natural origin",38,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8801]],
        PARAMETER["Longitude of natural origin",127,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8802]],
        PARAMETER["Scale factor at natural origin",1,
            SCALEUNIT["unity",1],
            ID["EPSG",8805]],
        PARAMETER["False easting",200000,
            LENGTHUNIT["metre",1],
            ID["EPSG",8806]],
        PARAMETER["False northing",600000,
            LENGTHUNIT["metre",1],
            ID["EPSG",8807]]],
    CS[Cartesian,2],
        AXIS["northing (X)",north,
            ORDER[1],
            LENGTHUNIT["metre",1]],
        AXIS["easting (Y)",east,
            ORDER[2],
            LENGTHUNIT["metre",1]],
    USAGE[
        SCOPE["Cadastre, topographic mapping."],
        AREA["Republic of Korea (South Korea) - onshore between 126°E and 128°E."],
        BBOX[33.14,126,38.33,128]],
    ID["EPSG",5186]]
Data axis to CRS axis mapping: 2,1
CONTS_ID: String (254.0)
CONTS_NAME: String (254.0)
IMG_URL: String (254.0)
... 생략!

파일에 특수문자 껴있을 때 조회법

가끔 파일에 특수문자가 있으면 단순하게 ogrinfo -sql 만으로는 조회가 안됩니다.

ogrinfo .\sample_comma+fixed.csv  -sql "select count(*) as tot_cnt from sample_comma+fixed"

INFO: Open of `.\sample_comma+fixed.csv'
      using driver `CSV' successful.
ERROR 1: SQL Expression Parsing Error: syntax error, unexpected '+', expecting end of string. Occurred around :
ct count(*) as tot_cnt from sample_comma+fixed
                                        ^



이럴 때는 아래처럼 "" 로 감싸 주면 됩니다.
다만! 어떤 운영체제에서 어떤 Escape 문자열을 제공하냐에 다라 다르게
약간의 차이가 있습니다.

window cmd => \ 로 escape :

ogrinfo .\sample_comma+fixed.csv -sql "select count(*) as tot_cnt from \"sample_comma+fixed\""

window powershell => 백틱( ` ) 로 escape :

ogrinfo .\sample_comma+fixed.csv  -sql "select count(*) as tot_cnt from `"sample_comma+fixed`""

bash 사용시 => \ 로 escape :

ogrinfo .\sample_comma+fixed.csv  -sql "select count(*) as tot_cnt from \"sample_comma+fixed\""

그외에도 -dialect sqlite 를 설정하고, from 절의 파일명을 '' 로 감싸주는 방법도 있습니다.

ogrinfo .\sample_comma+fixed.csv -dialect sqlite `
-sql "select count(*) as tot_cnt from 'sample_comma+fixed'" 

INFO: Open of `.\sample_comma+fixed.csv'
      using driver `CSV' successful.
... 생략 ...

인코딩 확인하는 법

인코딩은 사실 shp 파일 주변에 있는 cpg 의 내용을 보면 됩니다.
다만 cpg 파일이 보이지 않으면 개발자 스스로 알아내야 하는데,
나의 경우는 다음과 같이 알아냅니다.

-oo ENCODING=EUC-KR 사용 여부를 집중해서 봐주세요!

# 먼저 아래처럼 입력해서 글자가 깨지는 지 확인, 이때 사용되는 인코딩은 UTF-8 입니다.
ogrinfo -sql "select * from LSMD_CONT_LDREG_30_202311 limit 1" .\LSMD_CONT_LDREG_30_202311.shp -geom=summary -q

# 깨지면 Encoding 설정해서 재시도, 이번에는 EUC-KR 로 확인.
ogrinfo -sql "select * from LSMD_CONT_LDREG_30_202311 limit 1" ./LSMD_CONT_LDREG_30_202311.shp -q -geom=summary -oo ENCODING=EUC-KR

저는 주로 UTF-8, EUC-KR 둘 중 하나인데,
위처럼 빠르게 한번 돌려서 글자가 깨지는 지를 확인해서 인코딩을 체크합니다. (아래그림 참고)

참고로 -oo ENCODING=EUC-KR 같은 세팅을 아예 안주면 UTF8 로 해석합니다.




gdalsrsinfo

shp 의 간단한 PROJ4, WKT, SRID 등을 조회할 수 있습니다.

gdalsrsinfo .\kid_okZone_seoul_EUC_KR.shp -e

출력:

EPSG:5186 # ==> 이건 -e 옵션을 줘서 나오는 겁니다!

PROJ.4 : +proj=tmerc +lat_0=38 +lon_0=127 +k=1 +x_0=200000 +y_0=600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs

OGC WKT2:2018 :
PROJCRS["Korea 2000 / Central Belt 2010",
    BASEGEOGCRS["Korea 2000",
        DATUM["Geocentric datum of Korea",
            ELLIPSOID["GRS 1980",6378137,298.257222101,
                LENGTHUNIT["metre",1]]],
        PRIMEM["Greenwich",0,
            ANGLEUNIT["degree",0.0174532925199433]],
        ID["EPSG",4737]],
    CONVERSION["Korea Central Belt 2010",
        METHOD["Transverse Mercator",
            ID["EPSG",9807]],
        PARAMETER["Latitude of natural origin",38,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8801]],
        PARAMETER["Longitude of natural origin",127,
            ANGLEUNIT["degree",0.0174532925199433],
            ID["EPSG",8802]],
        PARAMETER["Scale factor at natural origin",1,
            SCALEUNIT["unity",1],
            ID["EPSG",8805]],
        PARAMETER["False easting",200000,
            LENGTHUNIT["metre",1],
            ID["EPSG",8806]],
        PARAMETER["False northing",600000,
            LENGTHUNIT["metre",1],
            ID["EPSG",8807]]],
    CS[Cartesian,2],
        AXIS["northing (X)",north,
            ORDER[1],
            LENGTHUNIT["metre",1]],
        AXIS["easting (Y)",east,
            ORDER[2],
            LENGTHUNIT["metre",1]],
    USAGE[
        SCOPE["Cadastre, topographic mapping."],
        AREA["Republic of Korea (South Korea) - onshore between 126°E and 128°E."],
        BBOX[33.14,126,38.33,128]],
    ID["EPSG",5186]]
  • gdalsrsinfo .\kid_okZone_seoul_EUC_KR.shp -e -o epsg 처럼 입력하면 EPSG 코드 값만 조회할 수도 있습니다.




gdalinfo

gdal 버전 정보

gdalinfo --version




It comes from another PROJ installation. 에러 해결법

PROJ_LIB 환경변수가 꼬여서 그런 걸 수도 있습니다.
ogr2ogr 명령어 사용 이전에 아래처럼 아예 PROJ_LIB 환경변수에 아무값도 세팅하지
않으면 문제가 해결됩니다.

$env:PROJ_LIB=""
# 이후에 ogr2ogr 명령어 실행




참고 링크

profile
백엔드를 계속 배우고 있는 개발자입니다 😊

0개의 댓글