Tmap 대중교통 API를 통해 받아온 값을 지도 위에 그리려고 한다. API 응답값이 좀 비대한데 문서에 응답값에 대한 설명이 아주 자세하게 나와있으니 너무 좋다.
응답값은 최적경로가 최상단으로 올라오는 것으로 보이고, `itineraries`의 각 원소는 각각의 루트로 보인다.
중요한 건 도보구간과 대중교통 구간이 다른 이름으로 정의되어있다는 것이다. 구간 좌표는 linestring으로 같지만 노드가 steps, passShape로 다르게 내려오는 부분과, linestring이 아래와 같이 내려온다.
"linestring": "126.937515,37.555115 126.93766,37.55516 126.93766,37.555157"
`latitude,longitude` 를 공백으로 구분해서 내려준다. 알고리즘 문제 푸는 것 처럼 공백을 기준으로 잘라서 좌표값을 가공해야된다.
네이버 지도의 경로선을 그리는 코드는 그래도 단순해서 다행이라 생각한다.
val path = PathOverlay()
path.coords = listOf(
LatLng(37.57152, 126.97714),
LatLng(37.56607, 126.98268),
LatLng(37.56445, 126.97707),
LatLng(37.55855, 126.97822)
)
path.map = naverMap
public LatLng(@NonNull Location location) {
this(location.getLatitude(), location.getLongitude());
}
공백을 기준으로 끊어서 LatLng객체로 만들어 처리하면 될 것 같다.
private fun parseLatLng(lineString: String): List<LatLng> {
return lineString.split(" ").map { coords ->
val (longitude, latitude) = coords.split(",")
LatLng(latitude.toDouble(), longitude.toDouble())
}
}
linestring 값을 받아 먼저 공백으로 끊고, 또 `,` 기준으로 latitude, longitude를 끊어서 나눠준다. List를 반환하니까 나중에 각 mode 별로 나온 list들을 합치는 작업도 해야된다. 근데 이건 addAll을 활용하면 된다.
Mode를 나누기 전에 약간의 가독성 상승을 위해서 enum class로 감싸줬다.
enum class Mode(val mode: String) {
WALK("WALK"),
BUS("BUS"),
SUBWAY("SUBWAY"),
EXPRESS_BUS("EXPRESS BUS"),
TRAIN("TRAIN"),
AIRPLANE("AIRPLANE"),
FERRY("FERRY"),
}
enum을 이렇게 쓰는 게 맞나 싶긴한데 일단 하드코딩되어있는 것보다는 낫지않을까... 제대로 된 이유를 알게되면 또 기록하겠다.
다음으로는 로딩 된 지도 위에 직접 그리는 작업이다.
@UiThread
override fun onMapReady(naverMap: NaverMap) {
lifecycleScope.launch {
// Tmap에서 받아온 값으로 navermap에 오버레이를 그린다.
val response = requestTmapAPI()
val routeOverlay = PathOverlay()
val routeData = mutableListOf<LatLng>()
// Log.d(TAG, "RouteData: $response")
val getBestRoute = response.metaData.plan.itineraries.first()
// Log.d(TAG, "getBestRoute: $getBestRoute")
for (route in getBestRoute.legs) {
when(route.mode){
Mode.WALK.mode -> {
for (lineString in route.steps.orEmpty()) {
routeData.addAll(parseLatLng(lineString.linestring))
}
}
else -> {
route.passShape?.let { lineString ->
routeData.addAll(parseLatLng(lineString.linestring))
}
}
}
}
// Log.d(TAG, "routeData: $routeData")
routeOverlay.apply {
coords = routeData
color = Color.BLUE
outlineWidth = 4
map = naverMap
}
}
}
가장 첫번째 itinerary 값이 최적경로로 보이니 그걸 가져와서 돌렸다. 수단 별로 경로 모양을 다르게 하려면 내가 쓴 것처럼 하면 안되지만 나는 일단 테스트 해보는 게 중요해서 else로 처리했다. else로 처리하면 Enum의 장점을 버리는 셈이니 기억해두자.
parseLatLng로 반환된 list값을 addAll해주면 routeData에 Collection의 elements들만 들어간다. Set형태가 아니니까 당연히 순차적으로 추가된다.
경로선 그리는 부분은 네이버 지도 공식문서에 잘 나와있었다.
경로선이 예쁘게 그려지는 모습을 볼 수 있다. 구간별 경로선을 그리는 방법도 제공하고 있으니 본 프로젝트에 돌입하면 도입하는게 좋겠다.
도움이 됐다면 댓글이나 공감 버튼 한 번씩 누르고 가주세요!
'Android 🖥️ > 삽질⛏️' 카테고리의 다른 글
ViewType을 나눠 RecyclerView를 구성하기(feat. SealedClass) (0) | 2024.07.23 |
---|---|
Expandable FAB(Floating Action Button) 구현하기 - ListPopupWindow (0) | 2024.07.20 |
Ktor로 간단한 API 호출하기 - Header에 Key 붙여서 (feat. Tmap 대중교통 API) (0) | 2024.07.11 |
Widget 알아보기(1) - 위젯에서 위치 정보 받기 (0) | 2024.07.09 |
Kotlin 한글 종성 구분하기 - 체언에 따라 조사 다르게 붙이기 (0) | 2024.05.24 |