[osmdroid] 안드로이드 지도 개발 - 커스텀 지도

panax·2023년 8월 29일
0

osmdroid

목록 보기
4/4
post-thumbnail

📌커스텀 지도

지도 기능을 만들다 보면 커스텀 지도를 만들어야 할 때도 있다. 예를 들어 다른 좌표계를 사용하는 지도를 만들거나 게임 내에 있는 지도를 구현하는 경우 등이 있다.

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

profile
안드로이드 개발자

0개의 댓글