Mapbox Navigation Viewport Data Source
Default implementation of ViewportDataSource to use with the NavigationCamera.
Use:
onRouteChanged to produce overview geometries that need to be framed
onRouteProgressChanged (requires also onRouteChanged) to produce following geometries of the current step and overview geometries of the remaining points on the route that need to be framed. This will make the frame in following mode change zoom level depending on the proximity to the upcoming maneuver and resize the frame in overview mode to fit only the remaining portion of the route.
onLocationChanged to pass a point to be framed and used as a source of bearing for the following camera frame
additionalPointsToFrameForFollowing - points that also need to be visible in the following camera frame
additionalPointsToFrameForOverview - points that also need to be visible in the overview camera frame
Whenever a set of these arguments is provided or refreshed, you need to call evaluate to process the data and compute an opinionated ViewportData update that NavigationCamera observes and applies.
Based on the provided data, the class will make decisions on how the camera should be framed. However, that might not always match with your expectations or needs. Let’s imagine that you would like to temporarily zoom in (when the user double-tapped the map) or change the camera’s bearing to focus on a POI. To serve those use-cases, all of the camera property values that this data source produces can be overridden. The source will keep producing the default, opinionated values, but as long as the override is present, they won’t be used. Passing null
as an override resets it to the default value.
The class also offers various mutable options that can be modified at any point in time to influence the style of frames that are produced. Make sure to get familiar with all of the public nested fields and their documentation in MapboxNavigationViewportDataSourceOptions. The options can be mutated by accessing options field.
Whenever any changes are made to the data source (new values provided, overrides added/removed, or options changed), remember to call evaluate to recompute frames and notify observers.
Padding and framing behavior
This data source initializes at the null island
(0.0, 0.0). Make sure to first provide at least onLocationChanged for following mode framing and onRouteChanged for overview mode framing (or the additionalPointsToFrameForOverview and additionalPointsToFrameForFollowing).
Overview
overviewPadding is used to generate the correct zoom level while positioning the contents on screen and is also applied to the resulting default ViewportData.cameraForOverview. The default bearing for overview framing is north (0.0
).
Following
followingPadding is used to generate the correct zoom level while positioning the contents on screen but the padding value is not applied the ViewportData.cameraForFollowing. Instead, the frame contains a specific CameraOptions.padding value that manipulates the vanishing point of the camera to provide a better experience for end users when the camera is pitched.
This vanishing point change cannot be recovered from automatically without impacting the camera position. That's why, if you use the MapboxNavigationViewportDataSource, you should explicitly define CameraOptions.padding in all other transition that your app is running. The side-effect of not recovering from the vanishing point change can be the center of the camera that's offset from the center of the MapView.
When following frame is used, 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's location provided via onLocationChanged, if available. This can be influenced by FollowingFrameOptions.maximizeViewableGeometryWhenPitchZero when there are at least 2 points available for framing.
The geometries that are below the bottom edge of the following padding on screen (based on camera's bearing) are ignored and not being framed. It's impossible to find a zoom level that would fit geometries that are below the vanishing point of the camera, since the vanishing point is placed at the bottom edge of the provided padding.
The default pitch for following frames is FollowingFrameOptions.defaultPitch and zoom is determined based on upcoming geometries or FollowingFrameOptions.maxZoom.
Debugging
This feature is currently experimental an subject to change.
You can use debugger to provide a MapboxNavigationViewportDataSourceDebugger instance which will draw various info on the screen when the NavigationCamera operates to together with the MapboxNavigationViewportDataSource.
Make sure to also provide the same instance to NavigationCamera.debugger.
Examples
Show route overview with padding
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 {
navigationCamera.clearRouteData()
}
}
}
Update current location
private val locationObserver = object : LocationObserver {
override fun onNewRawLocation(rawLocation: Location) {
// no impl
}
override fun onNewLocationMatcherResult(locationMatcherResult: LocationMatcherResult) {
viewportDataSource.onLocationChanged(locationMatcherResult.enhancedLocation)
viewportDataSource.evaluate()
}
}
Update route progress
private val routeProgressObserver = object : RouteProgressObserver {
override fun onRouteProgressChanged(routeProgress: RouteProgress) {
viewportDataSource.onRouteProgressChanged(routeProgress)
viewportDataSource.evaluate()
}
}
Request state to following with padding
viewportDataSource.onLocationChanged(enhancedLocation)
viewportDataSource.followingPadding = followingEdgeInsets
viewportDataSource.evaluate()
navigationCamera.requestNavigationCameraToFollowing()
Update current location and reset frame
private val locationObserver = object : LocationObserver {
override fun onNewRawLocation(rawLocation: Location) {}
override fun onNewLocationMatcherResult(locationMatcherResult: LocationMatcherResult) {
viewportDataSource.onLocationChanged(locationMatcherResult.enhancedLocation)
viewportDataSource.evaluate()
if (locationMatcherResult.isTeleport) {
navigationCamera.resetFrame()
}
}
}
Run your own animation to a POI
private fun animateToPOI() {
// request camera to idle first or use `NavigationBasicGesturesHandler` or `NavigationScaleGestureHandler`
mapView.camera.flyTo(
CameraOptions.Builder()
.padding(edgeInsets)
.center(point)
.bearing(0.0)
.zoom(14.0)
.pitch(0.0)
.build(),
MapAnimationOptions.mapAnimationOptions {
duration(1000L)
}
)
}