박상권님이 GitHub 에 오픈해 주신 https://github.com/ParkSangGwon/TedNaverMapClustering
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 == null) return
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 데이터 개수)는 좀 더 신경써야 할 거 같다.
'안드로이드 > Naver API' 카테고리의 다른 글
Android Naver Map 레벨 변경 및 위치 이동 (0) | 2023.05.18 |
---|---|
현재 위치반경 범위 데이터 가져오는 SQL 쿼리 (0) | 2022.02.24 |
[코틀린] kakao navi 해시 키 등록방법 (0) | 2021.04.11 |
[코틀린] GroupList 가져오기 (0) | 2020.08.02 |
구글 위치 API 로 현재 위치 좌표 얻기 (0) | 2020.07.18 |