Device location
Most navigation experiences rely on knowing the location of the device being used. Device location is often used as the origin in route requests and is used to provide location updates that are essential to computing route progress. This guide walks through how the Mapbox Navigation SDK uses device location information, how to customize the device location puck's UI, how to use a custom LocationEngine
, and how to use the Navigation SDK with the Maps SDK's LocationComponent
.
The Navigation UI SDK listens to device location changes to update many UI components as the user progresses along a route including displaying a device location puck on the map using the Maps SDK's LocationComponent
.
LocationEngine
works, see the LocationEngine
documentation.Observe location changes
The Navigation SDK's LocationObserver
interface listener is for receiving device location updates. Using the LocationObserver
interface is optional. It's available in case you want to track device location yourself and do something with the returned Location
object’s coordinates or altitude values.
By default, the Navigation UI SDK will automatically listen to location updates if highly-abstracted Navigation UI SDK classes, for example NavigationMapboxMap
or NavigationView
, are used. If these classes aren't used, a developer's custom LocationEngine
implementation code will need to push Location
updates to the Navigation UI SDK.
The LocationObserver
’s two methods, onRawLocationChanged
and onEnhancedLocationChanged
, will fire whenever the device changes location. These updates are only available after the trip session is started.
private val locationObserver = object : LocationObserver {
override fun onRawLocationChanged(rawLocation: Location) {
}
override fun onEnhancedLocationChanged(
enhancedLocation: Location, keyPoints: List<Location>) {
}
}
Register the LocationObserver
interface with your already-instantiated MapboxNavigation
object.
mapboxNavigation.registerLocationObserver(locationObserver)
Don’t forget to unregister the LocationObserver
interface:
override fun onStop() {
super.onStop()
mapView.onStop()
mapboxNavigation.unregisterLocationObserver(locationObserver)
}
onRawLocationChanged
The onRawLocationChanged
method delivers a raw Location
object from the developer-provided or default internal LocationEngine
.
onEnhancedLocationChanged
The onEnhancedLocationChanged
method provides the most accurate location update possible. The location is snapped to the route, or if possible, to the road network. The snapping is based on various algorithms in the Navigation SDK's logic.
This Navigation SDK enhanced location logic helps to solve common navigation issues such as:
- Loss of location coordinate precision in cities with tall buildings
- Frequency issues
- Loss of signal in tunnels
If the Navigation SDK isn't able to compute a better Location
update, the raw Location
will be returned.
keyPoints
is a potentially-empty list of predicted Location
coordinates that lead up to the latest Location
update coordinate. If the list isn't empty, the last coordinate in the list is always equal to enhancedLocation
object.
These coordinates are snapped to a route or road. This is especially helpful for making sure the device location puck doesn't cut intersection corners as it traverses through a turn maneuver.
Combine with LocationComponent
To show the a device location puck on a map in a Navigation SDK project, you can use the Maps SDK's LocationComponent
. Read more about the Maps SDK's LocationComponent
and customizing its look with its styling options.
By default, the style of the device location puck that is added to the map is inherited by default from the Mapbox Maps SDK for Android LocationComponent
. Read more about the Maps SDK's LocationComponent
and customizing its look with its styling options.
When using the LocationComponent
in combination with the Navigation SDK's LocationObserver
interface (described above), you'll need to pass false
through the LocationComponentActivationOptions.builder()
’s useDefaultLocationEngine()
method. This way, you can give the correct Location
object to the LocationComponent
inside of the onEnhancedLocationChanged()
callback method. Once you pass the Location
object, the Navigation SDK will move the LocationComponent
puck to the appropriate place on the map and make other adjustments.
To activate the LocationComponent
:
mapboxMap.setStyle(Style.MAPBOX_STREETS) { style ->
locationComponent = mapboxMap.locationComponent.apply {
activateLocationComponent(
LocationComponentActivationOptions.builder(context, style)
.useDefaultLocationEngine(false)
.build()
)
}
}
Pass the LocationComponent
the Location
object returned by the LocationObserver
:
private val locationObserver = object : LocationObserver {
override fun onRawLocationChanged(rawLocation: Location) {
}
override fun onEnhancedLocationChanged(
enhancedLocation: Location,
keyPoints: List<Location>
) {
if (keyPoints.isNotEmpty()) {
locationComponent?.forceLocationUpdate(keyPoints, false)
} else {
locationComponent?.forceLocationUpdate(listOf(enhancedLocation), false)
}
}
}
See how to combine the Navigation SDK with the Maps SDK's LocationComponent
LocationEngine
customization
The default LocationEngine
is provided by LocationEngineProvider.getBestLocationEngine(context)
.
Yet, LocationEngine
is an interface that can be overridden. This allows you to provide locations from automobiles, scooters, simulations, games, or your own version of Android's location provider.
Location
's provided by the LocationEngine
must override getElapsedRealtimeNanos()
with SystemClock.elapsedRealtimeNanos. This specific clock improves the location accuracy.Custom LocationEngine
with the Navigation UI SDK
You can pass the LocationEngine
with the NavigationViewOptions
:
navigationView.startNavigation(NavigationViewOptions.builder(context)
.locationEngine(customLocationEngine)
.build())
Custom LocationEngine
with the Navigation Core SDK
You can pass the LocationEngine
with the NavigationOptions
:
val mapboxNavigation = MapboxNavigationProvider.create(MapboxNavigation
.defaultNavigationOptionsBuilder(context, accessToken)
.locationEngine(customLocationEngine)
.build())
Location engine request
You can create your own location engine request if you'd like precise control over Location
querying and receiving Location
updates in your project.
First, create a LocationEngineRequest
to specify parameters such as the querying frequency or preferred accuracy:
val locationEngineRequest = LocationEngineRequest.Builder(DESIRED_INTERVAL_BETWEEN_LOCATION_UPDATES_IN_MILLISECONDS)
.setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
.setMaxWaitTime(DEFAULT_MAX_WAIT_TIME) // sets the maximum wait time in milliseconds for location updates. Locations determined at intervals but delivered in batch based on wait time. Batching is not supported by all engines.
.build()
Then pass the LocationEngineRequest
as MapboxNavigation
's constructor parameter.
Listen for raw location updates independently
If you still want to receive location updates when the trip session is stopped, create a custom class that implements the LocationEngineCallback
interface. This interface comes from Mapbox's Core library for Android:
private class CustomLocationEngineCallback(activity: Activity) :
LocationEngineCallback<LocationEngineResult> {
private val activityRef = WeakReference(activity)
override fun onSuccess(result: LocationEngineResult?) {
}
override fun onFailure(exception: Exception) {
}
}
Create an instance of the custom callback class:
val customLocationEngineCallback = CustomLocationEngineCallback(activity)
Start requesting Location
updates now that you have your LocationEngineRequest
and CustomLocationEngineCallback
objects:
mapboxNavigation?.locationEngine?.requestLocationUpdates(
locationEngineRequest,
customLocationEngineCallback,
mainLooper // returns your project's main looper, which lives in your project's main thread.
)
Make sure to remove location updating:
override fun onStop() {
super.onStop()
mapboxNavigation?.locationEngine?.removeLocationUpdates(locationListenerCallback)
}