DRAFT‎ > ‎

한반도 지도 차트

구글 차트 API에서는 지도 API를 마련해 세계지도와 미국 주단위 지도를 차트로 쓸 수 있게 하고 있다.

한반도의 도와 시군구 단위의 차트를 만들려면
  • 일반 GIS 프로그램은 보통 지원한다고 하므로 그걸 쓰거나
  • 엑셀 부가기능 중에 그런 게 있다고 한다. (근데 추가를 못 해봐서 되는지 어떤지 모르겠다)
정도의 방법이 있다. 구글 차트만큼 접근하기 쉬운 방법이 못 된다.

웹으로 한반도 지도 차트를 제공하기 위해서 필요한 절차는
  1. 대한민국 지방정부의 행정구역을 파악하되,
  2. 차트를 그릴 때 이용할 수 있는 형태인 벡터 이미지로 확보한 뒤
  3. 데이터베이스에 알맞은 스키마를 통해 저장하여
  4. 호출됐을 경우 적당한 이미지화를 거쳐 출력한다.
    1. 혹은 KML을 가공하지 않고 구글 맵 API에서 그대로 처리하는 방법도 있으나, 차트 용도에는 적합하지 않은 접근법이라 보인다.

행정구역 정보 확보

이때 행정구역을 구하기 위해 NGIS.go.kr 등을 돌아보았으나 원본 형태로 확보할만한 곳은 없었다.
구글맵에 사용자들이 참여한 정보의 원본을 받을 수 있으나, 자체 지도가 없는 저개발국을 위한 프로젝트이기 때문에 아프리카와 동남아 국가들뿐이고 대한민국은 없다.구글 어스에 행정구역 경계선이 나오므로 추출하여 이용할 수 있나 찾아보았더니 직접 추출할 수는 없다고 하면서 구글 어스 커뮤니티 링크가 있었고 거기에서 세계 행정구역 경계 목록을 찾았고 대한민국 도 단위시군구 단위의 .kmz 파일을 구했다.
(만든지 오래되지 않았음에도 서울이 서울직할시로 표기되고 울산이 없는 것으로 보아 참조한 자료가 상당히 오래됐으리라 짐작할 수 있지만, 일단 차트에 필요한 수준에서는 무시할 수 있다고 보고 차후 직접 수정을 통해 보완할 수도 있을 것이다.)

대신 지도 커뮤니티 자료실에서 .shp, .dbf 등이 있다는 것을 알게 되어 행정구역 파일을 확인했다. (ArcViewer 프로그램이 공식 어플인 것 같은데 설치 중에도 1935 에러가 떴고 필요한 패키지를 준비한 다음 설치를 하고 나서도 .shp를 어떻게 여는지 모르겠어서 포기)
EDSViewer로 XML과 CSV로 변환을 했는데 깔끔하긴 한데 내부 구조를 잘 모르겠다. 특히 좌표 방식을 모르겠다.

법정동 행정구역이 있는데 도 구분번호는 전화 지역번호인 건 알겠지만 거기 덧붙은 시 번호는 뭔지 모르겠다. 일단 법정동코드가 같이 붙어 있으므로 구분에 무리는 없을 것이다.

GIS 정보 추출

KML은 일련의 Polygon을 포함한 XML이므로 적당한 도구를 통해 XML에 포함된 정보를 추출하면 될 것이다.
이때 필요한 스키마는
  • 행정구역 수준 (level) : 도 혹은 그 이하
  • Polygon : 가변폭에 한 번에 저장해, 불러온 그대로 이미지 위에 표시한다.
  • 지명 : 선택적으로 차트에 표시?
정도가 있겠다.

도구로는 libkml과 그 바인딩인 python-kml이 있다.
#!env python
import kmlengine, kmldom
kmz=kmlengine.KmzFile.OpenFromFile('s_korea.kmz')
tmp=kmz.ReadKml()
dom=kmldom.ParseKml(tmp[1]) # tmp[0] True, tmp[1] str
kml=kmldom.AsKml(dom)
feat=kml.get_feature()
...
식으로 단계를 밟아나가면 된다.
libkml처럼 KML로 다룰 필요 없이, 일반적인 XML로 취급해 XQuery를 통해 원하는 노드에 접근, 필요한 내용을 추출하면 데이터베이스 준비용으로는 충분하다.

데이터베이스는 구글 앱엔진을 가정하고, 적당한 앱엔진용 loader를 구현하면 된다.
grep -n POINTS *dong.xml | sed 's/: <\//, /' | tr ':' '-' | awk '{print $1}' | xargs echo | tr ',' '\n' | bc | sort -gr
위 명령으로 동 단위 벡터를 구성하는 점의 갯수(POINTS 태그 사이에 한 줄에 하나씩 VERTEX가 있음)를 구해보니 한 자리 숫자도 있지만(계산쌍 순서로 보면 3637이고 .csv에서 보면 부산 남구 문현2동인데, 남구청 제공 행정구역 지도를 보니 그럴만하다) 최대값이 397이다. 즉 한 폴리곤을 위해 최소 5개에서 최대 397개의 좌표쌍이 소요된다는 얘기다.

근데 지금 보니 .csv에 있는 건 1동 2동이 따로 있는 걸 보니 행정동이네... 행정동도 코드가 있기는 한데... 그냥 법정동코드 있는 구 단위까지만 할까?-_- 구 단위는 법정동 코드에서 5자리까지만 쓰고 나머지는 0이라 코드도 간단해질 텐데. 좌표는 53개부터 2857개까지 있다.

이렇게 한 지역의 둘레 벡터를 구성하는 점의 좌표들이 적지 않아서 애초 생각했던 식으로 폴리곤 하나당 DB row 하나 구성은 불가능하겠다. 그렇다면 필드 구성을
법정동코드,폴리곤상점의순서,X좌표,Y좌표,폴리곤번호(섬은독립폴리곤)...
식으로 해서 동 단위 전체 점 갯수가 23만여 개고 구 단위는 18만여 개니까 40만이 넘는 row를 저장해야 한다는 건가?  호출할 때는 법정동코드를 공유하는 좌표들을 순서에 따라 가져오고?

시군구 통합 파일
의 폴리곤을 처리해보니 82만여 줄이 나온다. 근데 시군구 .csv에 저장된 법정동코드로 짐작되는 일련번호가 중복되는 게 있다. 전체 247줄이던 게 중복 제거하면 233줄 뿐이다. 가령 청주시상당구와 청주시흥덕구가 4311000000으로 같은데 이건 그냥 청주시 번호고 실제로는 4311100000과 4311300000이 각각 구 단위 번호다. 이렇게 되면 중복되는 것뿐 아니라 고유번호 자체가 잘못 매겨졌을 수도 있다는 건데 코드 말고 그냥 이름 필드로 비교해서 저장할까?

벡터 폴리곤 그리기

KML 자체를 렌더링하는 것은 없는 것 같고, 어차피 한반도라는 제한된 구간 안이므로 Polygon을 가져와 경도·위도를 좌표상의 가로·세로에 그대로 대입해 이미지를 생성할 수 있을 것으로 생각된다.
이때 GET 방식의 URL에 지정된 일련의 코드를 그대로 수용해 하나의 이미지 공간 안에 그려넣고 그대로 출력해주면 될 것이다. 가령 도와 시, 구가 섞여서 호출됐을 경우 (법정동코드를 쓴다면) 큰 단위부터 차례로 그려서 작은 단위가 덮어쓰도록 처리하면 (어차피 같은 영역을 공유하는 폴리곤이므로) 사용자의 의도가 무엇이든 간에 한 번에 출력할 수 있을 것으로 보인다.

일단 .csv 첫째 줄인 강릉시를 따로 빼다가 ImageMagick -draw 명령으로 그려보았다. POINTS의 좌표들은 단위가 상당히 큰데 그게 다 들어갈만한 크기로 하면 임시 파일 크기도 엄청나게 커지고 처리가 끝나지 않는다. 10만x10만 크기로 했을 때는 반나절 정도를 놔뒀다가 결국 포기했고, 1만x1만은 분 단위의 시간이 걸린 다음에 완료가 됐다. 1000x1000 정도가 순간적인 반응 속도를 보여줄 수 있는 수준인 것 같다. 구글 차트 API가 가로x세로 해서 출력 크기 30만 픽셀까지를 한계로 두고 있는데 에러 표시에는 메모리 사용량을 거론하지만 크기 때문도 있을 것 같다. 어차피 대단히 정밀한 그림이 필요한 것이 아니므로 차트의 좌표는 적당히 자릿수를 줄여서 처리해야 하겠다.

다음은 벡터를 그려낸 결과와 강릉시청 제공 행정구역 지도의 비교. 벡터는 그리고 보니 상하가 뒤집어져 있어서 돌려야 했다. 좌표 시작점이 다른 모양이다. 해안가에 선이 튀어나와 있는 건 무슨 이유인지 모르겠다. 원본의 좌표가 틀렸거나 추출 때 순서 등이 꼬인 것 같은데.

구글 앱엔진 적용

Datastore 저장

GeoPtProperty 필드를 지원하고, ListProperty로 여러 값을 하나에 넣을 수 있게 되어 있다. 만 단위까지 올라가는 값들을 다 넣을 수 있을지는 모르겠지만 일단 된다고 가정하면, 행정구역마다 한 row를 차지하고 그 중의 한 필드로 폴리곤 좌표를 넣을 수 있게 된다.

이때 곤란한 점은 한 영역에 속하는 폴리곤이 계속 이어지는 것이 아니라 중간중간 끝나고 다시 시작한다는 부분이다. XML로 추출한 내용에서 PART > START[INDEX="n"]이 왜 있는 건지 몰랐는데 좌표들이 새로 시작하는 부분을 안내하는 것으로 생각된다. (위의 강릉시 그림에서 해안선에 부자연스럽게 겹쳐진 길쭉한 부분이 제대로 끊어지지 않았기 때문에 생겼다)
따라서 Datastore에도 하나의 ListProperty 안에 전체 폴리곤을 넣지 않거나, 혹은 별개 필드로 PART 정보도 같이 저장해 그릴 때 참조하는 방법이 있겠다.

그런데 GeoPtProperty에 맞추기 위해 경위도로 변환을 했더니 예전 busfinder에 썼던 Proj 코드가 이상한 건지 tmerc->latlong이 아니었던 건지 한반도 모양은 맞는데 위치가 남서쪽으로 밀렸다. 애초 제대로 알고 계산식을 쓴 게 아니라서 어떻게 보정을 해야 되는지 모르겠다.
from pyproj import Proj, transform
latlong = Proj(proj='latlong',datum='WGS84',ellps='WGS84')
tm128 = Proj(proj='tmerc', lat_0='38N', lon_0='128E', ellps='bessel', x_0=400000, y_0=600000, k=0.9999, towgs84='-146.43,507.89,681.46')
utm = Proj(proj='tmerc', lat_0='38N', lon_0='127E', ellps='GRS80', x_0=200000, y_0=500000, k=0.9999, towgs84='-146.43,507.89,681.46')
long, lat = transform(utm,latlong,x,y)
처음에는 tm128->latlong을 했던 거였고, 이번엔 utm->latlong이 맞는 거였다. (저 식을 쓸 수 있게 공개해두신 분들께 감사를!) -- 하지만 자세히 보면 서쪽으로 300미터 정도 밀려 있다. 뭐, 이 정도야.

이미지 처리

현재 앱엔진은 제한적인 이미지 처리 API만을 제공하는데 벡터 처리는 전혀 포함되어 있지 않다. 최종 출력물의 품질이나 중간 가공 과정에서의 다양성을 생각해보면 가급적 벡터 기반으로 처리할 수 있어야 한다.

이미지 자체를 DB에 BLOB으로 저장하고 가져올 수 있다는 점, 그리고 API 중에 이미지 중첩이 있다는 점은 고려할만한 부분이다. 하지만 가령 고정적인 형태로 지역의 부분부분을 저장해둔다고 해도 차트로 표현하기 위해서는 선이 그려진 부분을 적당한 크기로 잘라내야 하고 그러기 위해서는 실시간으로 테두리를 검출하거나 사전에 이미지 바이너리와 함께 영역 정보도 기록해둬야 한다는 점을 고려해야 한다. (이 과정에서 이미지 크기에 따라 품질의 차이가 상당할 것이다)

그런데 외부 모듈로도 벡터를 구현할만한 게 도무지 안 보인다. aggdraw 모듈이 있기는 한데 소스 빌드를 해서 쓰는 거라 패키지에 같이 포함시켜서 올릴 수가 없을 것 같다. PNGCanvas라는 게 있긴 한데 polyline까지만 라인을 여러 개 그리는 식으로 처리해주고 polygon 내부를 색칠하거나 하는 동작은 지원하지 않는다. 정 없으면 wrapper 식으로 만들려고 했던 수준이라서 급한대로 쓸 수는 있겠지만 만족스러운 구현 수준은 아니다.

기타 메모

R 패키지가 SHP파일을 지원해 준다고 한다.

“국가수자원정보 종합관리 (www.wamis.go.kr)에서 동수준까지의 shp 파일을 제공하고 있습니다. 아직 행정구역 형태로는 이용해보지 않았지만, 홈페이지 상에서 보이는 것은 동 수준까지 보이네요. 참고하시기 바랍니다.” (게시판에 따르면 export 하는 식으로 받을 수 있나본데 행정구역 경계도는 아닌 것 같은 분위기라 일단 패스)

ċ
KIKcd_B.20100610
(2027k)
Jeong-Hee Kang,
2010. 7. 3. 오후 3:07
ċ
points.awk
(0k)
Jeong-Hee Kang,
2010. 7. 3. 오후 3:03
ċ
법정동_시군구.csv
(18k)
Jeong-Hee Kang,
2010. 7. 3. 오후 3:03
ċ
법정동_시군구.xml.gz
(7394k)
Jeong-Hee Kang,
2010. 7. 3. 오후 3:08
Comments