GIS와 지도
지도 앱의 파란 점 하나 뒤에는 지구를 어떤 모양으로 볼지, 둥근 면을 어떻게 평면에 펼칠지, 좌표를 어떻게 저장하고 빨리 찾을지라는 질문이 겹쳐 있다. 타원체와 좌표계, 거리와 투영, 타일 지도, 공간 인덱스와 공간 질의, 경로 찾기와 위치 개인정보를 직접 계산한 그림으로 따라간다.
목차 (12)
지구의 모양: WGS84 타원체
지구는 공이 아니다. 자전 때문에 적도 쪽이 조금 부풀어, 위아래로 살짝 눌린 회전 타원체에 가깝다. 지도와 위치 계산은 먼저 "어떤 타원체를 기준으로 할지"를 정해야 하고, 그 약속을 측지 기준계(datum)라 한다. GPS가 쓰는 기준이 WGS 84다. 적도 반지름 a = 6,378,137 m, 편평률의 역수 1/f = 298.257223563으로 정의하고, 극 반지름 b = a(1 − f)는 계산으로 나온다. 둘의 차이는 약 21.4 km다. 반지름 6,000 km가 넘는 공에서 그 정도라, 눈으로는 공과 구별되지 않는다.
타원체는 매끈한 수학 모형이고, 실제 해수면을 육지 아래까지 이은 울퉁불퉁한 면(지오이드)과는 곳에 따라 꽤 어긋난다. 그래서 GPS가 주는 타원체 기준 높이와 지도에 적힌 해발 고도는 다르다. 나라마다 자기 땅에 잘 맞는 기준계를 따로 두기도 하는데, 한국은 GRS80 타원체를 쓰는 KGD2002(한국 측지계 2002)를 쓴다. GRS80과 WGS 84는 거의 같아서 일상적인 정밀도에서는 구분하지 않아도 된다. 위성 신호로 위치를 재는 원리는 위치 측정 노트에 있다.
위경도와 좌표계: EPSG 코드
위도는 적도에서 남북으로 잰 각도(−90°~90°), 경도는 그리니치 본초 자오선에서 동서로 잰 각도(−180°~180°)다. 서울시청 부근은 대략 북위 37.5665°, 동경 126.978°다. 위경도는 각도라서 1도의 길이가 장소마다 다르다. 위도 1도는 어디서나 약 111.2 km지만, 경도 1도는 적도에서 가장 길고 극으로 갈수록 cos(위도)만큼 줄어 서울 위도에서는 약 88.1 km다. 그래서 위경도를 그대로 x·y처럼 쓰면 거리와 넓이가 틀린다.
거리를 m로 바로 재고 싶을 때는 땅을 평면에 펼친 투영 좌표계를 쓴다. 좌표계가 워낙 많아서 번호로 부르는데, 그 목록이 EPSG 코드다. 위경도 WGS 84는 EPSG:4326, 웹 지도의 메르카토르는 EPSG:3857이다. 한국 지도에서 자주 보는 것은 KGD2002 기반 횡단 메르카토르(TM) 좌표계로, 중부 지역을 맡는 EPSG:5186은 원점을 동경 127°·북위 38°에 두고 음수가 나오지 않게 동쪽 200 km·북쪽 600 km를 더한다. 나라 전체를 한 장에 담는 EPSG:5179(UTM-K)는 127.5°를 중심으로 한다. 좌표 숫자만 보고는 어느 좌표계인지 알 수 없으니, 데이터를 주고받을 때는 늘 EPSG 코드를 함께 적는다.
좌표계만큼 자주 틀리는 것이 순서다. 흔히 "위도, 경도" 순서로 말하지만, GeoJSON 표준(RFC 7946)은 좌표를 [경도, 위도] 순서로 적으라고 못 박는다. 서울시청은 [126.978, 37.5665]이다. 더 헷갈리는 것은 EPSG:4326의 공식 정의가 위도를 첫 축으로 둔다는 점이다. 그래서 GDAL 같은 라이브러리는 정의대로 위도 먼저 읽을지, 관례대로 경도 먼저 읽을지(OAMS_TRADITIONAL_GIS_ORDER)를 따로 정하게 한다(GDAL 좌표계 안내). 위도는 ±90을 넘을 수 없으니 한국 좌표는 순서가 뒤집히면 대개 범위 오류로 드러나지만, 경도와 위도가 둘 다 90 아래인 유럽·아프리카 좌표는 오류 없이 엉뚱한 곳에 찍힌다. 데이터를 받으면 알려진 점 하나를 지도에 찍어 보는 것이 가장 빠른 검사다.
- GeoJSON의 다른 약속: 좌표계는 늘 WGS 84(경도·위도, 10진 도)이고 세 번째 값은 높이(m)다. 다각형의 바깥 고리는 반시계, 구멍은 시계 방향으로 돈다(오른손 법칙). 180° 경선을 가로지르는 도형은 그 선에서 잘라 둘로 나눠 적는다.
거리 계산: 하버사인과 평면 근사
지구 위 두 점 사이의 가장 짧은 길은 지구 중심을 지나는 평면이 지표를 자른 큰 원, 대원을 따른다. 지구를 반지름 6,371 km(평균 반지름)인 구로 보고 그 길이를 구하는 표준 공식이 하버사인이다. 위도 차와 경도 차의 반각 사인 제곱으로 a를 만들고, 중심각 c = 2·atan2(√a, √(1 − a))에 반지름을 곱한다. 작은 거리에서도 반올림 오차에 강한 것이 장점이다.
서울시청(약 37.5665°N, 126.978°E)과 부산시청(약 35.1796°N, 129.0756°E) 부근을 넣으면 하버사인으로 약 325.1 km다. 경도 차에 평균 위도의 cos을 곱한 뒤 피타고라스로 푸는 등장방형 근사는 325.1 km로 거의 같다. 도시 안이나 나라 하나 크기에서는 이 근사로 충분하고 계산도 싸다. 반면 위경도의 "도"를 cos 없이 그대로 평면 좌표로 놓으면 353.3 km로 8.7% 길게 나온다. 경도 1도를 위도 1도와 같은 길이로 쳤기 때문이다. 구 모델 자체도 타원체와 다르지만, 그 오차는 대개 0.3% 아래이고 적도를 가로지르는 경우에도 0.55% 정도라는 것이 Movable Type 해설의 설명이다. 그보다 정밀해야 하면 타원체 위에서 반복 계산하는 빈센티 공식 같은 방법을 쓴다.
지도 투영: 메르카토르와 웹 메르카토르
공의 껍질은 찢거나 늘이지 않고는 평면에 펼 수 없다. 그래서 모든 평면 지도는 각도·넓이·거리·방향 가운데 무언가를 희생한다. 이 펼치는 규칙이 투영이다. 1569년 게라르두스 메르카토르가 내놓은 메르카토르 투영은 각도를 지킨다(정각). 지도 위 직선이 일정한 나침반 방위로 가는 항로가 되어 항해에 쓰였다. 대가는 넓이다. 위도 φ에서 길이를 sec φ배, 넓이를 sec²φ배 부풀린다. 30°에서 넓이 ×1.33, 60°에서 ×4, 그린란드 중앙인 73° 부근에서 약 ×11.7이다. 지도에서 그린란드가 아프리카만 해 보이지만 실제로는 아프리카가 약 14배 크다.
구글 지도·OpenStreetMap 같은 웹 지도가 쓰는 것이 웹 메르카토르(EPSG:3857)다. 타원체 좌표를 구처럼 펼친 단순화된 메르카토르라, epsg.io는 이를 인정된 측지 좌표계가 아니라고 적고 쓰임을 웹 지도와 시각화로 한정한다. 정식 메르카토르(EPSG:3395)와 배율이 0.7% 정도 어긋나기 때문이다. 세계 전체가 정사각형이 되도록 위도 ±85.0511°에서 잘라 극지방은 아예 없다. 화면에서 길이를 재면 서울 위도에서는 실제보다 1.26배 길게 나오므로, 거리·넓이 계산은 투영 좌표가 아니라 위경도(하버사인)나 해당 지역용 좌표계(한국이면 5186·5179)로 한다. 넓이를 지키는 투영(알베르스·몰바이데 등)은 통계 지도에 쓴다. 단계구분도에서 넓은 땅이 커 보이는 문제는 데이터 시각화 노트에 있다.
벡터와 래스터
지리 데이터는 두 가지 모양으로 담는다. 벡터는 좌표로 그린 도형이다. 가게 위치는 점, 도로와 강은 선, 건물·행정 구역은 다각형(면)이다. 속성(이름, 도로 등급)을 도형마다 붙일 수 있고, 아무리 확대해도 깨지지 않는다. 웹에서 가장 흔한 형식은 JSON으로 도형을 적는 GeoJSON이다. Point·LineString·Polygon과 그 묶음(Multi…, GeometryCollection)을 쓰며, 좌표는 경도, 위도 순서다. "위도, 경도"로 말하는 습관 때문에 뒤집어 넣어 남극 근처에 점이 찍히는 실수가 아주 흔하다. 파일 기반의 셰이프파일·GeoPackage, 지도 타일용 벡터 타일도 있다.
래스터는 땅을 고른 격자로 나누고 칸마다 값 하나를 담은 것, 곧 이미지다. 위성·항공 사진, 기온·강수량 분포, 땅의 높이를 담은 수치 표고 모델(DEM)이 래스터다. 칸 크기가 해상도이고, 해상도를 두 배로 하면 칸 수는 네 배가 된다. DEM에서는 이웃 칸과의 높이 차로 경사·방향을 구하고, 물이 흘러갈 길과 산 그림자를 계산한다. 벡터는 "어디에 무엇이 있나", 래스터는 "곳곳의 값이 얼마인가"에 맞고, 둘을 겹쳐 분석하는 일이 GIS의 일상이다.
타일 지도: z/x/y
세계 지도를 한 장의 거대한 이미지로 보내면 아무도 기다려 주지 않는다. 웹 지도는 웹 메르카토르로 편 세계를 256×256 픽셀 타일로 잘라 둔다. 줌 0에서는 세계 전체가 타일 하나이고, 줌이 하나 오를 때마다 타일 하나가 넷으로 쪼개진다. 줌 z의 타일은 축마다 2^z개, 모두 4^z개다. 줌 10이면 1,048,576개, 줌 18이면 68,719,476,736개다. 브라우저는 지금 화면에 보이는 몇 장만 /z/x/y.png 주소로 받아 붙이니, 줌과 이동이 빠르고 같은 타일을 모든 사용자가 캐시로 나눠 쓴다.
위경도에서 타일 번호를 구하는 공식은 OpenStreetMap 위키에 있다. n = 2^z일 때 x = n·(경도 + 180)/360, y = n·(1 − asinh(tan φ)/π)/2를 내림한다. x는 경도에 비례하지만 y는 메르카토르의 세로 좌표라 위도에 비례하지 않는다. 원점은 왼쪽 위(북서쪽)다. 요즘은 이미지 대신 도형 데이터를 타일로 보내 브라우저가 직접 그리는 벡터 타일도 흔하다. 글자 방향을 돌리고 색을 바꾸는 일을 서버를 다시 거치지 않고 할 수 있다.
줌 0: 세계 전체가 256×256 픽셀 타일 하나다. 서울(37.5665°N, 126.978°E)은 당연히 0/0/0.
지오코딩
사람은 주소로 말하고 컴퓨터는 좌표로 계산한다. 주소 문자열을 좌표로 바꾸는 일이 지오코딩, 그 반대가 역지오코딩이다. 지오코딩은 생각보다 지저분하다. 먼저 입력을 정규화한다. 띄어쓰기·약칭("서울시" → "서울특별시")·오타를 고치고, 시·구·도로명·건물 번호로 쪼갠다. 그다음 주소 데이터베이스(건물마다 대표 좌표가 있다)와 맞춘다. 정확히 맞으면 그 건물의 좌표를, 번호만 없으면 도로 구간에서 번호 비율로 끼워 넣은 좌표를, 그것도 안 되면 동·구의 중심점을 돌려준다. 그래서 좋은 지오코더는 좌표와 함께 "건물 수준/도로 수준/행정 구역 수준" 같은 정확도 등급을 알려 준다.
한국에는 지번 주소와 도로명 주소가 함께 쓰여 한 건물에 주소가 둘 이상이고, 새로 난 길이나 건물은 데이터베이스에 늦게 들어간다. 역지오코딩은 좌표에서 가장 가까운 건물·도로를 찾는 공간 질의라, 아래의 공간 인덱스가 있어야 빠르다. 여러 주소를 한꺼번에 변환하는 일은 외부 서비스의 요청 한도와 이용 약관을 확인하고 한다.
공간 인덱스: 지오해시, R-트리, H3
"내 주변 1 km 안의 카페"를 찾을 때 가게 수백만 곳과 하나하나 거리를 잴 수는 없다. 데이터베이스의 B-트리는 값 하나의 크기 순서로 정렬하는데, 위치는 위도와 경도 두 값이라 그대로는 정렬이 안 된다. 공간 인덱스는 2차원을 다루는 요령이다. 가장 단순한 것이 지오해시다. 2008년 구스타보 니에메예르가 공개한 방식으로, 경도와 위도의 범위를 번갈아 반으로 나누며 "위쪽 절반이면 1, 아래쪽이면 0"을 적고, 이 비트를 5개씩 묶어 base32 글자(0–9와 a·i·l·o를 뺀 소문자)로 쓴다. 서울시청 부근은 6글자로 "wydm9q"이다.
서울(37.5665, 126.978)을 인코딩한다. 경도 −180~180, 위도 −90~90 전체에서 시작한다.
지오해시는 그냥 문자열이라 일반 인덱스로 앞부분 일치 검색을 할 수 있고, 글자가 길수록 칸이 작아진다(6글자면 오차 약 ±0.61 km). 약점은 경계다. 바로 옆 점이라도 칸 경계(특히 적도·본초 자오선·180° 경선)를 넘으면 앞글자가 전혀 달라지므로, 실제로는 내 칸과 이웃 여덟 칸을 함께 찾는다. 앞글자가 같으면 가깝지만, 가깝다고 앞글자가 같지는 않다.
R-트리는 1984년 안토닌 구트만이 제안했다. 가까운 객체끼리 묶어 그 묶음을 감싸는 최소 경계 사각형(MBR)을 한 단계 위에 적고, 이를 B-트리처럼 균형 잡힌 나무로 쌓는다. 질의 영역과 겹치지 않는 사각형은 그 안을 통째로 건너뛰니, 점뿐 아니라 선·다각형도 색인할 수 있다. 사각형끼리 덜 겹치게 넣는 개량판 R*-트리도 널리 쓰인다. PostGIS의 GiST 인덱스, SQLite의 R*Tree 모듈이 이 계열이다. H3는 Uber가 만든 육각형 격자 인덱스다. 육각형은 이웃 여섯 칸의 중심이 모두 같은 거리에 있어, 이동·수요를 칸 단위로 모아 분석하고 이웃으로 퍼뜨리는 계산이 사각형 격자보다 고르다.
공간 질의: 점–다각형 판정
GIS의 흔한 질문은 도형 사이의 관계다. 이 점이 이 구역 안에 있나(배달 가능 지역, 행정 구역 찾기), 두 도형이 겹치나, 이 점에서 가장 가까운 것은 무엇인가, 이 선에서 100 m 이내는 어디인가(버퍼). PostGIS·SpatiaLite 같은 공간 데이터베이스는 이런 관계를 ST_Contains·ST_Intersects·ST_DWithin 같은 함수로 제공하고, 먼저 공간 인덱스로 사각형이 겹치는 후보만 추린 뒤 정확한 도형 계산을 한다. 데이터베이스의 인덱스와 질의 실행 일반은 데이터베이스 노트에 있다.
"점이 다각형 안에 있나"를 푸는 고전이 광선 투사(짝홀 규칙)다. 늦어도 1962년 시므랏의 알고리즘으로 알려졌다. 점에서 한 방향으로 광선을 쏘아 다각형의 변을 몇 번 건너는지 센다. 안에 있는 점은 밖으로 나가려면 홀수 번 건너야 하므로 홀수면 안, 짝수면 밖이다. 오목한 다각형에도 그대로 통한다. 까다로운 것은 광선이 꼭짓점을 정확히 지날 때다. 한 꼭짓점을 두 변이 공유하니 두 번 세일 수 있는데, 변의 한쪽 끝만 포함하는 반열린 구간으로 세면 해결된다. 경계 위의 점이나 부동소수점 오차는 따로 허용 오차를 두어 다룬다.
경로 찾기
길 찾기는 도로망을 그래프로 보는 데서 시작한다. 교차로가 노드, 도로 구간이 간선이고, 간선에는 길이 대신 예상 소요 시간을 무게로 붙인다. 일방통행은 방향 있는 간선, 회전 금지는 교차로 안의 특별한 규칙이다. 그 위에서 최단 시간 경로를 찾는 기본이 다익스트라 알고리즘이다. 출발점에서 가장 가까운 확정되지 않은 노드를 하나씩 확정하며 이웃의 거리를 고친다. 동작과 증명, 우선순위 큐로 빠르게 만드는 법은 알고리즘 › 그래프의 다익스트라 절에 있다.
나라 하나의 도로망은 노드가 아주 많아 다익스트라를 그대로 돌리면 느리다. A*는 "목적지까지 직선거리" 같은 낙관적 추정을 더해 목적지 쪽으로 먼저 뻗는다. 하버사인 거리는 실제 도로 거리보다 길 수 없으니 좋은 추정치다. 실제 길 찾기 서비스는 여기에 미리 계산을 더한다. 중요하지 않은 노드를 지름길 간선으로 건너뛰게 해 두는 축약 계층(contraction hierarchies) 같은 기법으로 대륙 규모 경로를 아주 짧은 시간에 답한다. 실시간 교통으로 간선 무게가 계속 바뀌는 것이 또 다른 어려움이다.
위치 정보와 개인정보
위치는 가장 민감한 개인정보 가운데 하나다. 밤에 머무는 곳은 집, 낮에 머무는 곳은 직장이고, 병원·종교 시설·집회 장소 방문은 건강·신념을 드러낸다. 이름을 지운 위치 기록도 집·직장처럼 자주 머무는 점 몇 개로 사람을 다시 알아낼 수 있어서, 단순히 이름만 지우는 것은 익명화가 되지 못한다. 한국에는 위치 정보만 따로 다루는 법률(위치정보의 보호 및 이용 등에 관한 법률)이 있을 만큼 별도로 보호한다.
- 정밀도 줄이기: 기능에 필요한 만큼만 남긴다. 날씨는 도시, 동네 소식은 동 수준이면 된다. 위도 소수 자릿수 하나가 칸 크기를 10배 바꾼다.
- 모아서 보여 주기: 개인 점 대신 칸(H3·지오해시) 단위 개수로 집계하고, 사람이 적은 칸은 숨긴다.
- 덜 오래 두기: 실시간 위치는 쓰고 나면 지우고, 기록은 보관 기간을 정한다.
- 새는 곳 막기: 사진의 EXIF에 촬영 위치가 남고, 운동 기록 앱의 경로 공개로 집 위치가 드러나기도 한다. 올리기 전에 위치 메타데이터를 지운다.
- 동의와 표시: 브라우저·OS는 위치 권한을 따로 묻고, 앱은 왜 필요한지와 언제 쓰는지를 보여 준다. 브라우저가 위치를 어떻게 고르는지는 위치 측정 노트, GPS 신호와 무선 측위는 무선 통신 노트에 있다.
핵심 자료
지도 위에 데이터를 칠하는 법은 데이터 시각화 노트, 위치를 재는 원리는 위치 측정 노트에 있다.
| 자료 | 종류 | 요지 | 링크 |
|---|---|---|---|
| EPSG:5186 KGD2002 / Central Belt 2010 | 좌표계 정의 | 한국 중부 지역 TM 좌표계. GRS80 타원체, 원점 127°E·38°N, 가산 동 200,000 m·북 600,000 m. 같은 사이트에서 다른 코드도 찾아볼 수 있다. | epsg.io |
| EPSG:3857 WGS 84 / Pseudo-Mercator | 좌표계 정의 | 웹 지도가 쓰는 구형 메르카토르. 측지 좌표계로 인정되지 않으며 쓰임을 웹 지도와 시각화로 적어 둔 이유가 나온다. | epsg.io |
| Slippy map tilenames | 위키 | 위경도 ↔ 타일 번호 공식, 줌별 타일 수, 위도 한계 85.0511°, 여러 언어의 예제 코드. | OpenStreetMap Wiki |
| Calculate distance, bearing and more between Latitude/Longitude points | 해설 | 하버사인·등장방형 근사·방위각·중간점 공식을 자바스크립트로 설명하고, 구 모델의 오차 범위를 적었다. | Movable Type Scripts |
| H3 | 공식 문서 | 지구를 육각형 칸으로 나누는 계층형 공간 인덱스. Uber가 데이터 분석을 위해 만들어 Apache 2 라이선스로 공개했다. | h3geo.org |
- EPSG:5186 KGD2002 / Central Belt 2010 — epsg.io
- EPSG:3857 WGS 84 / Pseudo-Mercator — epsg.io
- Slippy map tilenames — OpenStreetMap Wiki
- Calculate distance, bearing and more between Latitude/Longitude points — Movable Type Scripts
- H3 — h3geo.org
- EPSG:5179 KGD2002 / Unified CS — epsg.io
- World Geodetic System — Wikipedia
- Mercator projection — Wikipedia
- Geohash — Wikipedia
- R-tree — Wikipedia
- Point in polygon — Wikipedia
- RFC 7946: The GeoJSON Format — IETF
- OGR 좌표계 안내(축 순서와 OAMS_TRADITIONAL_GIS_ORDER) — GDAL