メインコンテンツまでスキップ

Coordination Layer

The Coordination Layer is an experimental module that helps you build a navigation experience using customizable high-level components.

As compared to classic NavSDK, the Coordination Layer offers multiple advantages:

  1. Its API is higher-level, consisting of multiple customizable building blocks that implement certain scenarios (like active navigation or routes preview), while giving the app full control of entering and exiting these scenarios and the ability to configure them.
  2. It provides better performance and synchronization between the components (route line, camera and location indicator): the Coordination Layer is able to achieve it because it has better control of the components as opposed to classic NavSDK.
  1. Ability to use HD Lite experience with active navigation. In HD Lite experience the route line and the location indicator are aligned with HD roads, elevated intersections and tunnels.

See the Releases page for detailed release notes.

SDK Installation

Get access to Coordination Layer
The Coordination Layer is a private module. Contact us to request access.

1. Configure credentials and set up repositories

To configure credentials refer to the Get started page and to initialize the SDK check the Initialization guide.

2. Add the dependency

Add the library dependency to your build.gradle. The Coordination Layer module version matches the version of all other Navigation SDK modules and they have the same release cadence. To ensure version compatibility, make sure your Coordination Layer module version matches the version of the other Navigation SDK modules in your project.

build.gradle
dependencies {
implementation "com.mapbox.navigationcore:coordination:3.25.0-rc.1"
}
Note
The Coordination Layer artifact is available starting from version 3.7.0-rc.1.

Possible issues

Depending on the versions of kotlin-stdlib, kotlin-stdlib-jdk7, kotlin-stdlib-jdk8 that you use in your project, you might run into the issue that has a similar error message:

Duplicate class kotlin.text.jdk8.RegexExtensionsJDK8Kt found in modules kotlin-stdlib-x.y.z.jar and kotlin-stdlib-jdk8-x.y.z.jar.

To fix this, exclude the dependencies by adding the following lines into your module's build.gradle:

build.gradle
configurations.all {
resolutionStrategy {
exclude group: "org.jetbrains.kotlin", module: "kotlin-stdlib-jdk8"
exclude group: "org.jetbrains.kotlin", module: "kotlin-stdlib-jdk7"
}
}

Responsibilities of the Coordination Layer

The Coordination Layer's purpose is to simplify the development of basic navigation scenarios, while still being highly customizable.

Currently supported scenarios are active navigation and routes preview.

Active navigation

Responsibilities:

  1. Control the presentation of the navigation scenario.
  2. Display current vehicle location on the map, animate its movement.
  3. Display navigation routes on the map and erase the traveled part as the vehicle moves.
  4. Display waypoint markers and route arrows.
  5. Handle clicks on alternative routes and automatically switch to the selected alternative.
  6. Implement navigation camera modes to either follow current vehicle location or overview current route.
  7. Support HD Lite mode.
  8. Automatically switch between SD and HD Lite modes based on internal factors (coverage availability, etc.).
  9. Allow to configure visibility and presentation options of location indicator, routes and waypoint markers.

Routes preview

Note: RoutesPreviewPresenter is available starting from NavSDK 3.18.0-beta.1.

Responsibilities:

  1. Control the presentation of the routes preview scenario.
  2. Display preview routes on the map.
  3. Display waypoint markers.
  4. Handle clicks on alternative routes and automatically switch to the selected alternative.
  5. Overview previewed routes with camera.
  6. Allow to configure visibility and presentation options routes and waypoint markers.
  7. Promote routes to the navigator (start active guidance by moving them from preview presenter to MapboxNavigation and seamlessly transfer the route line to the navigation presenter).
Note
The Coordination Layer is an alternative simplified way to implement the functionalities listed above. All the other features (requesting routes, loading map style, etc.) should be implemented using the regular NavSDK API.

Integrate The Coordination Layer module

Experimental API

The Coordination Layer API is in a preview state and has a high chance of being changed in the future.

The Coordination Layer has 3 main entities:

  1. PresentersNavigationContext
  2. PresentersScreenContext
  3. Presenter (there are different types of presenters, currently supported ones are: NavigationPresenter and RoutesPreviewPresenter.

PresentersNavigationContext

PresentersNavigationContext is an entity that holds and interacts with MapboxNavigation instance. It should be created once per MapboxNavigation instance and reused for multiple presenters (see Retrieve instances section).

It can be used to configure and access HD Lite specific navigation options like predictive cache and HD route object matcher.

PresentersNavigationContext is required to create instances of NavigationPresenter and RoutesPreviewPresenter. The way you create it depends on whether you use MapboxNavigation directly or create it via MapboxNavigationApp (see Retrieve instances section for details):

PresentersScreenContext

PresentersScreenContext is an entity that holds at most one MapView. If you have multiple MapView instances at the same time (for example, 2 different screens with a map), each one should have its own PresentersScreenContext. If MapView is re-created due to a configuration change, you can use the same PresentersScreenContext instance and update the MapView it holds via PresentersScreenContext#setMap method.

PresentersScreenContext holds a PresentersCameraManager that can be used to activate or deactivate camera from a specific presenter (see PresentersCameraManager section.)

PresentersScreenContext is required to create instances of NavigationPresenter and RoutesPreviewPresenter.

Presenters

Presenter is an entity that manages a single UI composition consisting of a map and its visual components within a single scenario (use case). Currently 2 scenarios are supported: active navigation and routes preview. For active navigation scenario, you should use NavigationPresenter (see NavigationPresenter section). For routes preview scenario - RoutesPreviewPresenter (see RoutesPreviewPresenter section).

You may choose to only implement the active navigation scenario, in this case you don't have to create a RoutesPreviewPresenter instance at all. Another possibility is to use both: while the user plans the trip, you activate RoutesPreviewPresenter and when they press a "Start navigation" button, you deactivate RoutesPreviewPresenter and activate NavigationPresenter. Read the sections below for the detailed description of each presenter.

Overview

NavigationPresenter is the presenter to use in the active navigation use case. It provides a high-level interface to configure presentation options and observe the navigation state.

Key features:

Each NavigationPresenter holds its own state and can be configured independently. You can create multiple presenters for different UI compositions (e.g., cluster display and head unit) that share the same navigation context.

Configuration

All the possible configuration options are listed in the NavigationPresenterConfig. You can pass the configuration on initial presenter creation:

val presenter: NavigationPresenter = NavigationPresenter.create(
screenContext = screenContext,
navigationContext = navigationContext,
) {
routeLine {
vanishingRouteLineStyle(VanishingRouteLineStyle.TRAVELED_COLORS)
routeLineColorResources(
RouteLineColorResources.Builder()
.routeLineTraveledColor(Color.GRAY)
.routeLineTraveledCasingColor(Color.DKGRAY)
.build(),
)
callouts {
visible(true)
}
}
locationIndicator {
sd(
config2D {
bearingImage = ImageHolder.from(R.drawable.mapbox_navigation_puck_icon)
}
)
hdLite(
config3D {
uri = "asset://3d-location-indicator.glb"
scale = ScaleValue(listOf(40.0, 40.0, 40.0))
}
)
}
camera {
following {
padding(EdgeInsets(200.0, 200.0, 200.0, 200.0))
}
overview {
overviewAlternativeRoutes(true)
}
}
}

and you can re-configure it at any point at runtime:

presenter.configure {
navigationQuality(NavigationDefinitionQuality.HD_LITE)
}

NavigationPresenterCamera is a sub-controller of NavigationPresenter that controls the camera.

The NavigationPresenterCamera supports camera handling for navigation and route overview modes. Note that for routes preview case there is a separate RoutesPreviewPresenterCamera (see RoutesPreviewPresenter section). You can configure camera options using NavigationPresenter#configure:

presenter.configure {
camera {
following {
padding(EdgeInsets(200.0, 200.0, 200.0, 200.0))
focalPoint(FollowingFrameOptions.FocalPoint(0.5, 0.8))
}
}
}

Full list of configuration options can be found in CameraConfig.

You can also control the mode the camera is in.

Set camera mode

NavigationPresenterCamera supports 2 modes: NAVIGATION and ROUTE_OVERVIEW.

In NAVIGATION the camera follows the location indicator (both in Free Drive and Active Guidance).

In ROUTE_OVERVIEW the camera overviews the current active route. Note that the ROUTE_OVERVIEW mode only makes sense when there is an active navigation route. If there's no route (or the route disappears while the camera is in ROUTE_OVERVIEW mode), NavigationPresenterCamera will fallback to NAVIGATION mode, but will animate back to ROUTE_OVERVIEW when the route appears.

By default it's in NAVIGATION mode. You can change it via invoking:

presenter.camera.setMode(CameraMode.ROUTE_OVERVIEW)

and set it back via:

presenter.camera.setMode(CameraMode.NAVIGATION)

Camera activation/deactivation

Camera activation/deactivation API is located at PresentersScreenContext level. See PresentersCameraManager section.

NavigationPresenter exposes presenter state: what is currently shown and how. You can access this information like this:

presenter.state.collect {
// explore the presenter state
}

This information can be used to retrieve the routes that are currently being displayed (handled by the presenter) and whether they are visible. Additionally, you can access the layers the routes are attached to. This information might be useful if you want to attach a custom DVA to route line. Find more information in the Callouts section. Also, use this API to understand whether HD Lite mode is currently active or not (see HD Lite).

Full information on what is exposed can be found in the API reference documentation for NavigationPresenterState.

RoutesPreviewPresenter

RoutesPreviewPresenter is available starting from NavSDK 3.18.0-beta.1.

Overview

RoutesPreviewPresenter is the presenter to use in the preview use case. It provides a high-level interface to configure presentation options and control the routes that are currently being previewed.

Key features:

Configuration

All the possible configuration options are listed in the RoutesPreviewPresenterConfig. Note that the configuration options for RoutesPreviewPresenter are independent of those for NavigationPresenter, meaning that you can configure them differently.

You can pass the configuration on initial presenter creation:

val presenter: RoutesPreviewPresenter = RoutesPreviewPresenter.create(
screenContext = screenContext,
navigationContext = navigationContext,
) {
routeLine {
routeLineColorResources(
RouteLineColorResources.Builder()
.routeLineTraveledColor(Color.BLUE)
.routeLineTraveledCasingColor(Color.BLACK)
.build(),
)
callouts {
visible(true)
}
}
camera {
overview {
overviewAlternativeRoutes(true)
}
}
}

and you can re-configure it at any point at runtime:

presenter.configure {
routeLine {
originAndDestinationPinsVisible(false)
}
}

Routes Preview Presenter Camera

RoutesPreviewPresenterCamera is a sub-controller of RoutesPreviewPresenter that controls the camera.

The RoutesPreviewPresenterCamera supports overview of the routes that are currently set to RoutesPreviewPresenter. Note that for routes that are set to MapboxNavigation there is a separate NavigationPresenterCamera (see NavigationPresenter section).

You can configure camera options using RoutesPreviewPresenter#configure:

presenter.configure {
camera {
overview {
padding(EdgeInsets(200.0, 200.0, 200.0, 200.0))
overviewAlternativeRoutes(true)
}
}
}

Full list of configuration options can be found in PreviewCameraConfig.

Camera activation/deactivation

Camera activation/deactivation API is located at PresentersScreenContext level. See PresentersCameraManager section.

RoutesPreviewPresenterState

RoutesPreviewPresenter exposes presenter state: what is currently shown and how. You can access this information like this:

presenter.state.collect {
// explore the presenter state
}

This information can be used to retrieve the routes that are currently being displayed (handled by the presenter) and whether they are visible.

Additionally, you can access the layers the routes are attached to. This information might be useful if you want to attach a custom DVA to route line. More information can be found in the Callouts section.

Explore the all options exposed in RoutesPreviewPresenterState in the API reference documentation.

Retrieve instances

The way to retrieve the instances of PresentersNavigationContext and NavigationPresenter depends on whether you create MapboxNavigation instance directly (MapboxNavigationProvider#create) or use MapboxNavigationApp.

If you create MapboxNavigation directly

If you create a MapboxNavigation instance directly, use the following code to retrieve PresentersScreenContext, PresentersNavigationContext, and NavigationPresenter/RoutesPreviewPresenter instances:

val screenContext: PresentersScreenContext = PresentersScreenContext.create()
val navigationContext: PresentersNavigationContext = PresentersNavigationContext.create(mapboxNavigation)
val navigationPresenter: NavigationPresenter = NavigationPresenter.create(screenContext, navigationContext)
val routesPreviewPresenter: RoutesPreviewPresenter = RoutesPreviewPresenter.create(screenContext, navigationContext)
Remove strong references to PresentersNavigationContext

When using MapboxNavigation directly, it's your responsibility to remove all strong references to PresentersNavigationContext when no longer needed (generally when MapboxNavigation is destroyed or recreated) to avoid native memory leaks. When using MapboxNavigationApp, the lifecycle is managed automatically.

If you use MapboxNavigationApp

If you use MapboxNavigationApp, use the following code to retrieve PresentersScreenContext, PresentersNavigationContextAppBased, and NavigationPresenter/RoutesPreviewPresenter instances:

val screenContext: PresentersScreenContext = PresentersScreenContext.create()
val appBasedNavigationContext: PresentersNavigationContextAppBased = PresentersNavigationContext.create()
val navigationPresenter: NavigationPresenter = NavigationPresenter.create(screenContext, appBasedNavigationContext)
val routesPreviewPresenter: RoutesPreviewPresenter = RoutesPreviewPresenter.create(screenContext, appBasedNavigationContext)

Lifecycle

PresentersNavigationContext

PresentersNavigationContext's lifecycle can be tied to the Application lifecycle.

  1. If you create MapboxNavigation directly (i. e. use PresentersNavigationContext and not PresentersNavigationContextAppBased), your PresentersNavigationContext should live as long as the MapboxNavigation instance lives (it dies when you invoke MapboxNavigationProvider#destroy).

Important: when you destroy MapboxNavigation, make sure to not hold a strong reference to PresentersNavigationContext to avoid memory leaks.

  1. If you use MapboxNavigationApp, your PresentersNavigationContext may live as long as your application lives: it will handle the MapboxNavigation lifecycle under the hood.

PresentersScreenContext and presenters

PresentersScreenContext and presenters instances can be created, for example, inside a ViewModel. If you have multiple presenters (for example, one NavigationPresenter and one RoutesPreviewPresenter) for one screen, you need only one PresentersScreenContext that will be passed as a dependency to both presenter instances. These instances can outlive configuration changes and they should live as long as the screen they correspond to is still relevant. To handle configuration changes, make sure you provide a valid MapView instance to PresentersScreenContext:

  1. When the view is created (in your Activity#onCreate or Fragment#onViewCreated), set the map to PresentersScreenContext:
presentersScreenContext.setMap(mapView)
  1. When the view is destroyed (in your Activity#onDestroy or Fragment#onDestroyView), nullify the map:
presentersScreenContext.setMap(null)
Note
Make sure to reset the map to avoid memory leaks.

When your screen is fully destroyed, destroy the presenters to clean up resources. You can do that, for example, in your ViewModel#onCleared:

navigationPresenter.destroy()
routesPreviewPresenter.destroy()

PresentersCameraManager

Different presenters provide their own camera sub-controllers (NavigationPresenter#camera and RoutesPreviewPresenter#camera).

To activate a specific camera, use PresentersCameraManager API. This is a camera manager that is available on PresentersScreenContext level (PresentersScreenContext#camera) and that handles camera activation/deactivation.

Only one camera may be active at once and PresentersCameraManager handles this under the hood: if you activate camera B, but camera A is active, camera A will be deactivated automatically.

For example, if you want to activate Navigation Presenter Camera, you may do it like this:

screenContext.camera.activateCamera(navigationPresenter.camera)

To deactivate the currently active camera, use:

screenContext.camera.deactivateCurrentCamera()

After this the Coordination Layer will not manipulate the map camera anymore and you may give the control over it to some other (custom) component.

Callouts

Default callouts

Callouts are small views attached to the route line that can be used to display additional information about the route. They are supported for both active navigation (NavigationPresenter) and routes preview (RoutesPreviewPresenter) scenarios. The Coordination Layer supports rendering callouts for view-based map out of the box. You can enable callouts via RouteLineConfig:

presenter.configure {
routeLine {
callouts {
visible(true)
}
}
}

Callouts can be displayed in 2 modes: ROUTES_OVERVIEW and NAVIGATION. In ROUTES_OVERVIEW mode a callout with ETA is attached to all routes:

In NAVIGATION mode callouts are attached to alternative routes only, displaying the ETA difference between the alternative and the primary route:

The callouts design is customizable, see CalloutsConfig documentation for details.

Note that rendering callouts with Compose Map is not supported directly in the Coordination Layer, but it exposes all the necessary data to attach the callouts on the app side, see the next section.

Custom callouts

If you need to attach a callout to route on your side (for example, you use Maps SDK Compose extensions), you can use the information exposed in NavigationPresenterState or RoutesPreviewPresenterState to do that.

With every route the callout data (including the layer id the route is attached to) is exposed: RouteInfo#callout, so you can use it to attach your callout to the route. The layer id might be null if the route does not currently have a layer associated with it (for example, during MapView recreation when configuration is changing or if route is not rendered). When layer id is null, you should not attach the callout anywhere and remove any existing callouts for this route.

Example of usage (inside a Composable function):

val routeInfo = presenter.state.map { it.routesInfo }.collectAsState(null).value ?: return
routeInfo.allRoutes.forEach { routeInfo ->
if (routeInfo.callout != null) {
ViewAnnotation(
options = viewAnnotationOptions {
annotatedLayerFeature(routeInfo.callout!!.layerId)
// other options
},
) {
MyRouteCalloutView(
routeDuration = routeInfo.route.directionsRoute.duration(),
diffWithPrimary = routeInfo.callout!!.durationDifferenceWithPrimary,
}
}
}
}

HD Lite

The Coordination Layer supports HD Lite mode for active navigation scenario (NavigationPresenter). For routes preview scenario RoutesPreviewPresenter HD Lite mode is not supported.

The look of the HD lite route components is very similar to the existing SD navigation. The difference is that the geometry of the route line and the location indicator position are aligned with the HD road surface.

HD Lite renders elevated roads and positions the route line and location indicator directly on them. It dynamically trims tunnels, overpasses, and buildings so you see your route with greater clarity.

Here are some screenshots that demonstrate features available in HD Lite mode:

If you use HD Lite mode, make sure to use a compatible HD style. Contact us to get a suitable style.

HD Lite mode is disabled by default, you can enable it via a configuration flag:

presenter.configure {
navigationQuality(NavigationDefinitionQuality.HD_LITE)
}

If you enable HD Lite mode, the Coordination Layer will under the hood switch between SD and HD Lite based on internal factors (coverage availability, HD tiles readiness, zoom levels (you can configure zoom threshold via hdLiteZoomThreshold configuration option, see HD Lite Configuration section for an example)).

When HD Lite mode is active, the map components in the presenters (route line, camera and location indicator) will use HD data (HD geometry of a route and HD-matched location). Location indicator and route line components will also be styled differently (see Configuration section for details). However, the camera will not automatically change its zooming strategy in HD Lite mode to zoom in more closely to be able to see HD roads in details. Switching camera to a more suitable mode for HD Lite is not yet covered by the Coordination Layer and must be done by the app (see an example below). It is recommended to use higher zoom levels in HD Lite mode to see the HD effect more clearly.

You can observe whether HD Lite mode is active or not via NavigationPresenterState:

presenter.state.map { it.navDefinitionQuality }.distinctUntilChanged().collect { navDefinitionQuality ->
val idHdLiteActive = navDefinitionQuality == NavigationDefinitionQuality.HD_LITE
if (idHdLiteActive) {
// configure zooms to be higher, for example, via zoom override
hdZoomJob = currentSpeed.collect { speed ->
// for example, if you use speed-based zoom strategy, make sure you adjust it return higher zoom levels in HD Lite mode
val hdZoom = getHdZoom(speed)
presenter.configure {
camera {
following {
overrides {
zoom(hdZoom)
}
}
}
}
}
} else {
hdZoomJob?.cancel()
// reset zoom overrides or use another strategy
presenter.configure {
camera {
following {
overrides {
zoom(null)
}
}
}
}
}
}

Note that NavigationPresenterState#navDefinitionQuality is not equivalent to the flag set in NavigationPresenterConfig. As mentioned above, it also takes into account all the internal factors like HD coverage and zoom threshold that affect the possibility of displaying HD Lite components.

HD Lite configuration

There are multiple configuration options for HD Lite mode:

  1. Enable/disable HD Lite:
presenter.configure {
if (enabled) {
navigationQuality(NavigationDefinitionQuality.HD_LITE)
} else {
navigationQuality(NavigationDefinitionQuality.SD)
}
}
  1. Configure PresentersNavigationContext:

When creating PresentersNavigationContext it is possible to specify custom PredictiveCacheLocationOptions which will apply to HD navigation only:

val presentersNavigationContext = PresentersNavigationContext.create {
loadPredictiveCacheForAlternativeRoutes(true)
// other options if needed
}
  1. Configure HD Lite location indicator using custom model asset:
presenter.configure {
locationIndicator {
hdLite(
config3D {
uri = "asset://3d-location-indicator.glb"
scale = ScaleValue(listOf(40.0, 40.0, 40.0))
}
)
}
}
  1. Configure SD / HD Lite switch zoom threshold

The Coordination Layer under the hood switches between SD and HD Lite modes based on multiple internal factors including current zoom level.

By default this zoom level is set to 15.0, because on lower zooms HD roads are not shown so the HD Lite mode doesn't make any sense there.

You can configure this threshold:

presenter.configure {
camera {
hdLiteZoomThreshold(18.0)
}
}
  1. Adjust NavigationOptions

For HD matching to work correctly, you need to set a proper custom config and HD tiles options when activating MapboxNavigation. Note that these configuration options belong to NavigationOptions and not to NavigationPresenterConfig.

First, extend your custom config (if you use any) or create a new one in a way that it includes the following options:

const val HD_LITE_CUSTOM_CONFIG = """
{
"navigation": {
"hd": {
"enabled": true,
"mapMatcher": {
"emissionCost": {
"camera": {
"enableLanesComparison": false,
"enableRoadMarkingsComparison": false
}
}
},
"offRoad": {
"transitionPointOffRoadDetector": {
"percentAlongMultiplier": 1.5
}
},
"route": {
"adaptableCandidateFinder": {
"mediumRoadClassFilter": {
"headingTolerance": 55.0
}
},
"alternatives": {
"adaptableCandidateFinder": {
"mediumRoadClassFilter": {
"headingTolerance": 55.0
}
},
"routeLaneGuidance": {
"maxForwardDistance":2000
},
"viterbiRouteMatcher": {
"lookAheadInAdvance":2500
}
},
"routeLaneGuidance": {
"maxForwardDistance":2000
},
"viterbiRouteMatcher": {
"lookAheadInAdvance":2500
}
}
}
}
}
"""

Then create HD tiles options. Create the default options to always pick the latest HD tiles or specify a custom version:

val hdTilesOptions = DomainTilesOptions.defaultHdTilesOptionsBuilder()
.tilesBaseUri(URI("https://api-3dln-tiles-production.mapbox.com"))
.build()

Finally, pass the custom config together with the tile options to NavigationOptions:

val mapboxNavigation = MapboxNavigationProvider.create(
NavigationOptions.Builder(this)
.deviceProfile(
DeviceProfile.Builder()
.customConfig(HD_LITE_CUSTOM_CONFIG)
.build()
)
.routingTilesOptions(
RoutingTilesOptions.Builder()
.hdTilesOptions(hdTilesOptions)
.build()
)
.build()
)

Share PresentersNavigationContext for optimal performance

Critical for performance

Always share the same PresentersNavigationContext instance across multiple NavigationPresenter instances when you have multiple screens displaying navigation data.

This architecture is essential for optimal performance because:

  • All complex native calculations are performed once: HD map-matching, route matching, and location processing happen in the navigation context, not in individual presenters
  • Multiple screens with different visual styles: Each NavigationPresenter can have its own MapView and visual configuration while sharing the underlying data
  • Significant performance improvement: When you have multiple screens (e.g., main display and instrument cluster), expensive calculations run once instead of being duplicated per presenter

Route objects matching

PresentersNavigationContext can be used to do route object matching (such as incidents, maneuvers etc.) from SD to HD and elevate the objects on top of elevated bridges or tunnels.

Red circles display original matchable points on the SD route and green markers HD matched elevated objects.

HD Matcher usage notes
  • Recommended and easiest way is to provide the full list of matchable objects for the whole route at once
  • If a route changes, objects won't be rematched automatically - you need to invoke RouteObjectMatcher again with the new route

Steps to match route objects to HD route and apply an elevation to render the objects on top of elevated roads :

  1. Configure map source and layer :
val matchedObjectsSourceId = "matched-objects-source"
val matchedObjectsLayerId = "matched-objects-layer"

mapView.mapboxMap.loadStyle(styleUri) { style ->
// Add source
style.addSource(
geoJsonSource(matchedObjectsSourceId) {
featureCollection(FeatureCollection.fromFeatures(emptyList()))
}
)

// Add layer (can be SymbolLayer, CircleLayer, or LineLayer)
val matchedObjectsLayer = SymbolLayer(matchedObjectsLayerId, matchedObjectsSourceId).apply {
iconImage("marker-icon")
}
style.addLayer(matchedObjectsLayer)

// Enable elevation for the layer
mapView.mapboxMap.addLayerElevationProperty(matchedObjectsLayer)
}
Elevating the HD objects

The MapboxMap.addLayerElevationProperty function is required for objects to be properly elevated on top of HD road layers. It supports SymbolLayer, CircleLayer and LineLayer.

  1. Observe matched objects and add elevated features to the source:
// Store matched objects
private val matchedObjects = mutableListOf<MatchedRouteObject>()

presentersNavigationContext
.getHdRouteObjectMatcher()
.addMatchingObserver(
object : RouteObjectMatcherObserver {
override fun onRouteObjectsMatched(result: Expected<List<MatchedRouteObjectError>, List<MatchedRouteObject>>) {
result.fold(
{ errors ->
// handle map-matching errors for multiple objects
errors.forEach { error ->
Log.e("RouteObjectMatcher",
"Error matching object ${error.matchableRouteObject.id}: ${error.error}")
}
},
{ newMatchedRouteObjects ->
// Add newly matched objects to our collection
matchedObjects.addAll(newMatchedRouteObjects)

// Convert to features with elevation data
val features = matchedObjects.map { matchedObject ->
matchedObject.buildElevatedFeature()
}

// Update the map source
updateMatchedObjectsSource(FeatureCollection.fromFeatures(features))
}
)
}

override fun onMatchingCancelled(matchableRouteObjects: List<MatchableRouteObject>) {
// handle cancellation of multiple objects
matchableRouteObjects.forEach { matchableRouteObject ->
// handle individual cancellation
}
}
},
)
Add elevated property to the source

To apply the elevation for the matched objects, generate the features using matchedObject.buildElevatedFeature.

  1. Request matching for route objects:
val routeObjects: Set<MatchableRouteObject> = setOf()
presentersNavigationContext
.getHdRouteObjectMatcher()
.matchRouteObjects(routeObjects)

Configure cutout

The Coordination Layer supports cutout around the location indicator in HD Lite mode. This feature creates a cutout in the elevated roads, tunnels, bridges and other 3D objects to improve visual clarity. The cutout behavior differs between free drive and active guidance modes.

The cutout is highly customizable with different distances and fade effects.

Free drive cutout

In free drive mode (when not navigating a route), the cutout follows the predicted path ahead of the vehicle. Configure free drive cutout using CutoutAroundLocationIndicatorFreeDriveConfig:

presenter.configure {
cutout {
hdLite {
aroundLocationIndicator {
freeDrive {
enabled(true)
aheadDistance(100)
aheadFadeDistance(100)
behindFadeDistance(100)
width(CutoutWidth.Auto)
widthFadeFraction(0.2)
maxNumberOfForkOnPath(1)
}
}
}
}
}

See CutoutAroundLocationIndicatorFreeDriveConfigUpdateBuilder for all configuration options and their descriptions.

Active guidance cutout

During active guidance (when navigating a route), the cutout follows the route line ahead and behind the location indicator. Configure active guidance cutout using CutoutAroundLocationIndicatorActiveGuidanceConfig:

presenter.configure {
cutout {
hdLite {
aroundLocationIndicator {
activeGuidance {
enabled(true)
aheadDistance(100)
aheadFadeDistance(100)
behindFadeDistance(100)
width(CutoutWidth.Auto)
widthFadeFraction(0.2)
}
}
}
}
}

See CutoutAroundLocationIndicatorActiveGuidanceConfigUpdateBuilder for all configuration options and their descriptions.

Width configuration

The cutout width can be configured in two ways for both free drive and active guidance modes:

  1. Auto width (CutoutWidth.Auto): The width is automatically calculated based on the HD road width of the current lane group.
  2. Fixed width (CutoutWidth.Fixed): The width is fixed to a specific value in meters.

Example:

// Using automatic width
width(CutoutWidth.Auto)

// Using fixed width of 10 meters
width(CutoutWidth.Fixed(10.0))

Sample application

To get a sample application that showcases the Coordination Layer integration, contact us.

この{Type}は役に立ちましたか?