Build the route
To enable turn-by-turn navigation in your app, the Navigation SDK requires a defined route. You can generate this route using the Navigation SDK API by providing an origin and destination Point
s. Additionally, the SDK offers several customization options for tuning the generated route and its visual presentation within your app, including:
- Adding waypoints between the origin and destination.
- Ensuring the route aligns with the driver's current direction to avoid unnecessary U-turns.
- Defining the preferred side of the road for approaching the destination or intermediate stops along the route.
Request a route
The Navigation SDK leverages the Mapbox Directions API behind the scenes to generate routes. Once you've initialized MapboxNavigation
, you can use therequestRoutes()
method to request routes via the Nav SDK API.
The method requires a built RouteOptions
object as defined in the Mapbox Java SDK's Directions wrapper. The RouteOptions
class has convenient methods for setting the various route parameters. You can find full descriptions of each parameter in the Directions API documentation.
To create a RouteOptions
object for a new route request, you can use the Java SDK's RouteOptions.builder
class.
This builder provides many methods for customizing your request, with two being mandatory:
coordinatesList()
must include at least 2Point
sorigin
;destination
.
applyDefaultNavigationOptions()
: use that as a starting point to apply smart defaults to the builder.
mapboxNavigation.requestRoutes(
RouteOptions.builder()
.applyDefaultNavigationOptions()
.coordinatesList(listOf(originPoint, destinationPoint))
.build(),
NavigationRouterCallback { ... }
)
The origin
and destination
are represented as Point
coordinates within a single list of Points
. The origin
must occupy the first position in the list, while the destination
must be the last. Any additional Points
present
between the origin
and destination
are treated as waypoints
.
Include waypoint stops
If your navigation experience involves several stops along the way to the final destination, you can add up to 25
coordinates (including the origin and destination) to the RouteOptions
builder. The coordinates are treated as
stops between the origin and destination Points
in the order that you add them (the first waypoint Point
is the
first stop along the route):
mapboxNavigation.requestRoutes(
RouteOptions.builder()
.applyDefaultNavigationOptions()
.coordinatesList(listOf(originPoint, firstWaypoint, secondWaypoint, thirdWaypoint, destinationPoint))
.build(),
NavigationRouterCallback { ... }
)
Request a route in a specific direction
The Navigation SDK is capable of sending a request to the Directions API which considers the orientation of the device. This ensures that the suggested route is optimized for the device's current bearing.
Device location: In the adjacent diagram, the blue dot with white stroke is the device location.
Bearing: Bearing indicates the direction a device is pointing, measured in degrees clockwise from true north, ranging from 0 to 359. For instance, a bearing of 0° signifies the device is facing north, while a 90° bearing indicates an eastward orientation. In the provided diagram, the pink arrow represents the device's direction, showing it is facing due west, corresponding to a bearing of 270°.
Tolerance: Tolerance refers to the acceptable degree variation from the bearing angle within which a route can still be recommended. Illustrated by the semi-transparent blue area, it represents the permissible deviation range. In this example, the tolerance is 90°, allowing a deviation of up to 45° on either side of the bearing angle.
The RouteOptions.builder()
's bearingsList()
method is a list of Bearing
objects where each specifies angle clockwise from true north between 0 and 359 and tolerance, which is the range of degrees by which the angle can deviate (recommended value is 45 or 90).
If the bearingsList()
method is used, the list of Bearing
objects must be the same length as the list of Point
s passed through the coordinates()
method. But you can skip a coordinate and show its position in the Bearing
list with the null
value.
mapboxNavigation.requestRoutes(
RouteOptions.builder()
.applyDefaultNavigationOptions()
.coordinatesList(listOf(originPoint, destinationPoint))
.bearingsList(
listOf(
Bearing.builder()
.angle(originLocation.bearing.toDouble())
.degrees(45.0)
.build(),
null
)
)
.build(),
NavigationRouterCallback { ... }
)
Specify a side of the road to approach
You can state from which side of the road to approach a waypoint by adding approaches
to the RouteOptions
builder. There are three options found in the DirectionsCriteria
class:
"unrestricted"
(default): the route can approach waypoints from either side of the road. UseDirectionsCriteria.APPROACH_UNRESTRICTED
."curb"
: Return the route in a way that, upon arrival, the waypoint is located on the side corresponding to thedriving_side
of the region where the route is situated. UseDirectionsCriteria.APPROACH_CURB
.null
: if no option is specified, it is translated internally to""
, which has the same result as setting an approach to"unrestricted"
. Usenull
.
If provided, the list of approaches must be the same length as the list of coordinates (including the origin
and the destination
) and in that particular order (origin
, waypoints, destination
).
If a re-route occurs and approaches
were used to fetch the first DirectionsRoute
, the new route fetched will take the same approaches
criteria into account.
mapboxNavigation.requestRoutes(
RouteOptions.builder()
.applyDefaultNavigationOptions()
.coordinatesList(listOf(originPoint, firstWaypoint, secondWaypoint, thirdWaypoint, destinationPoint))
.approachesList(listOf(DirectionsCriteria.APPROACH_UNRESTRICTED,
DirectionsCriteria.APPROACH_UNRESTRICTED, DirectionsCriteria.APPROACH_CURB, null, DirectionsCriteria.APPROACH_CURB))
.build(),
NavigationRouterCallback { ... }
)
Silent waypoints
Unlike other waypoints that are treated as a stops between route's legs, silent waypoints will influence the route so the user passes through the waypoint without specifically mentioning it in maneuver instructions.
By default all coordinates specified in the coordinatesList
are waypoints.
If you want to specify that one or more coordinates in the list should be treated as silent waypoints, use RouteOption.Builder
's waypointIndicesList
method.
List the index of each coordinate (as it is in coordinatesList
) that should be treated as a waypoint and omit the index of coordinates that should be treated as silent waypoints.
To create a route that includes the originPoint
, silentWaypoint
, and destinationPoint
, with no stop at the silentWaypoint
, you should set waypointIndicesList
to listOf(0, 2)
. This ensures that the route passes through the middle coordinate (silentWaypoint
) without designating it as a stopping point.
mapboxNavigation.requestRoutes(
RouteOptions.builder()
.applyDefaultNavigationOptions()
.coordinatesList(listOf(originPoint, silentWaypoint, destinationPoint))
.waypointIndicesList(listOf(0, 2))
.build(),
NavigationRouterCallback { ... }
)
The Navigation SDK will build a route that passes through all three coordinates, but the silentWaypoint
will not be mentioned in any of the route instructions.
Assign names to waypoints
You can assign a name to any waypoint using RouteOption.Builder
's waypointNamesList
method. Waypoint names are used in the maneuver UI component and voice instructions.
In the given example, a route is created starting from the originPoint
, with stops at two waypoints named "Gas station" and "Work".
mapboxNavigation.requestRoutes(
RouteOptions.builder()
.applyDefaultNavigationOptions()
.coordinatesList(listOf(originPoint, gasStationWaypoint, workWayPoint))
.waypointNamesList(listOf("", "Gas station", "Work"))
.build(),
NavigationRouterCallback { ... }
)
waypointIndicesList
and waypointNamesList
.Check the status of a route request
The Navigation SDK's mapboxNavigation.requestRoutes()
method requires two parameters: a RouteOptions
object and a NavigationRouterCallback
interface object. The Navigation SDK’s NavigationRouterCallback
interface provides methods, which inform you about the status of a route request.
val routesRequestCallback = object : NavigationRouterCallback {
fun onRoutesReady(routes: List<NavigationRoute>, @RouterOrigin routerOrigin: String) {
mapboxNavigation.setNavigationRoutes(routes)
}
fun onFailure(reasons: List<RouterFailure>, routeOptions: RouteOptions) {
}
fun onCanceled(routeOptions: RouteOptions, @RouterOrigin routerOrigin: String) {
}
}
Add the NavigationRouterCallback
object to the mapboxNavigation.requestRoutes()
method:
mapboxNavigation.requestRoutes(
RouteOptions.builder()
.applyDefaultParams()
.coordinatesList(listOf(originPoint, destinationPoint))
.build(),
routesRequestCallback
)
Route request/response models
The request/response models provide 2 types of API: Static and Dynamic. Static is used for stable features of Mapbox Directions API, Dynamic for unlisted, experimental, or Developer Preview features.
Static API
This is the type of interaction that's presented above - accessing/setting stable properties via plain Java getters/builders for every stable property from Directions API's response models.
Here is an example accessing duration
property from the route object:
directionsRoute.duration()
You can use builders to create and update models. Every model has an associated builder.
The builder can be created by calling a static builder()
method on a model's class or by calling toBuilder()
on a model's instance.
See an example of RouteOptions
creation via the builder:
RouteOptions.builder()
.applyDefaultNavigationOptions()
.applyLanguageAndVoiceUnitOptions(this)
.coordinatesList(listOf(origin, destination))
.build()
Dynamic API
To reference Directions API features that are either unlisted, experimental, or in a Developer Preview phase, you can use DirectionsJsonObject#getUnrecognizedProperty
.
For example, if Directions API route object had a duration_experimental
property marked as a preview, you would be able to access it like in the example below:
directionsRoute.getUnrecognizedProperty("duration_experimental")
DirectionsJsonObject#getUnrecognizedProperty
returns a JsonElement
. You can extract and cast the value based on the documented type.
For example, if duration_experimental
from the previous example had a type number
in documentation, you would be able to convert it to Double
.
directionsRoute.getUnrecognizedProperty("duration_experimental")?.asDouble
Read gson's documentation to learn how to access data from a JsonElement
.
You can't set unrecognized properties to models. The only exception is RouteOptions
that is used to create Directions API request.
Use RouteOptions.Builder#unrecognizedProperties
if you want to add an unlisted, experimental, or preview parameter to a request:
RouteOptions.builder()
.applyDefaultNavigationOptions()
.coordinatesList(listOf(origin, destination))
.unrecognizedProperties(mapOf(
"experimentalParameterName" to "experimentalParameterValue"
))
.build()
Set an active route for navigation
Once you have a reference to available routes, you can set a primary route that will be used for navigation by calling MapboxNavigation#setNavigationRoutes()
.
private val routesRequestCallback = object : NavigationRouterCallback {
override fun onRoutesReady(
routes: List<NavigationRoute>,
routerOrigin: RouterOrigin
) {
mapboxNavigation.setNavigationRoutes(routes)
}
override fun onCanceled(routeOptions: RouteOptions, routerOrigin: RouterOrigin) {
}
override fun onFailure(reasons: List<RouterFailure>, routeOptions: RouteOptions) {
}
}
If the list is not empty, the route at index 0 is valid, and the trip session is started, then the SDK enters an Active Guidance
state and RouteProgress
updates will be available.
Listen for route changes
Use the Navigation SDK’s RoutesObserver
interface if you want to know when the routes list has changed.
This observer will be called with a new list of routes when:
- Routes were changed with
MapboxNavigation.setNavigationRoutes
; - Routes were refreshed (for example, there are updated
congestion
annotations that supply details about live traffic conditions on the route.); - A driver went off route and a re-route was executed;
- Alternative routes were updated;
- The SDK moved to
FreeDrive
, that means routes were removed from the SDK level.
The RoutesObserver
is the source of truth for route updates. You should use it for all route-related actions (like updating UI elements or drawing a route on a map).
The route at index 0 in this list is always considered the primary route, while the rest are potential alternative routes. You can get the current list of routes with getNavigationRoutes()
:
val currentRoutes = mapboxNavigation.getNavigationRoutes()
private val routesObserver = object : RoutesObserver {
override fun onRoutesChanged(routes: List<DirectionsRoute>) {
}
}
Register the RoutesObserver
interface with your MapboxNavigation
object.
mapboxNavigation.registerRoutesObserver(routesObserver)
It is required to unregister the RoutesObserver
interface:
override fun onStop() {
super.onStop()
mapboxNavigation.unregisterRoutesObserver(routeProgressObserver)
}
Use Map match to generate a route
Map matching is the art of taking coordinates and aligning them along a road network. The request is like to the route requet
, where it is required to set coordinates
, that represent geometry of the route Point
by Point
:
val mapMatchingAPICallback = object : MapMatchingAPICallback {
override fun success(result: MapMatchingSuccessfulResult) {
mapboxNavigation.setRoutes(result.navigationRoutes)
}
override fun failure(failure: MapMatchingFailure) {
}
override fun onCancel() {
}
}
mapboxNavigation.requestMapMatching(
MapMatchingOptions.Builder()
.coordinates(routePointsList)
.build(),
mapMatchingAPICallback
)
If successful, the response will have a matched route. Convert this route to a DirectionsRoute
and give it to the already-instantiated MapboxNavigation
object:
mapboxMapMatchingRequest.enqueueCall(object : Callback<MapMatchingResponse> {
override fun onResponse(call: Call<MapMatchingResponse>, response: Response<MapMatchingResponse>) {
if (response.isSuccessful) {
response.body()?.matchings()?.let { matchingList ->
matchingList[0].toDirectionRoute().toNavigationRoute(
RouterOrigin.Custom()
).apply {
mapboxNavigation?.setNavigationRoutes(listOf(this))
}
}
}
}
override fun onFailure(call: Call<MapMatchingResponse>, throwable: Throwable) {
}
})