갤럭시 워치에 버스 도착 정보를 띄우자!(4) - 공공데이터 다루기

김흰돌·2023년 6월 21일
0
post-thumbnail

이번엔 카카오맵에 버스 정류장을 띄우고 그 버스 정류장의 고유 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를 통해 내가 원하는 버스를 즐겨찾기에 추가할 수 있는 방법을 포스팅해보겠다.

0개의 댓글