Route line

The Navigation SDK allows you to display route lines on a map. This is a common UI component used in turn-by-turn navigation applications. While the route line is technically a line drawn on the map using the an API from the Mapbox Maps SDK, the Navigation SDK's route line API saves development time by addressing the most common navigation related use cases while also offering a high level of customization.

With the route line API you can manage the visual state of the route in the UI of your application.

Use the route line UI component

The route line UI component consists of two main classes:

  • The MapboxRouteLineApi has methods for creating and modifying the route line and producing data describing the view related mutations.
  • The MapboxRouteLineView is responsible for consuming the data produced by the MapboxRouteLineApi and rendering the results on the map.

By default, the route line UI component displays the route as a solid blue line with a darker blue outline.

Instantiate the route line API

Instantiate the MapboxRouteLineApi and MapboxRouteLineView classes in your Activity or Fragment.

val routeLineOptions = MapboxRouteLineOptions.Builder().build()
val routeLineApi = MapboxRouteLineApi(routeLineOptions)
val routeLineView = MapboxRouteLineView(routeLineOptions)

Draw a route line by passing a DirectionsRoute wrapped in a RouteLine instance to the MapboxRouteLineApi and rendering the result with the MapboxRouteLineView. Read more about requesting a DirectionsRoute in the Route generation guide.

One way to draw a route line is using a RoutesRequestCallback when requesting a route via the MapboxNavigation class.

private val routesReqCallback = object : RoutesRequestCallback {
    override fun onRoutesReady(routes: List<DirectionsRoute>) {
        // note: the first route in the list is considered the primary route
        val routeLines = routes.map { RouteLine(it, null) }
        routeLineApi.setRoutes(routeLines) { value ->
            routeLineView.renderRouteDrawData(mapStyle, value)
        }
    }

    override fun onRoutesRequestFailure(throwable: Throwable, routeOptions: RouteOptions) {
    }

    override fun onRoutesRequestCanceled(routeOptions: RouteOptions) {
    }
}

Another way to draw a route line is by registering a RoutesObserver with MapboxNavigation:

private val routesObserver: RoutesObserver = object : RoutesRequestCallback {
    fun onRoutesChanged(routes: List<DirectionsRoute>) {
        // note: the first route in the list is considered the primary route
        val routeLines = routes.map { RouteLine(it, null) }     
        routeLineApi.setRoutes(routeLines) { value ->
            routeLineView.renderRouteDrawData(mapStyle, value)
        }        
    }
}

Be sure to always unregister observers in onStop() or onDestroy().

Request routes with the right data

When requesting a route using MapboxNavigation there are some options that are necessary if you want to display traffic congestion on the route line. You can use RouteOptions.builder().applyDefaultNavigationOptions(), which will use the PROFILE_DRIVING_TRAFFIC profile and the required annotations.

If you are using custom route options, be sure to use the PROFILE_DRIVING_TRAFFIC profile and include at least ANNOTATION_CONGESTION_NUMERIC and ANNOTATION_DISTANCE in the annotationsList at a minimum. If any of these options are absent, there will not be enough data in the directions response to display traffic congestion on the route line.

val routeOptions = RouteOptions.builder()
    .profile(DirectionsCriteria.PROFILE_DRIVING_TRAFFIC)
    .annotationsList(
        listOf(
            DirectionsCriteria.ANNOTATION_CONGESTION_NUMERIC,
            DirectionsCriteria.ANNOTATION_DISTANCE
        )
    )
    .accessToken(getMapboxAccessTokenFromResources())
    .coordinates(listOf(origin, destination))
    .build()
    
mapboxNavigation.requestRoutes(routeOptions,routesReqCallback)

To display alternative route lines, include the alternatives(true) route option.

val routeOptions = RouteOptions.builder()
    .alternatives(true)
    .accessToken(getMapboxAccessTokenFromResources())
    .coordinates(listOf(origin, destination))
    .build()
    
mapboxNavigation.requestRoutes(routeOptions,routesReqCallback)

Handle route updates

When the navigation route changes, the MapboxRouteLineApi must be updated with the latest route data and the changes rendered in order for the route line on the map to accurately represent the current route.

Register a RouteProgressObserver with MapboxNavigation to keep the route line updated.

private val routeProgressObserver = RouteProgressObserver { routeProgress ->
    routeLineApi.updateWithRouteProgress(routeProgress) { result ->
        routeLineView.renderRouteLineUpdate(mapStyle, result)
    }
}

Always be sure to unregister observers in onStop() or onDestroy().

Customize the route line

Differentiate the route traveled

To visually differentiate the part of the route that has already been traveled from the route ahead, use the route line API's vanishing route line feature. Enabling this feature will make the section of the route line the behind the user's current location transparent by default, or you can configure a custom color.

Enable this feature using the MapboxRouteLineOptions. The color can be confgured with RouteLineColorResources::routeLineTraveledColor.

val routeLineOptions = MapboxRouteLineOptions()
    .withVanishingRouteLineEnabled(true)
    .build()

You should also register an OnIndicatorPositionChangedListener with the map location component.

mapView.location.addOnIndicatorPositionChangedListener(onPositionChangedListener)
private val onPositionChangedListener = OnIndicatorPositionChangedListener { point ->
    val result = routeLineApi.updateTraveledRouteLine(point)
    routeLineView.renderRouteLineUpdate(mapStyle, result)
}

Always be sure to unregister observers in onStop() or onDestroy().

Position the route line relative to map layers

The map on which you are displaying your route line contains many individual layers (for example, roads, buildings, labels, and more). The route line UI component consists of three layers that are added to the map.

By default, the route line is placed on top of all the other layers in the map. This is not always the most optimal position for a route line because it may appear on top of road labels and other labels on the map.

You can specify the route line layers' veritcal position within all map layers using the routeLineBelowLayerId parameter of MapboxRouteLineOptions. The route line layers will be placed below the layer with the ID indicated in the routeLineBelowLayerId option.

val routeLineOptions = MapboxRouteLineOptions.Builder()
    .withRouteLineBelowLayerId("road-label")
    .build()
Which layer ID should I use?

For navigation-related Mapbox map styles, you can use a routeLineBelowLayerId value of "road-label", but this layer is not present in all map styles.

If you are using a custom map style or another Mapbox-designed style, you can find the layer IDs that exist in the style by opening the style in Mapbox Studio or printing the style JSON to the console. For more information see Finding layer ids.

Change the style of the route line

The RouteLineResources class offers several options for customizing the style of the route line.

Color

You can customize the route line colors by providing RouteLineColorResources to the RouteLineResources.Builder class.

val customColorResources = RouteLineColorResources.Builder()
    .routeDefaultColor(Color.parseColor("#FFCC00"))
    // ... etc
    .build()

val routeLineResources = RouteLineResources.Builder()
    .routeLineColorResources(customColorResources)
    .build()

Scale

A route line consists of three layers on the map. The lowest layer is called the casing layer. Above the casing layer is the main route line layer. And the top layer is a traffic line layer. By default, the casing layer has the greatest width giving the appearance of a border around the route line and traffic line.

The scaling behavior of these lines is determined by the Expression class from the Maps SDK. With the RouteLineResources.Builder you can customize the scaling expressions rather than using the defaults. See the Maps SDK documentation for more information on creating expressions.

val routeLineResources = RouteLineResources.Builder()
    .routeCasingLineScaleExpression(aCustomCasingExpression)
    .routeLineScaleExpression(aCustomRouteLineExpression)
    .routeTrafficLineScaleExpression(aCustomTrafficLineExpression)
    .build()
Note

For the default expressions used in the Navigation SDK, see the source code of the RouteLineResources class.

Style inactive route legs

It's common for logistics applications to use routes with multiple waypoints. For better route visibility, you may want the route line to visually differentiate between the active route leg and inactive route legs.

When this feature is enabled and properly configured, the inactive route legs will appear a different color (or transparency) than the active route leg, making it clearer to the driver where the route is taking them. As the driver transitions from one route leg to another, the route leg colors will change to differentiate the active route leg from the inactive route legs. This feature only works on routes with multiple waypoints.

Enable this feature by setting the MapboxRouteLineOptions::styleInactiveRouteLegsIndependently option to true.

val routeLineOptions = MapboxRouteLineOptions.Builder()
    .styleInactiveRouteLegsIndependently(true)
    .build()

Set the color used for inactive route legs using the inActiveRouteLegsColor option in the RouteLineColorResources class.

val customColorResources = RouteLineColorResources.Builder()
    .inActiveRouteLegsColor(Color.parseColor("#FFCC00"))
    .build()

Provide a progress update to the route line API and render the result.

private val routeProgressObserver = RouteProgressObserver { routeProgress ->
    routeLineApi.updateWithRouteProgress(routeProgress) { result ->
        routeLineView.renderRouteLineUpdate(mapStyle, result)
    }
}

Display restricted road sections

The MapboxRouteLineOptions class includes an option to display roads or sections of roads designated as restricted. When you set this option to true, the route line will display a dashed line.

val routeLineOptions = MapboxRouteLineOptions()
     .displayRestrictedRoadSections(true)
     .build()

Further customization of the restricted road dashed line is available in the RouteLineResources class. The availble options are restrictedRoadDashArray, restrictedRoadOpacity and restrictedRoadLineWidth. These options are used when creating the layer that contains the restricted line and correspond to the dash line layer options of the Maps SDK. See the Maps SDK line layer documentation for further explanation of these options.

You can configure the color of the dashed line representing restricted roads using the RouteLineColorResources class to set the restrictedRoadColor and alternativeRouteRestrictedRoadColor options.

val customColorResources = RouteLineColorResources.Builder()
    .restrictedRoadColor(Color.parseColor("#FFCC00"))
    .alternativeRouteRestrictedRoadColor(Color.parseColor("#FFCC00"))
    .build()

Display soft gradient for traffic

Traffic congestion is represented on the route line by different colors. By default there is an abrupt transition from one color to the next. The MapboxRouteLineOptions class includes an option to display a gradient transition between the colors instead.

There is also an option to influence the length of the gradient transition. A higher value will result in a gradient over a longer length. A smaller value will result in a gradient that is shorter in length. In general, the softGradientTransition option should be a value between 5.0 and 75.0.

val routeLineOptions = MapboxRouteLineOptions()
    .displaySoftGradientForTraffic(true)
    .softGradientTransition(30.0)
    .build()