Manual map management

VERSION 1.20.1
PUBLIC PREVIEW

For some use cases, you may require more control over the map regions to be updated than what automatic map updates provide. In such cases, this SDK offers manual map management functionality.

The term manual comes from the common use case of a navigation application, where users are presented with a screen that displays a list of map regions, allowing them to manually select the regions they wish to update. However, manual map management can also be implemented based on your own logic and may not necessarily require user interaction.

This guide provides an overview of how the map structure is represented and explains the steps to perform map operations on the map regions.

Map structure

The structure of a map is represented by the RegionGraph and CompositeRegionGraph, each offering a different perspective on the map structure.

You can choose to use either representation based on your needs. The RegionGraph provides a detailed and fine-grained representation of the map structure. On the other hand, the CompositeRegionGraph provides a higher-level and more streamlined view of the map structure.

Both RegionGraph and CompositeRegionGraph follow a tree structure. In the RegionGraph, each node is represented by a RegionGraphNode, while in the CompositeRegionGraph, each node is represented by a CompositeRegion.

You can see the visual differences between the two representations in the following images:

It’s important to note that the map structure may vary depending on factors such as the map provider, map versions, and other considerations. The specific regions and their hierarchy can differ based on these factors.

Map region state

The state of the RegionGraphNode is represented by the RegionGraphNodeState. Similarly, the state of CompositeRegion is represented by the CompositeRegionState. They provide valuable information that can assist you in determining which regions to install or update, such as whether the region is currently installed, whether there are available updates for the region, and the size of the update that needs to be downloaded.

Observing map structure and map region states

By Implementing the RegionGraphListener, you can receive notifications whenever there are changes in the RegionGraph structure or the state of individual RegionGraphNode:

1private var rootNodes = listOf<RegionGraphNode>()
2private val nodeStates = mutableMapOf<RegionGraphNodeId, RegionGraphNodeState>()
3private val regionGraphListener =
4 object : RegionGraphListener {
5 override fun onRegionGraphChanged(
6 regionGraphResult: Result<RegionGraph, MapUpdateError>,
7 mapRegionStates: Map<RegionGraphNodeId, RegionGraphNodeState>,
8 ) {
9 regionGraphResult
10 .ifSuccess {
11 // Your code goes here
12 // For example, you can save the region graph and the states
13 rootNodes = it.roots
14 nodeStates.clear()
15 nodeStates.putAll(mapRegionStates)
16 }
17 .ifFailure {
18 // Your code goes here
19 Log.e(TAG, "Can't get RegionGraph: $it")
20 }
21 }
22
23 override fun onMapRegionStatesChanged(mapRegionStates: Map<RegionGraphNodeId, RegionGraphNodeState>) {
24 // Your code goes here
25 // for example, update the current states
26 nodeStates.putAll(mapRegionStates)
27 }
28 }

After instantiating the NdsStoreUpdater, you can register the listener you’ve created. To construct a NdsStoreUpdater object, refer to the Offline map quickstart.

ndsStoreUpdater.addRegionGraphListener(regionGraphListener)

Similarly, by implementing the CompositeRegionListener, you can observe the changes in the CompositeRegionGraph structure or the state of individual CompositeRegion:

1private var rootCompositeRegions = setOf<CompositeRegion>()
2private val compositeRegionStates = mutableMapOf<CompositeRegionId, CompositeRegionState>()
3private val compositeRegionListener =
4 object : CompositeRegionListener {
5 override fun onCompositeRegionGraphChanged(
6 graphResult: Result<CompositeRegionGraph, MapUpdateError>,
7 changedStates: CompositeRegionStatesData?,
8 ) {
9 graphResult
10 .ifSuccess {
11 // Your code goes here
12 // For example, you can save the graph and the states
13 rootCompositeRegions = it.roots
14 compositeRegionStates.clear()
15 if (changedStates != null) {
16 compositeRegionStates.putAll(changedStates.stateMap)
17 }
18 }
19 .ifFailure {
20 // Your code goes here
21 Log.e(TAG, "Can't get CompositeRegionGraph: $it")
22 }
23 }
24
25 override fun onCompositeRegionStatesChanged(changedStates: CompositeRegionStatesData) {
26 // for example, update the current states
27 compositeRegionStates.putAll(changedStates.stateMap)
28 }
29 }

To register the listener, you need to initiate the CompositeRegionsUpdater object first:

val compositeRegionsUpdater = CompositeRegionsUpdater(ndsStoreUpdater)
compositeRegionsUpdater.addCompositeRegionListener(compositeRegionListener)

Representing map structure and map region states

Now that you have the data of map structure and the map region states. To represent the map structure and map region states, you can choose a suitable view framework such as ListView, RecycleViews, or any other appropriate framework. These frameworks provide efficient ways to display and manage lists or collections of data.

For instance, to display the map region name and the map region state, here is a layout with TextView:

1<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:tools="http://schemas.android.com/tools"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent" >
5
6 <TextView
7 android:id="@+id/regionName"
8 android:layout_width="wrap_content"
9 android:layout_height="wrap_content" />
10
11 <TextView
12 android:id="@+id/installState"
13 android:layout_width="wrap_content"
14 android:layout_height="wrap_content" />
15
16</LinearLayout>

Then here is the code snippets of assigning the data to the TextView:

1val node = rootNodes.first()
2val regionName = findViewById<TextView>(R.id.regionName)
3regionName.text = node.name
4
5val nodeState = nodeStates[node.id]
6val installState = findViewById<TextView>(R.id.installState)
7installState.text =
8 when (nodeState?.installState) {
9 InstallState.NotInstalled -> "Not Installed"
10 InstallState.CompletelyInstalled -> "Completely Installed"
11 InstallState.PartiallyInstalled -> "Partially Installed"
12 InstallState.Inconsistent -> "Inconsistent"
13 else -> ""
14 }

You can replace the RegionGrpahNode with the CompositeRegion and the RegionGraphNodeState with the CompositeRegionState.

Performing map operations

The SDK provides an API that allows you to perform map operations such as installing/updating, and uninstalling map regions on RegionGraphNode objects.

Note that not all nodes present in the RegionGraph support these map operations. For each RegionGraphNode, you can check the isUpdatableRegion property to identify if the node is eligible for map operations. isUpdatableRegion is a read-only property that returns true if the RegionGraphNode supports map operations and false otherwise.

Each RegionGraphNode is identified by a RegionGraphNodeId. For each updatable RegionGraphNode, you can perform map operations via scheduleMapOperations API. Here is an example:

1val regionsToUpdate = rootNodes.first().children.orEmpty()
2val regionsToUninstall = rootNodes.last().children.orEmpty()
3val mapOperations = mutableListOf<MapOperation>()
4
5regionsToUpdate.forEach {
6 if (it.isUpdatableRegion) { // make sure this node supports map operations
7 mapOperations.add(MapOperation(MapOperationType.InstallAndUpdate, it.id))
8 }
9}
10regionsToUninstall.forEach {
11 if (it.isUpdatableRegion) { // make sure this node supports map operations
12 mapOperations.add(MapOperation(MapOperationType.Uninstall, it.id))
13 }
14}
15ndsStoreUpdater.scheduleMapOperations(mapOperations)

NOTE: If automatic map updates are also in progress, the scheduled map operations take priority over those initiated by automatic map updates.

Similarly, you can use the scheduleMapOperations from the CompositeRegionsUpdater object to perform map operations on the CompositeRegion. Note that all the CompositeRegion support map operations. You can schedule map operations on any CompositeRegion like the following code snippets:

1// perform install and update on all regions
2val mapOperations =
3 rootCompositeRegions.map {
4 CompositeRegionOperation(it.id, MapOperationType.InstallAndUpdate)
5 }
6compositeRegionsUpdater.scheduleMapOperations(mapOperations)

Canceling map operations

If you want to cancel the scheduled map operations, you can do it via cancelAllMapOperations API. Here is an example:

val toBeCanceled = rootNodes.first().children?.map { it.id }.orEmpty()
ndsStoreUpdater.cancelAllMapOperations(toBeCanceled)

Similarly, you can use the cancelAllMapOperations from the CompositeRegionsUpdater object to cancel the scheduled map operations on the CompositeRegion.

Next steps

Since you have learned about manual map management, here are recommendations for the next steps: