polyline에 맞는 카메라 위치 잡기(feat. 공식 문서를 차분히, 천천히 봐야하는 이유)

Bettor·2024년 6월 16일
0

Android

목록 보기
3/3

지도 위에 폴리라인을 그리는데, 현재 위치를 불러오고 있지 않습니다. 카메라 위치를 잡아야 폴리라인을 볼 수 있기 때문에 폴리라인에 맞는 카메라 위치를 잡는 메서드(로직)이 필요했습니다.

private fun setCameraOnPolyLine() {
        /** 위도 경도의 최대값과 최소값을 통해 중심점을 찾아 카메라포지션을 잡기
         * '위치 정보를 불러왔을때' 조건을 추가해주면 되겠다. */

        if (locationList.isNotEmpty()) {
            var latMin = locationList[0].latitude
            var latMax = locationList[0].latitude
            var lngMin = locationList[0].longitude
            var lngMax = locationList[0].longitude

            for (latLng in locationList) {
                if (latLng.latitude < latMin) latMin = latLng.latitude
                if (latLng.latitude > latMax) latMax = latLng.latitude
                if (latLng.longitude < lngMin) lngMin = latLng.longitude
                if (latLng.longitude > lngMax) lngMax = latLng.longitude
            }

            val distanceDiff = DistanceCalculator.getDistance(latMin, latMax, lngMin, lngMax)
            // maxDiff를 기준으로 줌 레벨 조정
            Log.d("distanceDiff", "$distanceDiff")
            val zoomLevel = when {
                distanceDiff > 2.5 -> 10.0
                distanceDiff > 1.5 -> 12.5
                distanceDiff > 0.5 -> 15.0
                else -> 7.5 /** 500부터 2500으로 했었는데 거꾸로 바꿔주니까 when문을 잘 탄다!
                다만 거리마다 적합한 줌 배율을 정해야 하는데 이건 테스트가 필요하다 */
            }

            val centerLat = (latMin + latMax) / 2
            val centerLng = (lngMin + lngMax) / 2
            val center = LatLng(centerLat, centerLng)

            cameraPosition = CameraPosition(center, zoomLevel)
            Log.d("cameraPosition", "$zoomLevel")
            cameraUpdate = CameraUpdate.toCameraPosition(cameraPosition)
            naverMap.moveCamera(cameraUpdate)
            Log.d("setCameraOnPolyLine", "Camera moved to $center")
        } else {
            Log.d("setCameraOnPolyLine", "Location list is empty, cannot set camera")
        }
    }
  1. 폴리라인이 그려진 위치에 적합한 배율로 폴리라인을 보여주기 위함입니다.

  2. private lateinit var locationList: List<LatLng>

    locationList는 latlng을 가지고 있습니다.

  3. 초기 경계값 설정: 첫 번째 위치의 위도와 경도로 latMin, latMax, lngMin, lngMax 값을 초기화합니다.

  4. locationList에 있는 모든 위치를 순회하면서 최소 및 최대 위도와 경도를 갱신합니다.

  5. latMin, latMax, lngMin, lngMax 값을 이용하여 거리 차이를 계산합니다.

  6. 계산된 거리 차이에 따라 줌 레벨을 결정합니다.

  7. 최소 및 최대 위도와 경도를 이용하여 중앙 위치를 계산합니다.

  8. 중앙 위치와 줌 레벨을 사용하여 카메라 위치를 설정하고, 네이버 지도에서 해당 위치로 카메라를 이동시킵니다.

  9. locationList가 비어 있을 경우, 로그 메시지를 출력하고 카메라 설정을 하지 않습니다.

마지막으로 작업했던 나의 로직이였다. 그런데 송두리째 바뀌었다.

private fun setCameraOnPolyLine() {
        if (locationList.isNotEmpty()) {
            val boundsBuilder = LatLngBounds.Builder()
            for (latLng in locationList) {
                boundsBuilder.include(latLng)
            }
            val bounds = boundsBuilder.build()

            val padding = 100

            val cameraUpdate = CameraUpdate.fitBounds(bounds, padding)
            naverMap.moveCamera(cameraUpdate)
            Log.d("setCameraOnPolyLine", "Camera moved to fit bounds")
        } else {
            Log.d("setCameraOnPolyLine", "Location list is empty, cannot set camera")
        }
    }
  1. 초기 경계값을 정하는 변수, 최소 및 최대 위도 경도를 찾는 변수, 거리 차이를 계산하는 변수, 거리 차이에 따른 줌 레벨을 결정하는 변수, 중앙 위치를 계산하는 변수, 카메라를 위치하는 변수까지 전부 다 바뀌었다.

  2. LatLngBounds.Builder() : 네이버 맵에서 지원하고 있다.

    1. include() : LatLng 객체를 인수로 받아들이며, latLng 객체를 coords 리스트에 추가합니다.

      @NonNull
              public Builder include(@Nullable LatLng latLng) {
                  if (latLng != null && latLng.isValid()) {
                      this.coords.add(latLng);
                  }
      
                  return this;
              }
    2. build() : 필요한 좌표 데이터가 부족할 경우 IllegalStateException을 던져 예외 상황을 처리합니다. 이는 사용자가 include() 메서드를 통해 적절히 좌표를 추가했는지 확인하는 기능을 합니다.

      @NonNull
              public LatLngBounds build() {
                  LatLngBounds result = this.buildOrNull();
                  if (result == null) {
                      throw new IllegalStateException("coordinates are empty; call include() first");
                  } else {
                      return result;
                  }
              }
  3. fitBounds : 지도 카메라를 주어진 지도의 특정 영역(LatLngBounds)에 맞게 업데이트하는 데 사용됩니다. 또한, 패딩 값을 이용해 지도 가장자리와 경계 사이에 여유 공간을 설정

    @NonNull
        public static CameraUpdate fitBounds(@NonNull LatLngBounds bounds, @Px int padding) {
            return fitBounds(bounds, padding, padding, padding, padding);
        }

결론 :

  1. LatLngBounds는 지도의 특정 영역을 보여준다. class가 따로 있던데 엄청 방대하다. 그냥 네이버 지도에서 특정 영역을 보여주는 기능이라고 생각하면 되겠다. 그러다 보니 초기 경계, 최소 및 최대 위도 경도, 거리 차이에 대한 고민을 할 필요가 없어졌다.
  2. 그래서 include로 위치 정보가 추가될때 add 되고, build를 통해 필요한 좌표 데이터가 부족할 경우 include() 메서드를 통해 적절히 좌표를 추가했는지 확인하는 예외 상황을 처리합니다.
  3. fitBounds가 지도 카메라를 지도 특정 영역을 업데이트하기 때문에 줌 레벨, 중앙 위치(CameraPosition)를 고민할 필요가 없어졌다.
  4. padding 값도 추가 해주고 특정 영역에 맞게 카메라를 업데이트하여 지도에 moveCamera로 적용해 줍니다.
val cameraUpdate = CameraUpdate.fitBounds(bounds, padding)
naverMap.moveCamera(cameraUpdate)

최근에 런타임 권한 요청에 대해 찾아보면서도 느꼈지만 공식 문서를 잘 봐야겠다고 생각했다. 잘 봐야겠다고 잘 봐지는건 아니겠지만 일이 잘 안 풀릴때 환기하는 느낌으로 보면 접근이 훨씬 쉬워질 것 같다. 공식 문서 혹은 api 코드를 열심히 뜯어봐야 이상적인 코딩을 할 수 있겠구나!

0개의 댓글