Navigation SDK for Android

Route progress

Once turn-by-turn navigation begins, your project will need to follow the user's progress along the route to deliver contextual information in the correct way at the correct time. The Navigation SDK's RouteProgress object contains this information and this page teaches you about using it.

Levels of route progress

The Mapbox Navigation SDK's model for tracking route progress has three pieces with different levels of granularity: the route, the leg, and the step.

Route: The blue line is a route. A route stretches between the origin and the final destination.

Leg: The larger circles with a pink stroke represent waypoints, or stops, along the route. A leg is the part of the route between two waypoints.

Step: The smaller circles with a green stroke represent maneuvers. A step is the part of the leg between two maneuvers.

The Navigation SDK uses three classes to communicate information on the user's progress at these three different levels: RouteProgress, RouteLegProgress, and RouteStepProgress.


The RouteProgress class contains all the progress information at any time during a navigation session. A new RouteProgress object is generated whenever there's a new valid Location update or if no new Location update is received in the past second.

RouteProgress information includes distance measurements, the percentage of the route that's been completed, the current step index, the remaining number of route legs, and much more.


The RouteLegProgress class is specific to the current leg the user is on. If there is only one leg in the directions route, much of this information will be the same as in the parent RouteProgress.


The RouteStepProgress class is a progress object specific to the current step the user is on.

Listen to progress change

Tracking a user's progress along a route is key to providing helpful and prompt navigation instructions. Implement the Navigation SDK's RouteProgressObserver interface to receive a RouteProgress object every time the user's location changes.

The RouteProgressObserver can typically be used to refresh most of your application's user interface when a change occurs. For example, if you're displaying the user's current progress until the user needs to do the next maneuver. Every time this interface's onRouteProgressChanged() method fires, you can update your view with the new information inside the RouteProgress object.

private val routeProgressObserver = object : RouteProgressObserver {
        override fun onRouteProgressChanged(routeProgress: RouteProgress) {


If you've created your own RouteProgressObserver object, you'll need to:

  1. Register the RouteProgressObserver with your already-instantiated MapboxNavigation object.

  2. Don’t forget to unregister the observer with mapboxNavigation.unregisterRouteProgressObserver(routeProgressObserver). This line isn't needed if you're already running MapboxNavigation's onDestroy() method, which automatically unregisters the observer for you.

    override fun onStop() {

The UI SDK class NavigationMapRoute includes a RouteProgressObserver which will update the route line on the map. It includes a feature to alter the appearance of the route line as the navigation progresses. You can customize the appearance of the section of route line that has been traveled to be a different color or transparent. See the section on styling for more information about the route line appearance. The feature to alter the appearance of the route line during route progress is disabled by default. In order to enable it use the NavigationMapRoute builder.

MapboxNavigation mapboxNavigation = new MapboxNavigation(...);
NavigationMapRoute navigationMapRoute = new NavigationMapRoute.Builder(...)
    .withMapboxNavigation(mapboxNavigation, true)


The Navigation SDK's RouteProgressState enum contains various states that can occur while navigating. Using RouteProgressState helps you gain a better of understanding of what's happening in the overall navigation experience.

The RouteProgressObserver returns a RouteProgress object. Check the current state with the RouteProgressState object inside the returned RouteProgress object:

private val routeProgressObserver = object : RouteProgressObserver {
    override fun onRouteProgressChanged(routeProgress: RouteProgress) {
        routeProgress.currentState()?.let { currentState ->
            val state = currentState

There are six possible states:

  • RouteProgressState.ROUTE_INVALID
  • RouteProgressState.ROUTE_INITIALIZED
  • RouteProgressState.LOCATION_TRACKING
  • RouteProgressState.ROUTE_ARRIVED
  • RouteProgressState.LOCATION_STALE
  • RouteProgressState.ROUTE_UNCERTAIN


If the DirectionsRoute provided via MapboxNavigation.startNavigation() is not valid, the state will be RouteProgressState.ROUTE_INVALID.


When a new route is first loaded and the DirectionsRoute JSON is valid, route-following will start in the RouteProgressState.INITIALIZED state. From there, route-following will try to gain confidence that the Location objects being passed to the device are the user's location. To show this trust, at least a few location updates need to be delivered and they must be consecutively coherent in both time and space. While it is in the process of establishing this trust, the route-following logic will report that it's still in the RouteProgressState.INITIALIZED state.


Once MapboxNavigation is confidently tracking the Location updates and processing them against the DirectionsRoute. The state will change to RouteProgressState.TRACKING.


When the user has arrived at the destination of the given RouteLeg, the state will be RouteProgressState.ROUTE_ARRIVED.


If a lack of Location updates has caused a lack of confidence in the progress updates being sent, the state will be RouteProgressState.LOCATION_STALE.


The RouteProgressState.UNCERTAIN state occurs when the Navigation SDK has already been tracking location updates (RouteProgressState.TRACKING), but the route-snapping algorithm has temporarily lost confidence. For example, due to a re-route when the device strays off the turn-by-turn directions route. After a couple of Location updates after the re-route, RouteProgressState would return back to RouteProgressState.TRACKING.

Arrival experience

Show and create experiences when arriving at waypoints and destinations. There are a few options for customizing the arrival experience.


The ArrivalObserver interface is a callback, and you can have many observers. Each observer will receive one callback for every state change.

private val arrivalObserver = object : ArrivalObserver {
        override fun onNextRouteLegStart(routeLegProgress: RouteLegProgress) {


        override fun onFinalDestinationArrival(routeProgress: RouteProgress) {

  • onNextRouteLegStart is called once the driver has arrived at a stop and has started navigating the next leg. navigateNextRouteLeg returns true when this happens.
  • onFinalDestinationArrival is called once the driver has reached the final destination at the end of the route. routeProgress.currentState() will equal RouteProgressState. ROUTE_COMPLETE.

Register the ArrivalObserver interface with your already-instantiated MapboxNavigation object.


Don’t forget to unregister the ArrivalObserver interface:

override fun onStop() {


The ArrivalController allows you to decide when to navigate the next RouteLeg when navigating waypoints and routes with multiple stops. Use the ArrivalOptions class to decide when you want to receive navigateNextRouteLeg callbacks as you approach a stop.MapboxNavigation.attachArrivalController(yourArrivalController).

AutoArrivalController is the default and will automatically navigateNextRouteLeg. To override this behavior, attach your own ArrivalController or MapboxNavigation.removeArrivalController().

  • When your controller navigateNextRouteLeg returns true, the navigator will start navigating to the next stop.
  • When your controller navigateNextRouteLeg returns false forever, use MapboxNavigation.navigateNextRouteLeg directly to continue navigation.

You can attach your own ArrivalController. This example shows you can use both time and distance:

mapboxNavigation.attachArrivalController(object : ArrivalController {
    override fun arrivalOptions(): ArrivalOptions = ArrivalOptions.Builder()
    override fun navigateNextRouteLeg(routeLegProgress: RouteLegProgress): Boolean {

        return routeLegProgress.distanceRemaining() < 5.0

More about route progress

Read more about route progress in Faster-route detection: Specify when to check for faster routes and how to determine if a route is faster, then retrieve and initialize faster routes.

Was this page helpful?