raster2mb

카테고리 없음 2011. 8. 15. 02:24

tilemill에서 사용하는 파일 형식인 mbtiles 형식을 사용 하다가 찾은 툴이 raster2mb이다. 현재 2.0 버전이 unstable한 채 진행중이고, stable한 버전은 1.1 버전의 mbtiles specification이다. 이 mbtiles 포멧(사양서)은 LOD 구조의 타일을 SQLite DB에 저장한다. 이 스펙을 토대로 구현된 프로그램은 다음과 같다( https://github.com/mapbox/mbtiles-spec/wiki/Implementations). 이 프로그램 중 파이썬(Python)으로 된 프로그램을 찾았는데 그 중 하나가 raster2mb이다. GDAL과 Python을 필요로 하며 사용법은 아래와 같다.

 

raster2mb.py raster_merc.tiff raster_merc.mbtiles

만약 GeoTIFF 파일이 Google Mercator 투영법인(EPSG:900913) 아니라면, 아래와 같이 gdalwarp 먼저 실행하여 Google Mercator 투영법을 적용시킨다:

gdalwarp -t_srs EPSG:900913 raster.tiff raster_merc.tiff

raster2mb -h  실행하여 도움말을 있다.

 

이 툴은 MBTiles 1.1 맞춰서 제작되었고 옵션 인자 값인 bounds 속성을 가지고 있지 않다.

마지막으로 가장 중요한 점!

전 세계를 포함하지 않는 경계부터 시작하는 이미지의 mbtiles 데이터 파일의 생성은 가능하지만 제대로 된 데이터가 아니다. 따라서 Mapbox에서도 사용할 수 없고 이상한 좌표에 이미지가 매칭된다. 따라서 tileset을 생성하고 싶으면 전세계를 포함하고 있는 이미지를 사용하여야 한다. http://www.maptiler.org/google-maps-coordinates-tile-bounds-projection/ 에 가면 Google Mercator(EPSG:900913)과 관련된 픽셀좌표와 zoom레벨을 표시해 준다. 먼저 자신이 가진 Tiff 파일을 Spherical Mercator(Google Mercator)의 타일 경계에 맞춰 처리한 후 작업을 하면 용이하다. 하지만 실험을 해 본 결과 줌 레벨이 커질수록 전세계를 포함하고 있는 이미지의 사이즈가 너무 커져 실제적으로 작업을 수행하기가 어려웠다. gdal2tiles.py를 사용하여 타일을 생성한 후 mb-util을 사용하는 편이 더 맞는 것 같다.

 

gdal라이브러리를 이용하기 때문에 OSGeo4W shell을 이용하면 raster2mb.py 스크립트를 실행할 수 있다.

 

mbtiles는database이기 때문에 이미지로 바로 사용할 수 없다. 따라서 mbtiles 파일을 이미지로 export하기 위해서는 다음 링크를 클릭하여 스크립트를 실행하면 된다(https://gist.github.com/837851).

 

gdal2tiles와 같은 툴로 폴더 별 타일이미지를 가지고 있고 이 이미지들을 MBTiles 파일로 만들고 싶다면 mb-util을 사용해야 한다. raster2mb는 Tiff 이미지를 MBTiles로 바꾸는 것이기 때문이다.

Posted by 강부자아들
,

carto 문법

카테고리 없음 2011. 8. 14. 19:34

Cartod에서 "and" 혹은 "or" 는 빈 문자나 "," 로 표현된다; 즉, "[zoom>6][zoom<10]" 는 "[zoom>6] and [zoom<10]"로 읽혀지고 "[zoom=4],[zoom=5]"는 "[zoom=4] or [zoom=5]"로 읽혀진다. 여러 개의 "and"들이나 "or"들을 사용할 때, TileMill은 논리적인 모호성을 배제 시키기 위해 "and" 조건을 먼저 처리하고 그 다음 "or"를 처리한다. 따라서 "[zoom>4][zoom<6],[zoom=6]"는 "([zoom>4] and [zoom<6]) or [zoom=7]"과 같이 읽힐 수 있다.

Posted by 강부자아들
,

TileMill을 사용하다가 SHP 파일을 인코딩과 관련된 문제점에 봉착하였다. 우선 UTF-8로 인코딩 되지 않은 EUC-KR로 인코딩된 문자는 깨져서 보이는 것이었다. 그래서 QGIS에서 SHP파일을 읽어 UTF-8로 다시 인코딩하여 Export한 후 TileMill에서 사용할 수 있게 Google Mercator로 투영된 prj 파일과 함께 zip파일로 묶어 TileMill에서 읽어보았다.

"행정지명"이라는 값을 가진 "구분" 필드를 조건을 걸어 주어 랜더링 하려고 했으나 아래와 같은 에러를 마주치게 되었다. 예를 들어 h004_point 레이어 같은 경우에는 명칭, 구분, 형태 등의 필드를 포함하고 있는데 이러한 필드에 조건(ex: h004_point [명칭= 'null'])을 걸어 TileMill에서 랜더링 하려고 할 때, 이것이 적용되지 않는 것이었다. field부분만 한글로 되면 Invalid code에러를 발생시키는 것이었다. 조건에 따른 데이터 값은 한글로 되어 있어도 잘 동작하였다(ex: h004_point[gubun='행정지명']).

[field="데이터 "]와 같은 식으로 조건이 들어가야 하는데 field가 한글일 경우 에러를 일으키는 것이었다.

원래 Mapnik에서는 encoding 요소가 없으면 기본으로 UTF-8 인코딩을 사용하지만, encoding 요소를 지정 해서 다른 인코딩도 적용시킬 수 있다. 하지만 TileMill은 encoding 요소를 지정할 방법이 없는 것 같았다. (https://github.com/mapbox/tilemill/issues/557, https://github.com/mapbox/tilemill/issues/547)

 

결국 내가 이 문제를 해결하는 방법은 TileMill에서 고쳐주는 것을 기다리는 것과 혹은 내가 TileMill을 고치는 것, 마지막으로 SHP파일을 고치는 것이다.

기다리기는 시간이 없고, TileMill은 Node.js로 되어 있어서 고치기는 쉽지 않고 시간도 많이 걸릴 것 같아 그냥 SHP파일의 Field명을 한글에서 영어로 고치기로 했다(절대 옳은 방법이 아니니 따라 하지 마세요!).우선 내가 사용할 조건이 있는 Field명을 뽑아 보기로 했다.

명칭, 구분, 도로번호, 종류, 기타, 용도, 구조, 층수, 형태 …

name, class, road_no, road_category, category, etc, usage, structure, floor, form …

어차피 이런 속성 데이터는 dbf파일에 저장되어 있기 때문에 dbf파일의 인코딩과 Field명을 고쳐주면 된다. 그리고 이러한 삽질을 한 GIS사이트가 있었는데 러시아 사이트였다. http://gis-lab.info/qa/dbf-encode.html 사이트에서 크릴릭 알파벳과 씨름하다 dbfpy(http://dbfpy.sourceforge.net/)와 이를 토대로 러시아 개발자가 만든 http://gis-lab.info/programs/python/dbf-encode.zip 프로그램을 다운받았다.

dbf-encode.py -f euc-kr -t utf-8 input.dbf output.dbf와 같은 형식으로 사용하면 된다. 하지만 또 문제점에 봉착하였다. Field가 인코딩이 변하지 않는 것이었다. 결국 field 설정 부분을 발로 짜 보았다(막 짠 것이니 아시는 분은 도움의 댓 글을 부탁 드립니다). 아래 코드를 사용하려면 한글로 된 필드 명에 대한 배열을 미리 사전 형태로 가지고 있어야 합니다. 필드 이름 자체를 한글로 utf-8인코딩 하려고 했으나 실패 하였습니다. QGIS에서도 UTF-8로 export해도 깨지는 글자가 있었습니다.

 

ngi = {'관리기관':'authority', '관리번호':'authority_no', '구분':'class', '구조':'structure', '기타':'etc', '높이':'height', '도로구분':'road_category', '도로번호':'road_no', '도로폭':'road_width', '도엽명':'map_name', '도엽코드':'map_code', '등고수치':'contour', '등급':'grade', '면적':'area', '명칭':'name', '번호':'no', '보도':'sidewalk', '분리대유무':'separator', '상태':'status', '상하구분':'updown', '상하단구분':'updown2', '설치연도':'year', '수령':'su-ryung', '수용량':'capacity', '수종':'su-jong', '수치':'value', '시점':'time', '연장':'length', '용도':'usage', '일방통행':'oneway', '자전거도로':'bicycle_road', '재질':'quality', '종류':'category', '종점':'terminal', '좌표':'coord', '주기':'period', '주생산광물':'main_mineral', '주성분':'ingredient', '직경':'diameter', '차로수':'road_count', '층수':'floor', '통과하중':'pass_load', '포장재질':'packing_material', '폭':'width', '표고':'altitude', '하천명':'river_name', '하천번호':'river_no', '행선지':'destination', '형태':'form', 'UFID':'UFID'}

 

# copy dbf header from inFile to outFile

for f in inFile.header.fields:

outFile.addField( f )

 

for f in outFile.header.fields:

key = unicode( f.name, options.inEncoding).encode(options.outEncoding)

field_name = ngi.get(key)

f.name = field_name

Posted by 강부자아들
,