-
Notifications
You must be signed in to change notification settings - Fork 319
2.0 Navigation SDK Migration Guide
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.
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-v2InstructionView
. -
com.mapbox.navigation:ui-tripprogress
offers the trip progress view and APIs that replace the pre-v2SummaryBottomSheet
. -
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.
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.
- https://kotlinlang.org/docs/reference/java-interop.html#calling-java-code-from-kotlin and https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html have helpful information on Java and Kotlin interoperability.
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.
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
.
The NavigationOptions
class now contains all the right defaults out of the box, so the MapboxNavigation#defaultNavigationOptionsBuilder
has been removed.
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.
- 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 folderAPP_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.
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.
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
orroute
as an input and returns a list ofManeuver
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 givenRouteLeg
.
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.
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 anActivity
orFragment
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.
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 theMapboxRouteLineApi
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.
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 recognition is performed by the MapRouteLineApi#findClosestRoute
which also takes padding as an argument.
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.
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 theMapboxRouteArrowApi
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.
Mapbox Maps SDK v10 removes the CameraMode
s 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 CameraMode
s 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 CameraMode
s.
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
.
viewportDataSource = MapboxNavigationViewportDataSource(mapView.getMapboxMap())
navigationCamera = NavigationCamera(
mapView.getMapboxMap(),
mapView.getCameraAnimationsPlugin(),
viewportDataSource
)
locationComponent.setCameraMode(cameraMode)
This would transition to the last known location sample that the LocationComponent
knew (or do nothing if not available).
First, provide the MapboxNavigationViewportDataSource
with location data:
viewportDataSource.onLocationChanged(location)
viewportDataSource.evaluate()
The location can be obtained from the navigation's LocationObserver
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()
Any location update pushed to the LocationComponent
after setting the CameraMode
would update the camera.
Update the viewport data source:
viewportDataSource.onLocationChanged(location)
viewportDataSource.evaluate()
which will trigger a camera update when NavigationCamera
is in a FOLLOWING
state.
Tracking the users' location while having the camera pointing to the north was a separate state:
locationComponent.setCameraMode(CameraMode.TRACKING_GPS_NORTH)
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()
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.
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, 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()
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)
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()
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.
Both the legacy CameraMode
s 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.
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.
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 theNavigationCamera
toIDLE
whenever any outside transition or gesture interaction happens. -
NavigationScaleGestureHandler
which requests theNavigationCamera
toIDLE
whenever any outside transition happens but allows for executing navigation gestures described above, same asLocationComponentOptions#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() }
)
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.
The CameraMode
s 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.
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()
}
}
CameraMode
s 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.
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.
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.
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 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 toMapboxVoiceInstructionsPlayer
text-to-speech engine to be played. -
MapboxVoiceInstructionsPlayer: Text-to-speech engine implementation. Internally, this uses
VoiceInstructionsFilePlayer
orVoiceInstructionsTextPlayer
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.
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 aList<QueriedFeature>
if thePoint
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.
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 viaMapboxSpeedLimitApi
.
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.