728x90

박상권님이 GitHub 에 오픈해 주신 https://github.com/ParkSangGwon/TedNaverMapClustering 

 

GitHub - ParkSangGwon/TedNaverMapClustering: 네이버지도용 클러스터 유틸리티 라이브러리

네이버지도용 클러스터 유틸리티 라이브러리. Contribute to ParkSangGwon/TedNaverMapClustering development by creating an account on GitHub.

github.com

Android 용 TedNaverMapClustering 파일을 받아서 구현한 네이버 지도에 Clustering 기능을 구현했다.

 

라이브러리 적용 전 코드와 적용 후 코드를 구분하여 적어둔다.

서버로부터 받아오는 데이터 구조가 아래와 같이 구현되어 있었다. (일부 변수는 삭제)

import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
 
@Parcelize
data class ItemData (
    val code: String,
    val bldNM: String// 빌딩명
    val address: String// 주소
    var latitude: Double, // 위도
    var longitude: Double, // 경도
): Parcelable

 

TedNaverMapClustering 적용 후 dataclass

import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
 
@Parcelize
data class ItemData (
    val code: String,
    val bldNM: String// 빌딩명
    val address: String// 주소
    var latitude: Double, // 위도
    var longitude: Double, // 경도
): Parcelable
 

 

ItemData data class 는 그대로 두고 별도로 ItemData 에서 필수 항목만 뽑아서 아래와 같이 코드를 작성한다.

import ted.gun0912.clustering.clustering.TedClusterItem
import ted.gun0912.clustering.geometry.TedLatLng

data class NaverItem(var position: LatLng) : TedClusterItem {
    override fun getTedLatLng(): TedLatLng {
        return TedLatLng(position.latitude, position.longitude)
    }
 
    var code: String= null
    var bldNM: String= null
    var chtype: Int = 0
    var Cust: Int = 0
 
    constructor(lat: Double, lng: Double) : this(LatLng(lat, lng)) {
        code = null
        bldNM = null
        chtype = 0
        Cust = 0
    }
 
    constructor(lat: Double, lng: Double, code: String?, bldNM: String?, chtype: Int, Cust: Int) : this(
        LatLng(lat, lng)
    ) {
        this.code = code
        this.bldNM = bldNM
        this.chtype = chtype
        this.Cust = Cust
    }
}

 

 

기존 marker 처리 코드

https://navermaps.github.io/android-map-sdk/guide-ko/5-1.html 부분에 "대량의 오버레이를 다룰 경우 객체를 생성하고 초기 옵션을 지정하는 작업은 백그라운드 쓰레드에서 수행하고 지도에 추가하는 작업만을 메인 쓰레드에서 수행하면 메인 쓰레드를 효율적으로 사용할 수 있습니다." 라고 나온다. 많은 marker 를 생성시에는 백그라운드 쓰레드에서 처리하도록 하고 있다.

var activeMarkers: MutableList<Marker>= null
private fun makeAllMarker(itemList: List<ItemData>) {
    val executor = Executors.newSingleThreadExecutor()
    val handler = Handler(Looper.getMainLooper())
    executor.execute {
        // 백그라운드 쓰레드
        activeMarkers = ArrayList()
        for (i in itemList.indices) {
            val itemData = itemList[i]
            val marker = Marker()
            marker.position = LatLng(itemData.latitude, itemData.longitude)
            marker.icon = OverlayImage.fromBitmap(makePinBitmap(itemData.Cust,itemData.chtype)!!)
            //marker.setIconTintColor(Color.RED); // 덧입힐 색상
            marker.width = Marker.SIZE_AUTO
            marker.height = Marker.SIZE_AUTO
            //marker.setCaptionMinZoom(12);
            //marker.setCaptionMaxZoom(16);
            marker.captionText = itemData.bldNM
            marker.isHideCollidedCaptions = true // 겹치는 캡션 자동 숨김 처리
            marker.isHideCollidedSymbols = true //  마커와 겹치는 지도 심벌을 자동으로 숨기도록 지정
            //marker.setHideCollidedMarkers(false);
            marker.tag = itemData
            activeMarkers?.add(marker)
            marker.onClickListener = Overlay.OnClickListener { overlay: Overlay ->
                mInfoWindow.open(marker)
                val position = LatLng(itemData.latitude, itemData.longitude) // 현재 위치
                val cameraUpdate = CameraUpdate.scrollTo(position).animate(CameraAnimation.Easing)
                mNaverMap!!.moveCamera(cameraUpdate)
                onClickMarker(overlay)
                true
            }
        }
        handler.post {
            // 메인 스레드
            for (marker in activeMarkers!!) {
                marker.map = mNaverMap
            }
        }
    }
}
 
private fun freeActiveMarkers() {
    if (activeMarkers == nullreturn
    for (marker in activeMarkers!!) {
        marker.map = null // 마커들 지도에서 삭제
    }
}
 

 

 

위 코드를 TedNaverMapClustering 코드로 변경한 코드이다.

import ted.gun0912.clustering.naver.TedNaverClustering

lateinit var tedNaverClustering: TedNaverClustering<NaverItem>
private fun makeClusterMarker(itemList: List<NaverItem>){
    tedNaverClustering = TedNaverClustering.with<NaverItem>(this, mNaverMap)
        .customMarker{ itemData ->
            Marker(itemData.position).apply {
                icon = OverlayImage.fromBitmap(makePinBitmap(itemData.Cust,itemData.chtype)!!)
                captionText = itemData.bldNM!!
                tag = itemData
            }
        }
        .markerClickListener { itemData ->
            val position = itemData.position
            val cameraUpdate = CameraUpdate.scrollTo(position).animate(CameraAnimation.Easing)
            mNaverMap!!.moveCamera(cameraUpdate)
            onClickClusterMarker(itemData.code)
        }
        .minClusterSize(20)
        .make()
 
    val executor = Executors.newSingleThreadExecutor()
    val handler = Handler(Looper.getMainLooper())
    executor.execute {
        // 백그라운드 스레드
        tedNaverClustering.addItems(itemList)
        Log.e("TAG""Number of tedNaverClustering :" + itemList.size)
    } // end executor.execute
}
 

 

fun onClickClusterMarker(code: String?){
    // 호출해야 할 데이터가 다른 ArrayList 에서 code 값이 서로 일치하는 데이터만 추출해야 한다.
    val itemData: ItemData = itemDataList.filter {it.code.equals(code)}.single()
 
}

 

서버에서 가져온 데이터 List 에 추가하는 코드 중에서 핵심만 발췌

response.body()?.let {
    if (it.status.contains("success")) {
        itemDataList.clear() // 서버에서 가져온 데이터 초기화
        naverItemList.clear()
        val itemData: List<ItemData> = it.iteminfo
        Log.e("TAG""Number of itemData :" + itemData.size)
        for (item in itemData) {
            // 자동으로 타입 변환을 하는 듯함.
            itemDataList.add(item)
            putNaverItem(item.latitude,item.longitude,item.code,item.bldNM,item.chtype,item.Cust)
        }
        showMapMarker(itemDataList)
    } else {
        Utils.showAlert(context, it.status, it.message)
    }
 
fun putNaverItem(lat: Double, lng: Double, code: String, bldNM: String, chtype: Int, Cust: Int){
    // NaverItem 생성자에 데이터 추가 및 naverItemList 에 추가
    var naverItem = NaverItem(lat,lngitude,code,bldNM,chtype,Cust)
    naverItemList.add(naverItem)
}

 

코드 구현 이슈

Thread Pool size 를 초과하는 데이터 크기(서버로부터의 Input 데이터 개수)는 좀 더 신경써야 할 거 같다.

 

 

 

블로그 이미지

Link2Me

,