Location tracking
The Mapbox Navigation SDK uses raw device location to power important navigation features including:
- Enhancing location updates and snapping them to the road graph.
- Determining which instruction to show next, and when to show it, based on where the user is along the route.
- Detecting when the driver has deviated from the current route, prompting the SDK to generate a new route to get them back on track.
- Proactively downloading map tiles and routing information ahead of the driver's current location in case the driver enters an area with no or poor network connection.
The Navigation SDK depends on two other Mapbox SDKs, the Mapbox Android Core library and the Maps SDK for Android, to handle location permissions, track location updates, and show device location on a map. These SDKs are bundled with the Navigation SDK, and you do not need to install them separately.
Privacy and permissions
Users must grant your application permission before it can access information about their location. During this permission prompt, you can present a custom string explaining how location will be used.
If you're building your Android project targeting API level 23 or higher, your application will need to request permissions at runtime. Handling this directly in your activity produces boilerplate code and can often be hard to manage. One option is to use the Mapbox Core Library for Android's PermissionsManager
class. When using the Mapbox Core Library, you can specify the text used in the permission prompt using onExplanationNeeded
.
Location provider
By default, the Navigation SDK utilizes the Mapbox Core Library for Android's LocationEngine
class to fetch the user's location. The LocationEngine
class simplifies the process of getting location information and supports the following location providers:
- Google's Fused Location Providers
- Android GPS and Network Providers
groovyimplementation("com.google.android.gms:play-services-location:21.0.1")
LocationEngine
works, see the LocationEngine
documentation.Enhanced location updates
To receive location updates while the trip session is running, you can subscribe to the LocationObserver
. This will allow you to listen for two types of location updates:
- Raw location (
onNewRawLocation
): Invoked as soon as a newLocation
has been received. - Enhanced location (
onNewLocationMatcherResult
): Invoked when a newLocation
is snapped to the route or map matched to the road.
private val locationObserver = object : LocationObserver {
override fun onNewRawLocation(rawLocation: Location) {
...
}
override fun onNewLocationMatcherResult(locationMatcherResult: LocationMatcherResult) {
...
}
}
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()
mapboxNavigation.unregisterLocationObserver(locationObserver)
}
onNewRawLocation
callback
The onNewRawLocation
method delivers a raw Location
object from the developer-provided or default internal LocationEngine
.
onNewLocationMatcherResult
callback
The onNewLocationMatcherResult
method provides the most accurate location update possible. The location is snapped to the route, or if possible, to the road network.
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.
locationMatcherResult
provides details about the status of the enhanced location. 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 the 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.
Location puck on the map
If your application includes a map, you can display the user location puck on the map using the Mapbox Maps SDK's user location component. The plugin can display both a 2D and 3D puck.
Maps SDK
documentation.After you've added a MapView
to your application, create the NavigationLocationProvider
utility class, which helps manage connecting the Navigation SDK and the Maps SDK to display the user location puck:
val navigationLocationProvider = NavigationLocationProvider()
Then, activate the Maps SDK's user location component, set the puck drawable (included with the Navigation SDK), and set the location data provider:
mapView.location.apply {
this.locationPuck = LocationPuck2D(
bearingImage = ContextCompat.getDrawable(
this@MainActivity,
R.drawable.mapbox_navigation_puck_icon
)
)
setLocationProvider(navigationLocationProvider)
enabled = true
}
Once the puck is initialized, you can start passing the enhanced location updates to the puck and render the updated location on the map:
private val locationObserver = object : LocationObserver {
override fun onNewRawLocation(rawLocation: Location) {
...
}
override fun onNewLocationMatcherResult(locationMatcherResult: LocationMatcherResult) {
val transitionOptions: (ValueAnimator.() -> Unit) =
{
duration = 1000
}
navigationLocationProvider.changePosition(
location = locationMatcherResult.enhancedLocation,
keyPoints = locationMatcherResult.keyPoints,
latLngTransitionOptions = transitionOptions,
bearingTransitionOptions = transitionOptions
)
}
}
Use NavigationLocationProvider
to show a device's current location as a puck on a map.
NavigationCamera
uses a duration of 1 second. To keep the puck in sync, use 1 second for the puck transitionOptions
as well.Custom location engine
By default, the Navigation SDK uses a LocationEngine
that comes from LocationEngineProvider.getBestLocationEngine(context)
, but you can override this default. 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.You can pass the LocationEngine
with the NavigationOptions
:
val navigationOptions = NavigationOptions.Builder(context)
.accessToken("{your_access_token}")
.locationEngine(customLocationEngine)
.build()
val mapboxNavigation = MapboxNavigationApp.setup(navigationOptions)
Location engine request
You can create your own location engine request if you want 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 customLocationEngineRequest = 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()
val navigationOptions = NavigationOptions.Builder(context)
.accessToken("{your_access_token}")
.locationEngine(customLocationEngine)
.locationEngineRequest(customLocationEngineRequest)
.build()
val mapboxNavigation = MapboxNavigationApp.setup(navigationOptions)
Raw location updates when the trip session is stopped
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?) {
// for example, push a location update to the Map Location Plugin and the NavigationLocationProvider here
}
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:
locationEngine.requestLocationUpdates(
locationEngineRequest,
customLocationEngineCallback,
mainLooper // returns your project's main looper, which lives in your project's main thread.
)
LocationEngine
interface.kotlinval locationEngine = LocationEngineProvider.getBestLocationEngine(context)
Make sure to remove location updating:
override fun onStop() {
super.onStop()
locationEngine.removeLocationUpdates(locationListenerCallback)
}