티스토리 뷰

반응형

서울 열린 데이터 광장 API 발급받기

 

https://data.seoul.go.kr/ 에 접속을 하고 회원가입을 합니다.

 

화장실을 검색을 하고

 

서울시 공중화장실 위치정보를 선택 후에

 

인증키 신청을 눌러주고 인증키를 신청합니다.

 

 

권한 요청 설정

 

AndroidManifest에 아래의 코드를 작성해줍니다.

 

<uses-permission android:name="android.permission.INTERNET"/>

<application
        android:usesCleartextTraffic="true"

 

인터넷 사용 권한을 요청하고 usesCleartextTraffic를 true로 설정해줍니다.

 

usesCleartextTraffic은 예전에는 기본 값이 true였지만 파이부터 기본 값이 false로 변경되어 true로 설정해줘야 됩니다.

usesCleartextTraffic은 일반 HTTP 통신을 허용할지 안 할지를 고르는 것입니다. 따라서 false로 설정 시에 HTTPS 통신만 가능합니다.

 

 

AsyncTask를 사용하여 API 연동하기

 

먼저 Vecter Asset을 추가해줍니다.

 

 

 

 

그리고 MainActivity에 아래 코드를 작성해줍니다.

 

 

 

발급받은 API 키

val API_KEY = "api 키"

 

UI가 종료되었을 때 백그라운드 작업을 종료하기 위해서 선언한 변수

var task: ToiletReadTask? = null

 

서울시 화장실 데이터를 전부 저장할 변수

var toilets = JSONArray()

 

화장실 위치 이미지로 사용할 비트맵 선언 코드

val bitmap by lazy {
    val bitmap = ResourcesCompat.getDrawable(resources, R.drawable.ic_person_black_24dp, null)?.toBitmap()
    Bitmap.createScaledBitmap(bitmap!!, 64, 64, false)
}

 

JSONArray의 객체 병합을 쉽게 하도록 도와주는 확장 합수

fun JSONArray.merge(anotherArray: JSONArray) {
    for (i in 0 until anotherArray.length()) {
        this.put(anotherArray.get(i))
    }
}

 

API에서 데이터를 읽어오는 함수

fun readData(startIndex: Int, lastIndex: Int): JSONObject {
    val url = URL("http://openAPI.seoul.go.kr:8088" + "/${API_KEY}/json/SearchPublicToiletPOIService/${startIndex}/${lastIndex}")
    val connection = url.openConnection()
    val data = connection.getInputStream().readBytes().toString(charset("UTF-8"))
    return JSONObject(data)
}

화장실 개수는 엄청 많지만 한 번에 읽어 올 수 있는 개수는 1000개가 최대여서  재사용을 위해 함수를 분리하였습니다.

화장실 정보를 읽어와 JSONObject로 반환해줍니다.

 

 

AsyncTask 클래스

 inner class ToiletReadTask : AsyncTask<Void, JSONArray, String>() {

    override fun onPreExecute() {

        googleMap?.clear()
        toilets = JSONArray()
    }

    override fun doInBackground(vararg params: Void?): String {

        val step = 1000
        var startIndex = 1
        var lastIndex = step
        var totalCount = 0

        do {
            if (isCancelled) break

            if (totalCount != 0) {
                startIndex += step
                lastIndex += step
            }

            val jsonObject = readData(startIndex, lastIndex)

            totalCount = jsonObject.getJSONObject("SearchPublicToiletPOIService").getInt("list_total_count")

            val rows = jsonObject.getJSONObject("SearchPublicToiletPOIService").getJSONArray("row")

            toilets.merge(rows)

            publishProgress(rows)

        } while (lastIndex < totalCount)

        return "complete"
    }

    override fun onProgressUpdate(vararg values: JSONArray?) {

        val array = values[0]
        array?.let {
            for (i in 0 until array.length()) {
                addMarkers(array.getJSONObject(i))
            }
        }
    }
}

 

onPreExecute에서 기존 데이터를 초기화해줍니다.

 

 

val step = 1000
var startIndex = 1
var lastIndex = step
var totalCount = 0

doInBackground에서는 데이터를 최대 1000개씩 가져올 수 있기 때문에 여러분 호출하기 위해 위와 같이 선언을 했습니다.

 

 

val jsonObject = readData(startIndex, lastIndex)

totalCount = jsonObject.getJSONObject("SearchPublicToiletPOIService").getInt("list_total_count")

val rows = jsonObject.getJSONObject("SearchPublicToiletPOIService").getJSONArray("row")

toilets.merge(rows)
publishProgress(rows)

startIndex와 lastIndex로 데이터를 조회하고 totalCount와 데이터 집합을 가져옵니다.

그리고 merge로 병합하고 UI 업데이트를 위해 progress를 발행합니다.

 

 

 

val array = values[0]

array?.let {
    for (i in 0 until array.length()) {
        addMarkers(array.getJSONObject(i))
    }

onProgressUpdate는 데이터를 읽어오는 중간중간 실행을 해줍니다.

따라서 onProgressUpdate를 사용하여 마커를 추가해줍니다.

 

onProgressUpdate에서 쓰인 vararg은 개수를 늘릴 수 있는 가변 파라미터입니다.

 

 

 

onStart와 onStop 함수

override fun onStart() {
    super.onStart()
    task?.cancel(true)
    task = ToiletReadTask()
    task?.execute()
}

override fun onStop() {
    super.onStop()
    task?.cancel(true)
    task = null
}

 

 

 

마커를 추가하는 함수

fun addMarkers(toilet: JSONObject) {
    googleMap?.addMarker(
        MarkerOptions()
            .position(LatLng(toilet.getDouble("Y_WGS84"), toilet.getDouble("X_WGS84")))
            .title(toilet.getString("FNAME"))
            .snippet(toilet.getString("ANAME"))
            .icon(BitmapDescriptorFactory.fromBitmap(bitmap))
    )
}

 

 

 

이제 실행을 하게 되면 지도에 화장실이 표시됩니다.

 

예제 코드

https://github.com/Im-Tae/Blog_Example/tree/master/Android/Google_Map_Example

반응형
댓글