Skip to content

2.0 Navigation SDK Migration Guide

Pablo Guardiola edited this page Oct 20, 2021 · 44 revisions

Read me first (1): Navigation SDK v2 upgrades to Mapbox Maps SDK v10. Maps SDK v10 offers 3D maps, improved performance, and Metal support (on iOS). It's also a SEMVER major release with breaking API changes. Please make sure you read the Maps SDK migration guide before reading the navigation-specific content below.

Read me first (2): This is a migration guide that doesn't intend to cover general Navigation SDK usage. For higher-level tutorials and code snippets, please check Navigation SDK's public documentation. An examples repository is also available to get yourself familiar with the new API's and how to use them.


Artifact name changes

The Navigation SDK is now available under one Maven artifact - com.mapbox.navigation:android:{version}.

However, in addition to the one grouping artifact there are also multiple granular ones that you can pick-and-choose if you’re not planning to use all of the features and the binary size of your application is a consideration:

  • com.mapbox.navigation:core offers all the core localization features from pre-v2.
  • com.mapbox.navigation:ui-maps offers the route line and arrow APIs, navigation camera, building highlight and extrusion, extended visual guidance and other tools and features that integrate with the Mapbox Maps SDK.
  • com.mapbox.navigation:ui-maneuver offers the maneuver view and its APIs that replace the pre-v2 InstructionView.
  • com.mapbox.navigation:ui-tripprogress offers the trip progress view and APIs that replace the pre-v2 SummaryBottomSheet.
  • com.mapbox.navigation:ui-voice offers all necessary APIs to play voice instructions.
  • com.mapbox.navigation:ui-speedlimit offers a view and APIs to display speed limits.

Kotlin:

The Navigation SDK v2 is written 100% in Kotlin. This is a change from how the pre-v2 versions were a mix of Java and Kotlin.

About the v2 Navigation Core Components:

Core localization components in the Navigation SDK v2 remain architecturally mostly unchanged but introduce a couple of breaking changes that improve the experience or clarify the behavior of the SDK to streamline the integration.

Requesting a route

Changed MapboxNavigation#requestRoutes to not automatically set the result as the primary route for the navigation experience. When calling MapboxNavigation#requestRoutes make sure to also call MapboxNavigation#setRoutes with a result. This change allows for dispatching and managing multiple route requests concurrently, including canceling with MapboxNavigation#cancelRouteRequest.

NavigationOptions changes

Defaults

The NavigationOptions class now contains all the right defaults out of the box, so the MapboxNavigation#defaultNavigationOptionsBuilder has been removed.

Routing tiles configuration (pre-v2 OnboardRouterOptions)

OnbourdRouterOptions were renamed to RoutingTilesOptions to better reflect the purpose. If you are using a custom base URI, dataset, or version of the tiles, those are now separate fields in the builder rather than a single URI path.

Predictive Caching

  • When migrating please ensure you have cleaned up the old navigation tiles cache folder to reclaim disk space. Navigation SDK 2.0 caches navigation tiles in a default folder under APP_FOLDER/mbx_nav/tiles/api.mapbox.com. Previous versions of Nav SDK used to cache tiles under a default folder APP_FOLDER/Offline/api.mapbox.com/$tilesVersion/tiles. The old cache is not compatible with a new version of SDK 2.0. It makes sense to delete any folders used previously for caching including a default one.
  • OnboardRouterOptions enabled you to specify a path where nav-tiles will be saved and if a custom directory was used, it should be cleared as well.

About the v2 Navigation UI Components:

Unlike in v1, where the views were exposed with no access to data whatsoever, the v2 UI components now focus on separating the data transformation logic (preparing for presentation) from the actual presentation layer that updates the app’s UI elements. You can now access API's specially catered to give you access to the data, prepared using raw data points from core modules. Based on the data retrieved from these API's, you can choose to render it either on a Mapbox designed View or your on custom view. This separation might require additional synchronization on the app level, but scales better with non-trivial apps. This returns the control over the UI components’ lifecycle back to developers who can now better fit them into more complex setups. This is also true for UI components that interact directly with the Mapbox Map - the presentation data is prepared separately from the Map itself, and the updates can be dispatched by the developer at the correct time, depending on the Map’s placement in the view hierarchy and its lifecycle.

New UI Components

Maneuvers

Maneuvers is the v2 version of the InstructionView. Maneuvers has the following components that are responsible for updating the current maneuver instructions with banner related data.

  • MapboxManeuverApi: The API takes routeProgress or route as an input and returns a list of Maneuver that can be rendered onto the screen.
  • MapboxPrimaryManeuver: This is an android TextView that renders primary banner instructions.
  • MapboxSecondaryManeuver: This is an android TextView that renders secondary banner instructions.
  • MapboxSubManeuver: This is an android TextView that renders sub banner instructions.
  • MapboxLaneGuidance: This is an android ImageView that renders the upcoming lanes for banner instructions.
  • MapboxTurnIconManeuver: This is an android ImageView that renders the turn icons for current banner instructions.
  • MapboxStepDistance: This is an android TextView that can be used to render the step distance remaining and total step distance for current banner instruction.
  • MapboxManeuverView: This is a UI implementation that combines all of the views above to draw a comprehensive turn by tun instruction based on current and upcoming instructions.
  • MapboxUpcomingManeuverAdapter: - Analogous to pre v2 InstructionList, this adapter renders all the instructions for a given RouteLeg.

MapboxManeuverView allows you to quickly integrate the maneuver instructions in your app. For more customized requirements, you can do any of the following:

  • Use any of these views individually and position them anywhere on the screen; or
  • Instantiate and invoke MapboxManeuverApi to retrieve the data and render it on your custom maneuver view.

To understand more about the API and View look at the detailed example.

Trip Progress

The Navigation SDK v2 replaces the SummaryBottomSheet with a trip progress component. Trip progress has the following components that are responsible for updating trip related data on to the screen.

  • MapboxTripProgressApi: The Api is responsible for consuming RouteProgress objects and returning a state object holding data related to the trip.
  • MapboxTripProgressView: This is an android View that can be added to an Activity or Fragment layout and is responsible for updating the view components with trip-related data.

MapboxTripProgressView allows you to quickly integrate trip-related data in your app. For more customized requirements you can use the MapboxTripProgressApi to retrieve the data and render it on your custom trip progress view.

To understand more about the API and View look at the detailed example.

Route Line

The Navigation SDK v2 changes the interaction with the route line component. In pre-v2 interaction was wrapped in the NavigationMapRoute component but in v2 it is interacted with directly. The NavigationMapRoute class does not exist in version v2. Utilizing the route line component involves the following:

  • RouteLineResources: Defines configurable properties associated with the route line.
  • RouteLineColorResources: Contains colors and other values used to determine the appearance of the route line.
  • MapboxRouteLineApi: Processes input and outputs a data structure describing the mutations that will alter the appearance of the route line on the map.
  • MapboxRouteLineView: Consumes the data structure outputted by the MapboxRouteLineApi and renders the map-related mutations.
  • MapboxRouteLineOptions: Contains options for the configuration of the route line.

To draw a route(s) on the map pass one or more DirectionsRoute(s) to the MapboxRouteLineApi's setRoutes() method and pass the result of that call to the render method of MapboxRouteLineView. The MapboxRouteLineApi class will create data describing the map mutations but the mutations will not take place until the MapboxRouteLineView's render method is called with that data. It's also important to inform the MapboxRouteLineApi when the route changes. One reason a new route could be generated is as a result of a reroute event. One way to update the route line on the map as a result of a reroute event is to register a RoutesObserver with MapboxNavigation and check for a change in the route and if detected update the MapboxRouteLineApi, rendering the result.

MapRouteLineInitializedCallback

The MapRouteLineInitializedCallback is not available anymore. As soon as you call MapboxRouteLineView#render, the layers are added to the map synchronously, so this is the point in time after which you can reference them for z-positioning other runtime layers. You can check RouteConstants for layer IDs. Also, you may call MapboxRouteLineView#initializeLayers in advance to initialize the layers manually.

Alternative route selection and padding

Alternative route selection recognition is performed by the MapRouteLineApi#findClosestRoute which also takes padding as an argument.

Vanishing Route Line

The vanishing route line feature can be enabled to change the color of the route line behind the puck during active navigation. Enable this feature in the MapboxRouteLineOptions. The color that appears behind the puck can be customized with the RouteLineColorResources::routeLineTraveledColor parameter.

To understand more about the API and View look at the detailed example.

Route Arrow

In pre-v2 the route arrow was also wrapped in the NavigationMapRoute class but in v2 it is interacted with directly. The route arrow is updated by RouteProgress events and uses the data to determine the placement of the arrow on the route line. Utilizing the route arrow component involves the following:

  • RouteArrowOptions: Options for determining the appearance of route arrow(s)
  • MapboxRouteArrowApi: Processes input and outputs a data structure describing the mutations that will alter the appearance of the maneuver arrow on the map.
  • MapboxRouteArrowView: Consumes the data structure outputted by the MapboxRouteArrowApi and renders the map-related mutations.

With v2.0, you can also show and hide route arrow, add more than one arrow on the route line using MapboxRouteArrowApi which wasn't available in pre-v2 version of the Nav SDK.

To understand more about the API and View look at the detailed example.

Navigation Camera

Mapbox Maps SDK v10 removes the CameraModes that were part of the LocationComponent and used to order the map camera to follow the location puck. Now, the concepts of updating the puck’s position and updating the camera’s position are completely decoupled.

With Maps SDK v10, all camera properties can be manipulated independently and in parallel, so there should be no limits as to how you can build a custom system that tracks the puck’s position in navigation scenarios. This wasn’t the case with Maps SDK v9 and earlier that was the cause for the introduction of CameraModes that aren’t needed anymore.

That said, we still want to make it easy to integrate a camera system that tracks the puck’s location and this is where NavigationCamera and the concept of the ViewportDataSource are introduced.

NavigationCamera is a class that tries to simplify management of the Map's camera object in typical navigation scenarios, maintains a state (IDLE/FOLLOWING/OVERVIEW), and executes camera transitions. NavigationCamera does not produce any camera position values, the positions that the camera should transition to are generated by the ViewportDataSource interface. Mapbox also provides a default MapboxNavigationViewportDataSource implementation that produces opinionated camera positions based on the inputted data.

The new NavigationCamera also replaces the legacy Navigation SDK implementations of DynamicCamera and SimpleCamera that were wrappers on top of the Maps SDK CameraModes.

The below guide will try to walk you through a couple of LocationComponent camera usage examples and how they can be recreated or improved with the NavigationCamera and the MapboxNavigationViewportDataSource.

Initialize camera

viewportDataSource = MapboxNavigationViewportDataSource(mapView.getMapboxMap())
navigationCamera = NavigationCamera(
    mapView.getMapboxMap(),
    mapView.getCameraAnimationsPlugin(),
    viewportDataSource
)

Starting to track the users' location

Before
locationComponent.setCameraMode(cameraMode)

This would transition to the last known location sample that the LocationComponent knew (or do nothing if not available).

Now

First, provide the MapboxNavigationViewportDataSource with location data:

viewportDataSource.onLocationChanged(location)
viewportDataSource.evaluate()

The location can be obtained from the navigation's LocationObserver or MapMatcherResultObserver if the trip session has already been started (MapboxNavigation#startTripSession), or from any LocationEngine instance. You can also fetch the initial data directly from the LocationComponentPlugin's LocationProvider.

Then, initialize the FOLLOWING state:

navigationCamera.requestNavigationCameraToFollowing()

Tracking users' location with bearing matching location

Before

Any location update pushed to the LocationComponent after setting the CameraMode would update the camera.

Now

Update the viewport data source:

viewportDataSource.onLocationChanged(location)
viewportDataSource.evaluate()

which will trigger a camera update when NavigationCamera is in a FOLLOWING state.

Tracking users' location with locked bearing

Before

Tracking the users' location while having the camera pointing to the north was a separate state:

locationComponent.setCameraMode(CameraMode.TRACKING_GPS_NORTH)
Now

To detach the camera's bearing from the location, use the property overrides:

viewportDataSource.followingBearingPropertyOverride(0.0)
viewportDataSource.evaluate()

When the override is set, the MapboxNavigationViewportDataSource will not change the value of the overridden property. This means that now you can not only lock the camera's bearing to the north while tracking but also to any other value.

To reset back to location bearing matching, clear the override:

viewportDataSource.followingBearingPropertyOverride(null)
viewportDataSource.evaluate()

Changing the zoom level, pitch, or padding

Zoom level, pitch, and padding manipulation operations have the same before and now changes, only the property name changes. Let's take zoom as an example.

Before

The zoom level could be changed via the locationComponent.zoomWhileTracking tracking method. The method only worked if CameraMode was already set to one of the tracking modes, otherwise, the request was lost.

The zoom level could also be changed by overriding the com.mapbox.navigation.ui.camera.Camera#zoom method and recomputing the value that Navigation SDK requested.

Now

Now, the zoom level control can either be left to the MapboxNavigationViewportDataSource which will have a constant zoom level when in a free-drive mode (when there is no route present) and a dynamic zoom level when we're in an active guidance session, or overridden and controlled manually.

To let the default viewport data source manage the zoom when in an active guidance session, provide an active route via the:

viewportDataSource.onRouteChanged(route)

often used with the RoutesObserver:

private val routesObserver = object : RoutesObserver {
    override fun onRoutesChanged(routes: List<DirectionsRoute>) {
        if (routes.isNotEmpty()) {
            viewportDataSource.onRouteChanged(routes.first())
            viewportDataSource.evaluate()
        } else {
            viewportDataSource.clearRouteData()
            viewportDataSource.evaluate()
        }
    }
}

and provide the RouteProgress via:

viewportDataSource.onRouteProgressChanged(routeProgress)

often used with the RouteProgressObserver:

private val routeProgressObserver = object : RouteProgressObserver {
    override fun onRouteProgressChanged(routeProgress: RouteProgress) {
        viewportDataSource.onRouteProgressChanged(routeProgress)
        viewportDataSource.evaluate()
    }
}

When a route and progress are available, the viewport data source will always try to produce camera positions that best frame the upcoming maneuvers together with the user's location from viewportDataSource.onLocationChanged(location).

But you can also always manually control the zoom level by overriding the zoom property:

viewportDataSource.followingZoomPropertyOverride(16.5)
viewportDataSource.evaluate()
Additional route state notes

Remember to call viewportDataSource.clearRouteData() whenever the active route is cleared to prevent the default viewport data source from trying to frame the route's geometry that's not visible anymore. For example:

val routesObserver = object : RoutesObserver {
    override fun onRoutesChanged(routes: List<DirectionsRoute>) {
        if (routes.isNotEmpty()) {
            viewportDataSource.onRouteChanged(routes.first())
            viewportDataSource.evaluate()
        } else {
            viewportDataSource.clearRouteData()
            viewportDataSource.evaluate()
        }
    }
}
mapboxNavigation.registerRoutesObserver(routesObserver)
Additional padding notes

The default MapboxNavigationViewportDataSource does not apply any padding. You can apply the padding for framed geometries with:

viewportDataSource.followingPadding(padding)
viewportDataSource.evaluate()

and

viewportDataSource.overviewPadding(padding)
viewportDataSource.evaluate()
Controlling puck's position on screen

When in the FOLLOWING state, the first point of the framed geometry list will be placed at the bottom edge of this padding, centered horizontally. This typically refers to the user location provided by the viewportDataSource.onLocationChanged(location), if available.

Controlling the position of the puck on screen does not happen via the map's padding value anymore, but by the padding provided to the viewportDataSource.followingPadding. Make sure to read the inline documentation of the property.

Camera transition collisions and navigation gestures management

Both the legacy CameraModes and the new NavigationCamera assume full ownership of the camera object while not idle. This means that by design, any outside transition (like MapboxMap#easeTo and others) would compete with the internal transitions of the navigation camera features.

Before

Whenever any other transition was scheduled CameraMode would fallback to NONE but you could use the LocationComponentOptions#trackingGesturesManagement to prevent gestures like double-tap to zoom in, two-tap to zoom out, or quick sale from breaking the tracking state.

Now

By default, the NavigationCamera only falls back to the IDLE state when another transition interrupts the navigation camera's scheduled transition. However, in practice, developers should use the CameraAnimationsLifecycleListener to avoid competing transitions.

Mapbox provides 2 default implementations of that interface:

  • NavigationBasicGesturesHandler which requests the NavigationCamera to IDLE whenever any outside transition or gesture interaction happens.
  • NavigationScaleGestureHandler which requests the NavigationCamera to IDLE whenever any outside transition happens but allows for executing navigation gestures described above, same as LocationComponentOptions#trackingGesturesManagement did in the past.

You can enable those implementations by registering the lifecycle listeners:

mapView.getCameraAnimationsPlugin().addCameraAnimationsLifecycleListener(
    NavigationScaleGestureHandler(
        context,
        navigationCamera,
        mapView.getMapboxMap(),
        mapView.gestures,
        mapView.location,
        object : NavigationScaleGestureActionListener {
            override fun onNavigationScaleGestureAction() {
                viewportDataSource.options.followingFrameOptions.zoomUpdatesAllowed = false
            }
        }
    ).apply { initialize() }
)
Note about managing zoom level updates after gesture interaction

When an allowed scale gesture interaction happens and the zoom level changes, the navigation camera would try to reset that zoom level back to the opinionated value computed via the default viewport data source provider as soon as another MapboxNavigationViewportDataSource#evaluate call is made. To prevent that, notice the:

object : NavigationScaleGestureActionListener {
    override fun onNavigationScaleGestureAction() {
        viewportDataSource.options.followingFrameOptions.zoomUpdatesAllowed = false
    }
}

above. This prevents the viewport data source from producing any zoom values after the gesture interaction happens. Reset the followingFrameOptions.zoomUpdatesAllowed value whenever appropriate to restore the dynamic (or overridden) zoom level updates.

Route overview

Before

The CameraModes did not offer any means of showing the overview of the route in the context of the position of the puck.

The legacy Navigation SDK APIs did have a concept of an overview, but it also did not take into account the current user's location.

Now

NavigationCamera offers an OVERVIEW state available via navigationCamera.requestNavigationCameraToOverview(). When paired with the MapboxNavigationViewportDataSource, the overview state will always frame the whole route, or if the RouteProgress is also being provided it will frame the remainder of the route. Here's how an overview with padding can be requested:

private val routesObserver = object : RoutesObserver {
    override fun onRoutesChanged(routes: List<DirectionsRoute>) {
        if (routes.isNotEmpty()) {
            viewportDataSource.onRouteChanged(routes.first())
            viewportDataSource.overviewPadding = overviewEdgeInsets
            viewportDataSource.evaluate()
            navigationCamera.requestNavigationCameraToOverview()
        } else {
            viewportDataSource.clearRouteData()
            viewportDataSource.evaluate()
        }
    }
}

Then, on each RouteProgress update, the camera position will be recalculated to only frame the remainder of the route:

private val routeProgressObserver = object : RouteProgressObserver {
    override fun onRouteProgressChanged(routeProgress: RouteProgress) {
        viewportDataSource.onRouteProgressChanged(routeProgress)
        viewportDataSource.evaluate()
    }
}

Framing other points

Before

CameraModes did not allow for framing any arbitrary points in the camera's frame. We had to rely on the location and zoom level to manipulate what's visible on the map.

Now

The MapboxNavigationViewportDataSource allows for passing an arbitrary list of points and the API will make sure that all of those points are visible in the frame together with the provided location and route (if present), as long as the zoom level property is not overridden. Additional points that you'd like to be captured in the camera's frame can be passed via:

viewportDataSource.additionalPointsToFrameForFollowing(points)
viewportDataSource.evaluate()

This can be powerful when paired with bearing manipulation, like:

val center = mapboxMap.cameraState.center
viewportDataSource.additionalPointsToFrameForFollowing(listOf(lookAtPoint))
viewportDataSource.followingBearingPropertyOverride(
    TurfMeasurement.bearing(center, lookAtPoint)
)
viewportDataSource.evaluate()

In the active guidance, the above operation would frame the current location, the upcoming maneuver, the lookAtPoint but also changes the bearing of the camera to head towards that new point.

Options

Make sure to browse through the MapboxNavigationViewportDataSource.options and read about customization to the generated camera positions and how you can tweak the algorithms and change the user experience.

Mapbox Electronic Horizon

The electronic horizon API was changed. You can use both callbacks and direct calls to get the electronic horizon data. With callbacks you get only the most important data, which can be more efficient and save resources. If you need additional data (for example: edge metadata, edge shape, road object metadata, etc.) you can make a direct call.

Check docs to get more details.

Voice

Voice is the v2 version of the NavigationSpeechPlayer. Voice has the following components that are responsible for playing timely and detailed voice instructions.

  • MapboxSpeechApi: This allows you to generate an announcement based on VoiceInstructions objects and returns a state object (that includes the announcement to be played when the announcement is ready or an error and a fallback with the raw announcement) that should be passed to MapboxVoiceInstructionsPlayer text-to-speech engine to be played.
  • MapboxVoiceInstructionsPlayer: Text-to-speech engine implementation. Internally, this uses VoiceInstructionsFilePlayer or VoiceInstructionsTextPlayer speech players, if a synthesized speech mp3 is provided or not, respectively.

Noting that currently there's no replacement for VoiceInstructionLoader. Tracking that work in https://github.com/mapbox/mapbox-navigation-android/issues/4208.

Buildings

Mapbox Navigation SDK v2 provide options to highlight a particular building footprint Polygon or extrude a footprint as a 3D building shape. As compared to pre-v2, BuildingExtrusionHighlightLayer and BuildingFootprintHighlightLayer don't exist anymore. Instead v2 introduces the following components:

  • MapboxBuildingsApi: The Api helps in querying the building as a Feature and returns a List<QueriedFeature> if the Point associated is a location point.
  • MapboxBuildingView: This is a custom android View that renders the List by highlighting the footprint and extrusion.
  • MapboxBuildingHighlightOptions: Defines options to configure the color and opacity of the building highlights.

MapboxBuildingView allows you to quickly integrate building-related data in your app. For more customized requirements you can use the MapboxBuildingsApi to retrieve the data and render it on your custom building view.

To understand more about the API and View look at the detailed example.

SpeedLimits

Speed Limit is the v2 version of the SpeedLimitView. Speed limit has the following components that are responsible for updating speed related data on a view:

  • MapboxSpeedLimitApi: The Api makes use of speed annotation to return the speed related data that can be rendered on a view.
  • MapboxSpeedLimitView: This is an android TextView that renders the speed data obtained via MapboxSpeedLimitApi.

MapboxSpeedLimitView allows you to quickly integrate speed-related data in your app. For more customized requirements you can use the MapboxSpeedLimitApi to retrieve the data and render it on your custom speed limit view.

To understand more about the API and View look at the detailed example.