이번엔 카카오맵에 버스 정류장을 띄우고 그 버스 정류장의 고유 ID 값을 이용해 버스 도착 정보를 받는 기능을 구현하려고 한다.
공공데이터포털에서 사용할 API는 세 가지이다.
국토교통부(TAGO)버스정류소정보를 통해 버스 정류소 정보를 받아온다.
경기도_버스도착정보 조회를 통해 버스 정류소의 버스 도착 정보를 알 수 있다.
경기도_버스노선 조회로 버스의 상세 정보를 얻을 수 있다.
먼저 의존성 주입을 하기 위한 Module을 만들어준다.
@Module
@InstallIn(SingletonComponent::class)
object BusAPIModule {
private const val BASE_URL = "http://apis.data.go.kr"
@Singleton
@Provides
fun provideBusService(): BusService {
return Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(
TikXmlConverterFactory.create(
TikXml.Builder().exceptionOnUnreadXml(false).build()
)
).build().create(BusService::class.java)
}
}
그리고 Repository를 작성한다.
interface BusAPIRepository {
suspend fun getBusStationInfo(latitude: Double, longitude: Double): BusStationResponse
suspend fun getBusArrivalTime(stationId: String): BusArrivalResponse
suspend fun getBusName(routeId: Int) : BusInfoResponse
}
class BusAPIRepositoryImpl @Inject constructor(
private val busService: BusService
) : BusAPIRepository {
override suspend fun getBusStationInfo(
latitude: Double,
longitude: Double
): BusStationResponse {
return busService.getBusStationInfo(
"API 키",
latitude,
longitude
)
}
override suspend fun getBusArrivalTime(stationId: String): BusArrivalResponse {
return busService.getBusArrivalTime(
"API 키",
stationId
)
}
override suspend fun getBusName(routeId: Int): BusInfoResponse {
return busService.getBusName(
"API 키",
routeId
)
}
}
MapViewModel에서 위치 정보를 이용해 근방의 버스 정류소 데이터를 받아온다.
fun getBusStationInfo(latitude: Double, longitude: Double) {
viewModelScope.launch {
_busStationResult.value = busAPIRepository.getBusStationInfo(latitude, longitude)
}
}
MapActivity에서 ViewModel의 데이터를 받아 버스 정류소 마커를 생성한다.
mapViewModel.busStationResult.observe(this, androidx.lifecycle.Observer {
if (it.body?.items?.item?.isEmpty() == true) {
Toast.makeText(this, "근처에 버스 정류장이 없습니다.", Toast.LENGTH_SHORT).show()
} else {
for (i in it.body?.items?.item!!) {
setBusStopMarker(i.gpsLati!!, i.gpsLong!!, i.nodeNm!!, i.nodeId!!.substring(3))
}
}
})
private fun setBusStopMarker(
latitude: Double,
longitude: Double,
title: String,
stationID: String
) {
mapView.setMapCenterPoint(MapPoint.mapPointWithGeoCoord(latitude, longitude), true);
val marker = MapPOIItem()
marker.apply {
itemName = "$title / $stationID"
isShowCalloutBalloonOnTouch
mapPoint = MapPoint.mapPointWithGeoCoord(latitude, longitude)
markerType = MapPOIItem.MarkerType.CustomImage
selectedMarkerType = MapPOIItem.MarkerType.CustomImage
customImageResourceId = R.drawable.icon_bus
customSelectedImageResourceId = R.drawable.icon_bus
isCustomImageAutoscale = false
setCustomImageAnchor(0.5f, 1.0f)
}
mapView.addPOIItem(marker)
}
이렇게 하면 일단 카카오맵에 내가 검색한 위치 혹은 내 주변의 버스 정류소를 표시할 수 있게 된다.
다음으론 버스 정류소를 선택했을 시 버스 도착정보를 볼 수 있어야 한다.
여기서 귀찮은 상황이 발생한다.
버스 정류소 ID(stationID)를 이용해 경기도_버스도착정보 조회 API로 해당 정류소에 도착 예정인 버스의 목록을 가져올 수는 있다.
하지만 받아온 목록에 버스의 번호가 포함되어있지 않다.
버스의 번호 대신 해당 버스가 가진 고유 routeID를 가지기 때문에 이 routeID를 이용해 경기도_버스노선 조회 API에 다시 요청을 하여 버스 번호를 알아내는 작업이 필요하다.
그렇기 때문에 ViewModel에선 사용자가 버스 정류소를 선택했을 시 두 번의 API요청을 해야한다.
fun getBusArrivalTime(stationId: String) {
viewModelScope.launch {
_busArrivalTimeResult.value = busAPIRepository.getBusArrivalTime(stationId)
}
}
fun getBusName(routeId: Int) {
viewModelScope.launch {
_busName.value = busAPIRepository.getBusName(routeId)
}
}
Activity는 이렇게 작성했다.
// stationID를 통해 버스 도착 시간을 먼저 받아오고
busArrivalViewModel.getBusArrivalTime(stationID)
// 받아온 정보중 routeID를 이용해 버스 정보를 받아온다.
busArrivalViewModel.busArrivalTimeResult.observe(this, Observer {
if (it.body?.busArrivalList == null) {
Toast.makeText(this, "도착 예정 버스가 없습니다.", Toast.LENGTH_SHORT).show()
} else {
for (i in it.body?.busArrivalList!!) {
busArrivalViewModel.getBusName(i.routeId)
busList.add(
BusInfoEntity(
i.routeId.toString(),
i.predictTime1,
i.predictTime2
)
)
}
}
})
이렇게 버스 도착 시간과 버스 번호를 가져와 정보를 띄울 수 있었다.
다음은 FireBase를 통해 내가 원하는 버스를 즐겨찾기에 추가할 수 있는 방법을 포스팅해보겠다.