네이버 지도 다중 마커 (100개 이상)

조회수 5226회

네이버 지도를 이용하여서 앱을 제작하는 중인데 마커의 개수가 적을때는 정상작동 하는데 마커가 100개가 넘어가면 마커를 띄우는 시간이 오래 걸려지고 500개가 넘어가면 앱이 멈춰버리는 현상이 발생합니다 ㅠㅠ 혹시 해결 방법이 있을까요..??

  • (•́ ✖ •̀)
    알 수 없는 사용자

2 답변

  • 네이버 API를 이용하는 유저들 사이에서 흔히 일어나는 문제같네요.

    같은 이유로 어떤분이 네이버 개발자포럼에 문의해 네이버측에 해결방법을 답변 받은 사례가 있습니다.
    요약하면 사용자가 보고있는 영역에만 동적으로 마커를 그려라라는것인데, 자세한 사항은 링크를 참조하시면 될듯합니다.

    • 좋은답변 감사합니다! 알 수 없는 사용자 2020.3.9 09:38
    • 예제가 안드로이드에 관한게 없네요 ㅠㅠ 알 수 없는 사용자 2020.3.9 09:59
    • 예제는 자바스크립트지만, 같은 맥락으로 안드로이드에서도 가상으로 영역을 잡고 사용자가 보고있는 영역에만 마커를 그리도록 처리하면 될듯합니다. 예제코드를 작성해드리고 싶은데 지금 컴퓨터에 안드로이드 개발환경이 구축이 안돼있네요. 이따가 한번 예제코드를 작성해보겠습니다 ㅎㅎ CUSO 2020.3.9 10:06
    • 너무 감사합니다!!! 알 수 없는 사용자 2020.3.9 10:09
    • 예제 코드는 이 글에 답변 으로 작성해 주실수 있나요 혹시??? ㅎㅎ 알 수 없는 사용자 2020.3.9 13:53
  • 느낌만 대충 이해하시라는 의도로 최대한 간결하게 작성하였습니다.
    1킬로미터 간격으로 가상 마커 4만개를찍고 보고있는 위치 반경 3킬로미터 이내만 마커를 동적으로 표시하는 예제입니다.

    현재 네이버가 권장하고있는 네이버 클라우드 플랫폼 API를 기준으로 작성하였습니다.

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <fragment android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/map"
            android:name="com.naver.maps.map.MapFragment" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    MainActivity.java

    package com.learn.navercloudapis;
    
    import android.os.Bundle;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.UiThread;
    import androidx.fragment.app.FragmentActivity;
    import androidx.fragment.app.FragmentManager;
    
    import com.naver.maps.geometry.LatLng;
    import com.naver.maps.map.CameraPosition;
    import com.naver.maps.map.CameraUpdate;
    import com.naver.maps.map.MapFragment;
    import com.naver.maps.map.NaverMap;
    import com.naver.maps.map.OnMapReadyCallback;
    import com.naver.maps.map.overlay.Marker;
    
    import java.util.Vector;
    
    public class MainActivity extends FragmentActivity implements OnMapReadyCallback {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_main);
    
            FragmentManager fm = getSupportFragmentManager();
            MapFragment mapFragment = (MapFragment)fm.findFragmentById(R.id.map);
            if (mapFragment == null) {
                mapFragment = MapFragment.newInstance();
                fm.beginTransaction().add(R.id.map, mapFragment).commit();
            }
    
            mapFragment.getMapAsync(this);
        }
    
        @UiThread
        @Override
        public void onMapReady(@NonNull final NaverMap naverMap) {
            // 카메라 초기 위치 설정
            LatLng initialPosition = new LatLng(37.506855, 127.066242);
            CameraUpdate cameraUpdate = CameraUpdate.scrollTo(initialPosition);
            naverMap.moveCamera(cameraUpdate);
    
            // 마커들 위치 정의 (대충 1km 간격 동서남북 방향으로 만개씩, 총 4만개)
            markersPosition = new Vector<LatLng>();
            for (int x = 0; x < 100; ++x) {
                for (int y = 0; y < 100; ++y) {
                    markersPosition.add(new LatLng(
                            initialPosition.latitude - (REFERANCE_LAT * x),
                            initialPosition.longitude + (REFERANCE_LNG * y)
                    ));
                    markersPosition.add(new LatLng(
                            initialPosition.latitude + (REFERANCE_LAT * x),
                            initialPosition.longitude - (REFERANCE_LNG * y)
                    ));
                    markersPosition.add(new LatLng(
                            initialPosition.latitude + (REFERANCE_LAT * x),
                            initialPosition.longitude + (REFERANCE_LNG * y)
                    ));
                    markersPosition.add(new LatLng(
                            initialPosition.latitude - (REFERANCE_LAT * x),
                            initialPosition.longitude - (REFERANCE_LNG * y)
                    ));
                }
            }
    
            // 카메라 이동 되면 호출 되는 이벤트
            naverMap.addOnCameraChangeListener(new NaverMap.OnCameraChangeListener() {
                @Override
                public void onCameraChange(int reason, boolean animated) {
                    freeActiveMarkers();
                    // 정의된 마커위치들중 가시거리 내에있는것들만 마커 생성
                    LatLng currentPosition = getCurrentPosition(naverMap);
                    for (LatLng markerPosition: markersPosition) {
                        if (!withinSightMarker(currentPosition, markerPosition))
                            continue;
                        Marker marker = new Marker();
                        marker.setPosition(markerPosition);
                        marker.setMap(naverMap);
                        activeMarkers.add(marker);
                    }
                }
            });
        }
    
        // 마커 정보 저장시킬 변수들 선언
        private Vector<LatLng> markersPosition;
        private Vector<Marker> activeMarkers;
    
        // 현재 카메라가 보고있는 위치
        public LatLng getCurrentPosition(NaverMap naverMap) {
            CameraPosition cameraPosition = naverMap.getCameraPosition();
            return new LatLng(cameraPosition.target.latitude, cameraPosition.target.longitude);
        }
    
        // 선택한 마커의 위치가 가시거리(카메라가 보고있는 위치 반경 3km 내)에 있는지 확인
        public final static double REFERANCE_LAT = 1 / 109.958489129649955;
        public final static double REFERANCE_LNG = 1 / 88.74;
        public final static double REFERANCE_LAT_X3 = 3 / 109.958489129649955;
        public final static double REFERANCE_LNG_X3 = 3 / 88.74;
        public boolean withinSightMarker(LatLng currentPosition, LatLng markerPosition) {
            boolean withinSightMarkerLat = Math.abs(currentPosition.latitude - markerPosition.latitude) <= REFERANCE_LAT_X3;
            boolean withinSightMarkerLng = Math.abs(currentPosition.longitude - markerPosition.longitude) <= REFERANCE_LNG_X3;
            return withinSightMarkerLat && withinSightMarkerLng;
        }
    
        // 지도상에 표시되고있는 마커들 지도에서 삭제
        private void freeActiveMarkers() {
            if (activeMarkers == null) {
                activeMarkers = new Vector<Marker>();
                return;
            }
            for (Marker activeMarker: activeMarkers) {
                activeMarker.setMap(null);
            }
            activeMarkers = new Vector<Marker>();
        }
    
    }
    

    갤럭시 노트10 기준으로 크게 버벅임은 없는것으로 확인였습니다.

    추가로, 코드를 잘 읽어보시면 아시겠지만 카메라 이동 이벤트가 발생할때마다 가시거리 내 마커를 찾기위해 루프를 4만번씩 돕니다.
    특정 알고리즘을 이용하는등의 최적화작업을 통해 루프도는 횟수를 줄일 필요가 있습니다.
    이정도는 질문자님도 충분히 해결 가능할것이라고 생각되어 일부로 남겨두었습니다.

    또한 제가 평소에 Java를 많이 쓰는 편이 아니라 코드가 일부 Convention에 안맞을 수도 있습니다.
    이부분은 너그러이 양해부탁드리겠습니다.

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)