지도 기능을 만들다 보면 커스텀 지도를 만들어야 할 때도 있다. 예를 들어 다른 좌표계를 사용하는 지도를 만들거나 게임 내에 있는 지도를 구현하는 경우 등이 있다.
osmdroid는 기본으로 제공하는 지도말고 다른 지도를 설정할 수 있다.
다음 예제들은 국토정보맵을 사용했다.
타일 소스을 사용하면 지도 이미지의 위치를 지정할 수 있다.
타일 소스는 ITileSource 클래스를 상속해서 만들 수 있는데, 예제로 사용할 지도 이미지는 인터넷으로 받아올거라 OnlineTileSourceBase를 사용한다.
getTileURLString을 구현해야하는데, 입력으로 타일 인덱스가 주어지면 그에 해당하는 타일 이미지 주소를 반환하면 된다. 여기서는 국토정보맵의 URL을 사용하고 있다.
class NgiiTileSource(
tileSourceName: String = "영상지도",
zoomMinLevel: Int = 5,
zoomMaxLevel: Int = 18,
tileSize: Int = 256,
fileExtends: String = ".jpg",
baseUrl: Array<String> = arrayOf(TILE_URL)
) : OnlineTileSourceBase(
tileSourceName,
zoomMinLevel,
zoomMaxLevel,
tileSize,
fileExtends,
baseUrl
) {
override fun getTileURLString(pMapTileIndex: Long): String =
baseUrl + "&tilematrix=${MapTileIndex.getZoom(pMapTileIndex)}" +
"&tilerow=${MapTileIndex.getY(pMapTileIndex)}" +
"&tilecol=${MapTileIndex.getX(pMapTileIndex)}"
}
- 위 코드에서 TILE_URL은 국토지리정보원의 URL을 사용한다.
- 지도를 사용할 사람은 국토정보맵 페이지에서 웹브라우저의 개발자 모드를 사용하면 openapi 폴더에 wmts_ngii 파일이 있는데 URL과 API 키를 볼 수 있다.
타일 시스템에서 지도의 좌표계를 설정한다. WGS84 같은 위경도 좌표계를 사용하면 설정하지 않아도 되지만, 다른 좌표계를 사용한다면 설정해야 한다.
코드 자체는 다음 3가지로 나눠진다.
대부분의 경우 아래 코드에서 bound 변수를 제외하면 코드를 수정할 일이 없을 거라 생각한다.
class CustomTileSystem(
var bound: RectF = NGII_BOUND
) : TileSystem() {
override fun getX01FromLongitude(longitude: Double): Double =
(longitude - minLongitude) / (maxLongitude - minLongitude)
override fun getY01FromLatitude(pLatitude: Double): Double =
(maxLatitude - pLatitude) / (maxLatitude - minLatitude)
override fun getLatitudeFromY01(pY01: Double): Double =
maxLatitude - pY01 * (maxLatitude - minLatitude)
override fun getLongitudeFromX01(pX01: Double): Double =
minLongitude + pX01 * (maxLongitude - minLongitude)
override fun getMinLatitude(): Double = bound.bottom.toDouble()
override fun getMaxLatitude(): Double = bound.top.toDouble()
override fun getMinLongitude(): Double = bound.left.toDouble()
override fun getMaxLongitude(): Double = bound.right.toDouble()
}
예제로 사용한 지도는 흔히 쓰는 위경도 좌표계(WGS84)가 아닌 EPSG 5179 좌표계(UTM)을 사용하기 때문에 타일 시스템을 설정해야 한다.
국토정보맵의 지도 코드를 확인하면 좌측이 -200000, 상단이 4000000으로 되어있다.
우측과 하단도 있지만 그대로 사용하면 지도가 이상해진다.
좌측과 상단을 기준으로 다시 계산해야 올바른 범위를 구할 수 있다.
내 경우 다음 공식으로 값을 계산하니 문제가 없었다.
val BOUND = RectF(
-200000.0f, 4000000.0f, 16912760.32f, -13112760.32f
)
먼저 1픽셀이 현실에서 차지하는 길이(resoultion)를 구해야 한다.
국토정보맵 코드에 resolution이 있어서 계산을 하지 않아도 되지만 공식은 다음과 같다.
ScaleDenominator은 지도 축척 값이라 지도와 줌 레벨에 따라 다르다.
resolution = ScaleDenominator(화면과 지도의 비율) * 0.00028(1 픽셀의 길이)
1픽셀의 지도 길이를 구했으니 전체 픽셀 수를 곱하면 전체 지도 길이가 나온다.
타일 개수는 줌 레벨에 따라 다르다. 1레벨은 1개이고 레벨이 커질수록 2배로 늘어난다.
타일 당 픽셀 수는 지도 이미지 1개의 픽셀 수로 국토정보맵은 가로 256 픽셀을 사용한다.
가로 길이 = resolution * 2^(줌 레벨) * 타일 당 픽셀 수
이 값을 좌측에 더하면 우측이 나온다. 지도 이미지가 정사각형이라 상단 값에서 빼면 하단 값도 나온다.
사실 좌표계 내용은 어렵고 내용도 많아서 나도 완벽히 이해하고 사용하지는 못하고 있다.
다만 나처럼 커스텀 지도를 사용해야 하는 사람들에게 도움이 됐으면 좋겠다.
지도 타일 시스템
구글 맵 / Bing 맵 / WMTS
좌표계 정보
EPSG 5179
resolution 계산
StackExchange