Manual map management
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 regionGraphResult10 .ifSuccess {11 // Your code goes here12 // For example, you can save the region graph and the states13 rootNodes = it.roots14 nodeStates.clear()15 nodeStates.putAll(mapRegionStates)16 }17 .ifFailure {18 // Your code goes here19 Log.e(TAG, "Can't get RegionGraph: $it")20 }21 }2223 override fun onMapRegionStatesChanged(mapRegionStates: Map<RegionGraphNodeId, RegionGraphNodeState>) {24 // Your code goes here25 // for example, update the current states26 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 graphResult10 .ifSuccess {11 // Your code goes here12 // For example, you can save the graph and the states13 rootCompositeRegions = it.roots14 compositeRegionStates.clear()15 if (changedStates != null) {16 compositeRegionStates.putAll(changedStates.stateMap)17 }18 }19 .ifFailure {20 // Your code goes here21 Log.e(TAG, "Can't get CompositeRegionGraph: $it")22 }23 }2425 override fun onCompositeRegionStatesChanged(changedStates: CompositeRegionStatesData) {26 // for example, update the current states27 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" >56 <TextView7 android:id="@+id/regionName"8 android:layout_width="wrap_content"9 android:layout_height="wrap_content" />1011 <TextView12 android:id="@+id/installState"13 android:layout_width="wrap_content"14 android:layout_height="wrap_content" />1516</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.name45val 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>()45regionsToUpdate.forEach {6 if (it.isUpdatableRegion) { // make sure this node supports map operations7 mapOperations.add(MapOperation(MapOperationType.InstallAndUpdate, it.id))8 }9}10regionsToUninstall.forEach {11 if (it.isUpdatableRegion) { // make sure this node supports map operations12 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 regions2val 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: