Location

An app using maps usually tracks a user’s location and displays it on the map. The NAVER Maps SDK helps you easily implement such features by providing the location overlay and location tracking feature. You can also implement your own location based features, without using the embedded location tracking feature.

Location overlay

It is recommended to use a location overlay when you need to display a user’s location on the map. Only one location overlay exists on a map, and it supports location specific characteristics, such as coordinates and directions. For more information, refer to Location overlay.

Embedded location tracking feature

The NAVER Maps SDK supports the location tracking feature which receives a user location event, displays the location on the map and moves the camera.

Permissions and LocationSource

Since the NAVER Maps SDK basically does not use user’s location information, it does not request location-related permissions from users. Therefore, for apps using the location tracking feature, you should specify ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission in AndroidManifest.xml.

The following code example specifies the ACCESS_FINE_LOCATION permission in AndroidManifext.xml.

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

In addition, you should call setLocationSource() to set a LocationSource implementation. LocationSource is an interface that provides location to the NAVER Maps SDK. As the LocationSource’s methods including activate() and deactivate() are called by the map object, do not directly call it.

FusedLocationSource

The NAVER Maps SDK provides FusedLocationSource, an implementation that returns the best location by using FusedLocationProviderClient of Google Play Service, terrestrial magnetism and acceleration sensors. To use FusedLocationSource, you should add dependencies for play-services-location to build.gradle of your app module.

dependencies {
    implementation 'com.google.android.gms:play-services-location:21.0.1'
}

Groovy

dependencies {
    implementation 'com.google.android.gms:play-services-location:21.0.1'
}

Kotlin

dependencies {
    implementation("com.google.android.gms:play-services-location:21.0.1")
}

FusedLocationSource requires an activity or fragment to process permissions in runtime. You should pass an activity or fragment object to the constructor and specify a permission request code. And then, you should pass the result of onRequestPermissionResult() to onRequestPermissionsResult() of FusedLocationSource.

The following code example creates a FusedLocationSource in an activity and set it to NaverMap.

public class LocationTrackingActivity extends AppCompatActivity
    implements OnMapReadyCallback {
    private static final int LOCATION_PERMISSION_REQUEST_CODE = 1000;
    private FusedLocationSource locationSource;
    private NaverMap naverMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // ...
        locationSource =
            new FusedLocationSource(this, LOCATION_PERMISSION_REQUEST_CODE);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
        @NonNull String[] permissions,  @NonNull int[] grantResults) {
        if (locationSource.onRequestPermissionsResult(
            requestCode, permissions, grantResults)) {
            if (!locationSource.isActivated()) { // Permission denied
                naverMap.setLocationTrackingMode(LocationTrackingMode.None);
            }
            return;
        }
        super.onRequestPermissionsResult(
            requestCode, permissions, grantResults);
    }

    @Override
    public void onMapReady(@NonNull NaverMap naverMap) {
        this.naverMap = naverMap;
        naverMap.setLocationSource(locationSource);
    }
}

Java

public class LocationTrackingActivity extends AppCompatActivity
    implements OnMapReadyCallback {
    private static final int LOCATION_PERMISSION_REQUEST_CODE = 1000;
    private FusedLocationSource locationSource;
    private NaverMap naverMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // ...
        locationSource =
            new FusedLocationSource(this, LOCATION_PERMISSION_REQUEST_CODE);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
        @NonNull String[] permissions,  @NonNull int[] grantResults) {
        if (locationSource.onRequestPermissionsResult(
            requestCode, permissions, grantResults)) {
            if (!locationSource.isActivated()) { // Permission denied
                naverMap.setLocationTrackingMode(LocationTrackingMode.None);
            }
            return;
        }
        super.onRequestPermissionsResult(
            requestCode, permissions, grantResults);
    }

    @Override
    public void onMapReady(@NonNull NaverMap naverMap) {
        this.naverMap = naverMap;
        naverMap.setLocationSource(locationSource);
    }
}

Kotlin

class LocationTrackingActivity : AppCompatActivity(), OnMapReadyCallback {
    private lateinit var locationSource: FusedLocationSource
    private lateinit var naverMap: NaverMap

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        locationSource =
               FusedLocationSource(this, LOCATION_PERMISSION_REQUEST_CODE)
    }

    override fun onRequestPermissionsResult(requestCode: Int,
                                            permissions: Array<String>,
                                            grantResults: IntArray) {
        if (locationSource.onRequestPermissionsResult(requestCode, permissions,
                grantResults)) {
            if (!locationSource.isActivated) { // Permission denied
                naverMap.locationTrackingMode = LocationTrackingMode.None
            }
            return
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }

    override fun onMapReady(naverMap: NaverMap) {
        this.naverMap = naverMap
        naverMap.locationSource = locationSource
    }

    companion object {
        private const val LOCATION_PERMISSION_REQUEST_CODE = 1000
    }
}

Location tracking mode

You can set LocationSource to NaverMap to use the location tracking feature. There are two ways to use the location tracking feature:

  • Enable location tracking mode: Call setLocationTrackingMode() to programmatically enable the location tracking mode.
  • Enable current location button control: Use UiSettings.setLocationButtonEnabled(true) to enable the current location button control, and the location tracking mode is enabled or disabled by a user’s click on the button.

The location tracking mode is subdivided into the following four modes, which are defined in the LocationTrackingMode enum.

  • None: Location is not tracked.
  • NoFollow: Location tracking is enabled, and the current location overlay moves following a user’s location. However, the map does not move.

NoFollow mode enabled

  • Follow: Location tracking is enabled, and the current location overlay and the camera’s coordinates move following a user’s location. If the camera moves according to an API call or a user’s gestures, the mode turns into NoFollow.

Follow mode enabled

  • Face: Location tracking is enabled, and the current location overlay, the camera’s coordinates and bearing move following a user’s location and direction. If the camera moves according to an API call or a user’s gestures, the mode turns into NoFollow.

Face mode enabled

The following code example sets the location tracking mode to Follow by calling setLocationTrackingMode().

naverMap.setLocationTrackingMode(LocationTrackingMode.Follow);

Java

naverMap.setLocationTrackingMode(LocationTrackingMode.Follow);

Kotlin

naverMap.locationTrackingMode = LocationTrackingMode.Follow

Location change event

Add OnLocationChangeListener with the addOnLocationChangeListener() method to receive an event for location changes. If the location tracking mode is enabled and a user’s location changes, the onLocationChange() callback method is called with the user’s location passed as a parameter.

The following code example displays a user’s location coordinates as a toast message when the location changes.

naverMap.addOnLocationChangeListener(location ->
    Toast.makeText(this,
        location.getLatitude() + ", " + location.getLongitude(),
        Toast.LENGTH_SHORT).show());

Java

naverMap.addOnLocationChangeListener(location ->
    Toast.makeText(this,
        location.getLatitude() + ", " + location.getLongitude(),
        Toast.LENGTH_SHORT).show());

Kotlin

naverMap.addOnLocationChangeListener { location ->
    Toast.makeText(this, "${location.latitude}, ${location.longitude}",
             Toast.LENGTH_SHORT).show()
}

Custom implementation

If it is hard to use the LocationSource or the location tracking feature supported by the NAVER Maps SDK, you can implement your own location tracking feature.

Custom location source

If you use the embedded location tracking mode, it is recommended to use FusedLocationSource which supports sensors and the well-designed permission handling feature. If needed, however, you can implement LocationSource on your own.

Methods of LocationSource are called by the NAVER Maps SDK. The activate() method is called if the location tracking mode is enabled, and the deactivate() method is called if disabled. Therefore, the implementation of LocationSource should start tracking a user’s location when the activate() is called, and stop tracking it when the deactivate() is called. In addition, while the location tracking is enabled, the LocationSource.OnLocationChangedListener object’s onLocationChanged() passed to the activate() should be called whenever the user’s location changes.

LocationSource is also responsible for handling location permissions. When the activate() method is called, LocationSource should check and request permissions.

The following code example shows an implementation of LocationSource that receives GPS locations by using LocationManager.

public class GpsOnlyLocationSource implements LocationSource, LocationListener {
    @NonNull
    private final Context context;
    @Nullable
    private final LocationManager locationManager;
    @Nullable
    private LocationSource.OnLocationChangedListener listener;

    public GpsOnlyLocationSource(@NonNull Context context) {
        this.context = context;
        locationManager =
            (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
    }

    @Override
    public void activate(
        @NonNull LocationSource.OnLocationChangedListener listener) {
        if (locationManager == null) {
            return;
        }

        if (PermissionChecker.checkSelfPermission(context,
            Manifest.permission.ACCESS_FINE_LOCATION)
            != PackageManager.PERMISSION_GRANTED
            && PermissionChecker.checkSelfPermission(context,
            Manifest.permission.ACCESS_COARSE_LOCATION)
            != PackageManager.PERMISSION_GRANTED) {
            // Permission request logic omitted.
            return;
        }

        this.listener = listener;
        locationManager.requestLocationUpdates(
            LocationManager.GPS_PROVIDER, 1000, 10, this);
    }

    @Override
    public void deactivate() {
        if (locationManager == null) {
            return;
        }

        listener = null;
        locationManager.removeUpdates(this);
    }

    @Override
    public void onLocationChanged(Location location) {
        if (listener != null) {
            listener.onLocationChanged(location);
        }
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }

    @Override
    public void onProviderEnabled(String provider) {
    }

    @Override
    public void onProviderDisabled(String provider) {
    }
}

Java

public class GpsOnlyLocationSource implements LocationSource, LocationListener {
    @NonNull
    private final Context context;
    @Nullable
    private final LocationManager locationManager;
    @Nullable
    private LocationSource.OnLocationChangedListener listener;

    public GpsOnlyLocationSource(@NonNull Context context) {
        this.context = context;
        locationManager =
            (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
    }

    @Override
    public void activate(
        @NonNull LocationSource.OnLocationChangedListener listener) {
        if (locationManager == null) {
            return;
        }

        if (PermissionChecker.checkSelfPermission(context,
            Manifest.permission.ACCESS_FINE_LOCATION)
            != PackageManager.PERMISSION_GRANTED
            && PermissionChecker.checkSelfPermission(context,
            Manifest.permission.ACCESS_COARSE_LOCATION)
            != PackageManager.PERMISSION_GRANTED) {
            // Permission request logic omitted.
            return;
        }

        this.listener = listener;
        locationManager.requestLocationUpdates(
            LocationManager.GPS_PROVIDER, 1000, 10, this);
    }

    @Override
    public void deactivate() {
        if (locationManager == null) {
            return;
        }

        listener = null;
        locationManager.removeUpdates(this);
    }

    @Override
    public void onLocationChanged(Location location) {
        if (listener != null) {
            listener.onLocationChanged(location);
        }
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }

    @Override
    public void onProviderEnabled(String provider) {
    }

    @Override
    public void onProviderDisabled(String provider) {
    }
}

Kotlin

class GpsOnlyLocationSource(
        private val context: Context) : LocationSource, LocationListener {
    private val locationManager = context
            .getSystemService(Context.LOCATION_SERVICE) as LocationManager?
    private var listener: LocationSource.OnLocationChangedListener? = null

    override fun activate(listener: LocationSource.OnLocationChangedListener) {
        if (locationManager == null) {
            return
        }

        if (PermissionChecker.checkSelfPermission(context,
                        Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED
                && PermissionChecker.checkSelfPermission(context,
                        Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            // Permission request logic omitted.
            return
        }

        this.listener = listener
        locationManager.requestLocationUpdates(
                LocationManager.GPS_PROVIDER, 1000, 10f, this)
    }

    override fun deactivate() {
        if (locationManager == null) {
            return
        }

        listener = null
        locationManager.removeUpdates(this)
    }

    override fun onLocationChanged(location: Location) {
        listener?.onLocationChanged(location)
    }

    override fun onStatusChanged(provider: String, status: Int,
                                 extras: Bundle) {
    }

    override fun onProviderEnabled(provider: String) {
    }

    override fun onProviderDisabled(provider: String) {
    }
}

Custom location tracking

You do not need to use the embedded location tracking feature when displaying a user’s location on the map. You can customize the feature as you need, by specifying the LocationOverlay’s position and bearing to move the camera after directly receiving location events in your app.

The following code example receives GPS locations by using LocationManager in an activity and moves the LocationOverlay and the camera.

public class CustomLocationTrackingActivity extends AppCompatActivity
    implements LocationListener {
    private static final int PERMISSION_REQUEST_CODE = 100;
    private static final String[] PERMISSIONS = {
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    };

    private NaverMap map;
    @Nullable
    private LocationManager locationManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // ...

        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(naverMap -> map = naverMap);

        locationManager = (LocationManager)getSystemService(LOCATION_SERVICE);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
        @NonNull String[] permissions, @NonNull int[] grantResults) {

        if (requestCode == PERMISSION_REQUEST_CODE) {
            if (hasPermission() && locationManager != null) {
                locationManager.requestLocationUpdates(
                    LocationManager.GPS_PROVIDER, 1000, 10, this);
            }
            return;
        }

        super.onRequestPermissionsResult(
            requestCode, permissions, grantResults);
    }

    @Override
    protected void onStart() {
        super.onStart();

        if (hasPermission()) {
            if (locationManager != null) {
                locationManager.requestLocationUpdates(
                    LocationManager.GPS_PROVIDER, 1000, 10, this);
            }
        } else {
            ActivityCompat.requestPermissions(
                this, PERMISSIONS, PERMISSION_REQUEST_CODE);
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (locationManager != null) {
            locationManager.removeUpdates(this);
        }
    }

    @Override
    public void onLocationChanged(Location location) {
        if (map == null || location == null) {
            return;
        }

        LatLng coord = new LatLng(location);

        LocationOverlay locationOverlay = map.getLocationOverlay();
        locationOverlay.setVisible(true);
        locationOverlay.setPosition(coord);
        locationOverlay.setBearing(location.getBearing());

        map.moveCamera(CameraUpdate.scrollTo(coord));
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }

    @Override
    public void onProviderEnabled(String provider) {
    }

    @Override
    public void onProviderDisabled(String provider) {
    }

    private boolean hasPermission() {
        return PermissionChecker.checkSelfPermission(this, PERMISSIONS[0])
            == PermissionChecker.PERMISSION_GRANTED
            && PermissionChecker.checkSelfPermission(this, PERMISSIONS[1])
            == PermissionChecker.PERMISSION_GRANTED;
    }
}

Java

public class CustomLocationTrackingActivity extends AppCompatActivity
    implements LocationListener {
    private static final int PERMISSION_REQUEST_CODE = 100;
    private static final String[] PERMISSIONS = {
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION
    };

    private NaverMap map;
    @Nullable
    private LocationManager locationManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // ...

        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(naverMap -> map = naverMap);

        locationManager = (LocationManager)getSystemService(LOCATION_SERVICE);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
        @NonNull String[] permissions, @NonNull int[] grantResults) {

        if (requestCode == PERMISSION_REQUEST_CODE) {
            if (hasPermission() && locationManager != null) {
                locationManager.requestLocationUpdates(
                    LocationManager.GPS_PROVIDER, 1000, 10, this);
            }
            return;
        }

        super.onRequestPermissionsResult(
            requestCode, permissions, grantResults);
    }

    @Override
    protected void onStart() {
        super.onStart();

        if (hasPermission()) {
            if (locationManager != null) {
                locationManager.requestLocationUpdates(
                    LocationManager.GPS_PROVIDER, 1000, 10, this);
            }
        } else {
            ActivityCompat.requestPermissions(
                this, PERMISSIONS, PERMISSION_REQUEST_CODE);
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (locationManager != null) {
            locationManager.removeUpdates(this);
        }
    }

    @Override
    public void onLocationChanged(Location location) {
        if (map == null || location == null) {
            return;
        }

        LatLng coord = new LatLng(location);

        LocationOverlay locationOverlay = map.getLocationOverlay();
        locationOverlay.setVisible(true);
        locationOverlay.setPosition(coord);
        locationOverlay.setBearing(location.getBearing());

        map.moveCamera(CameraUpdate.scrollTo(coord));
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }

    @Override
    public void onProviderEnabled(String provider) {
    }

    @Override
    public void onProviderDisabled(String provider) {
    }

    private boolean hasPermission() {
        return PermissionChecker.checkSelfPermission(this, PERMISSIONS[0])
            == PermissionChecker.PERMISSION_GRANTED
            && PermissionChecker.checkSelfPermission(this, PERMISSIONS[1])
            == PermissionChecker.PERMISSION_GRANTED;
    }
}

Kotlin

class CustomLocationTrackingActivity : AppCompatActivity(), LocationListener {
    private var map: NaverMap? = null
    private var locationManager: LocationManager? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // ...

        val fm = supportFragmentManager
        val mapFragment = fm.findFragmentById(R.id.map) as MapFragment?
                ?: MapFragment.newInstance().also {
                    fm.beginTransaction().add(R.id.map, it).commit()
                }

        mapFragment.getMapAsync { naverMap ->
            map = naverMap
        }

        locationManager = getSystemService(LOCATION_SERVICE) as LocationManager?
    }

    override fun onRequestPermissionsResult(requestCode: Int,
                                            permissions: Array<String>,
                                            grantResults: IntArray) {

        if (requestCode == PERMISSION_REQUEST_CODE) {
            if (hasPermission()) {
                locationManager?.requestLocationUpdates(
                        LocationManager.GPS_PROVIDER, 1000, 10f, this)
            }
            return
        }

        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }

    override fun onStart() {
        super.onStart()

        if (hasPermission()) {
            locationManager?.requestLocationUpdates(
                    LocationManager.GPS_PROVIDER, 1000, 10f, this)
        } else {
            ActivityCompat.requestPermissions(
                    this, PERMISSIONS, PERMISSION_REQUEST_CODE)
        }
    }

    override fun onStop() {
        super.onStop()
        locationManager?.removeUpdates(this)
    }

    override fun onLocationChanged(location: Location?) {
        if (location == null) {
            return
        }

        map?.let {
            val coord = LatLng(location)

            val locationOverlay = it.locationOverlay
            locationOverlay.isVisible = true
            locationOverlay.position = coord
            locationOverlay.bearing = location.bearing

            it.moveCamera(CameraUpdate.scrollTo(coord))
        }
    }

    override fun onStatusChanged(provider: String, status: Int,
                                 extras: Bundle) {
    }

    override fun onProviderEnabled(provider: String) {
    }

    override fun onProviderDisabled(provider: String) {
    }

    private fun hasPermission(): Boolean {
        return PermissionChecker.checkSelfPermission(this, PERMISSIONS[0]) ==
                PermissionChecker.PERMISSION_GRANTED &&
                PermissionChecker.checkSelfPermission(this, PERMISSIONS[1]) ==
                PermissionChecker.PERMISSION_GRANTED
    }

    companion object {
        private const val PERMISSION_REQUEST_CODE = 100
        private val PERMISSIONS = arrayOf(
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION)
    }
}

results matching ""

    No results matching ""