Manage onboard regions

The onboard region store holds the NDS map data the SDK falls back to when the online cache cannot cover the current location. This guide covers what you can do with it from your app: monitor its state, discover which regions the driver needs, install and remove regions, and choose between deploying a pre-populated map or a small empty map that fills itself up over time.

For a conceptual introduction read Map data overview. For directory placement see Configure map data storage. For corruption recovery see Map corruption recovery.

The region-management types on this page are provided by the com.tomtom.sdk.datamanagement:data-store dependency added in the Map data quickstart.

RegionStore is a typealias of NdsStore; the API reference lists its members under NdsStore.

Obtaining a RegionStore

RegionStore is the runtime handle to the onboard region store. Obtain it after TomTomSdk.initialize(…​) completes, and enable update processing — it starts disabled every time the SDK initializes, and no downloads, deletions, or updates run until it is enabled:

val regionStore = TomTomSdk.getOfflineRegionStore()
// Update processing is disabled when the SDK starts. Enable it so that
// downloads and deletions can run.
regionStore.setUpdatesEnabled(true)

getOfflineRegionStore() throws IllegalStateException if the SDK was initialized without a regionStorePath (no onboard region store was configured).

The RegionStore methods you use directly are:

  • setUpdatesEnabled(Boolean) — the master switch for update processing. Call setUpdatesEnabled(false) to stop all downloads, deletions, and updates again, for example when the user turns off background data in your app settings.
  • replaceMap(…​) and replaceKeystore(…​) — used only for corruption recovery. See Map corruption recovery.

Everything else you do with the onboard store — install and delete regions, monitor state, look up which regions cover a position or route — goes through RegionUpdater, described in the next sections.

Obtaining a RegionUpdater

RegionUpdater is the manual-management interface. Obtain it from the RegionStore:

val regionUpdater = regionStore.obtainRegionUpdater()
  • obtainRegionUpdater() returns the same instance until it is closed; a new call after close() creates a fresh instance.
  • Call regionUpdater.close() when you no longer need it (for example when the map-management screen is dismissed) to release resources.
  • Throws IllegalStateException if the RegionStore was configured without update capability.

Region model

Three concepts are worth introducing before the operations.

Region

A Region is a single downloadable/removable unit of map data. Each region has a stable Region.Id, a localized name, an isUpdatable flag, and a set of children — regions form a tree, from continental roots down to leaf regions.

RegionState

RegionState is the on-device status of an updatable region, combining three properties:

  • installState — one of RegionInstallState.NotInstalled, PartiallyInstalled, CompletelyInstalled, or Inconsistent (the installed data needs a repair, that is, a re-download).
  • updateState — whether downloading would change the region data on the device (downloadAvailable) and the estimated downloadSize. null while update information is not yet available.
  • dataInfo — details about the installed data, such as its size on disk. null when the region is not installed.

State changes are reported by RegionInfoUpdateListener.onStatesChanged.

RegionOperation and RegionOperationStatus

An in-flight download or deletion, and its progress. Progress updates are reported by RegionInfoUpdateListener.onOperationStatusesChanged.

Monitoring region state

Register a RegionInfoUpdateListener on the RegionUpdater to receive callbacks whenever the onboard region set, individual region states, or in-flight operations change.

val listener = object : RegionInfoUpdateListener {
override fun onStructureChanged(structureResult: Result<RegionStructure, MapUpdateError>) {
// Called once when the listener is registered and again whenever the
// set of available regions changes. Render the region tree from the
// structure's roots and cache its region states.
}
override fun onStatesChanged(stateInfo: RegionStateInfo) {
// Delta update: merge stateInfo.regionStates into your cached state map.
}
override fun onOperationStatusesChanged(statuses: Set<RegionOperationStatus>) {
// Delta update on in-flight downloads and deletions. Show progress and
// completion in your UI.
}
}
regionUpdater.addRegionInfoUpdateListener(listener)

onStructureChanged fires first, providing the full region tree and initial states. onStatesChanged and onOperationStatusesChanged then deliver delta updates — only regions and operations whose state changed since the previous call.

Remove the listener when your UI no longer needs it:

regionUpdater.removeRegionInfoUpdateListener(listener)

Discovering which regions to install

Two lookup methods answer the “which regions cover this area” question without downloading anything. Both are asynchronous and return a Cancellable handle you can use to abort the query.

Around a position

val query = regionUpdater.findRegionsAroundPosition(
position = currentPosition,
radius = Distance.kilometers(30),
callback = object : RegionQueryCallback {
override fun onSuccess(result: Set<Region.Id>) {
// These regions cover the requested area; pass them to download().
}
override fun onFailure(failure: MapUpdateError) {
// Handle the failure.
}
},
)

Use this on first launch to figure out which region contains the driver’s current location, or before opening a map-management picker to show the user “these are the regions around you”.

Along a route

val query = regionUpdater.findRegionsAlongPolyline(
polyline = routeGeometry, // Requires at least two points.
radius = Distance.kilometers(3), // Corridor half-width.
callback = object : RegionQueryCallback {
override fun onSuccess(result: Set<Region.Id>) {
// Check coverage, or pass the IDs to download().
}
override fun onFailure(failure: MapUpdateError) {
// Handle the failure.
}
},
)

Use this before starting a long trip to check whether the driver has onboard coverage for the whole route.

Both queries only identify regions. To actually install them, hand the returned Region.Id values to download — note that the callbacks deliver a Set<Region.Id> while download and delete take a List<Region.Id>.

A third way to choose regions is to let the user browse the full region tree delivered by RegionInfoUpdateListener.onStructureChanged — the approach behind the region-management screen described in the first-install options below.

Installing and removing regions

Downloading

val result = regionUpdater.download(regionIds.toList())

The result is Success(Set<RegionOperation>) if the operations were scheduled, or Failure(MapUpdateError) if any could not be — for example if a region is already scheduled, or if none of the passed IDs is available for download. Progress and completion are reported through your RegionInfoUpdateListener.

Deleting

val result = regionUpdater.delete(regionIds.toList())

Same result type as download; likewise reports through the listener.

Cancelling

regionUpdater.cancelOperations(
regionIds = regionIds,
action = DownloadedRegionDataAction.Delete, // Or .Keep to allow resuming later.
)

Cancellation of an in-flight download can either delete the partially-downloaded data (frees disk immediately) or keep it, in which case a subsequent download on the same region resumes rather than restarts.

Deployment: pre-populated vs empty map

The regionStorePath you pass to buildSdkConfiguration points at an on-disk NDS map. There are two common ways to provision this directory.

Pre-populated map

The device ships with a full region set for a defined market (for example a continental cut). Advantages: navigation is available immediately on first boot, no connectivity required for the initial user experience. Disadvantages: larger disk footprint at ship time; content freshness lags until the first update. Use this when your product must work out-of-the-box in an unconnected first-boot scenario, or when your legal/commercial arrangement requires shipping content pre-loaded.

Preinstalling the map is not an SDK function — you define the process that places it on the device, for example factory flashing or dealer provisioning. Contact Sales for the map deliverables.

Empty map

The device ships with a minimal region store (metadata only, no downloadable region content) and the driver — or an automated flow — pulls in regions on demand. Advantages: small footprint at ship time; content downloaded is guaranteed fresh. Disadvantages: requires network connectivity for the first drive; you must decide how the initial region set is chosen.

The rest of this section describes the empty-map first-install flow.

First install from an empty map

On first launch, the onboard store contains no regions. Any of the following will populate it:

  1. User-driven from a picker — call findRegionsAroundPosition around the current location (radius wide enough to cover the driver’s home region), present the returned regions in a picker UI, and call download on the user’s selection. The RegionInfoUpdateListener.onOperationStatusesChanged callback drives your progress UI.
  2. Programmatic from position — call findRegionsAroundPosition and immediately download all returned IDs, without user intervention. Reasonable for a “quiet install” during dealer setup on WiFi.
  3. Programmatic along a planned route — if the driver plans a long route before their first offline stretch, findRegionsAlongPolyline returns the regions the route passes through; download them ahead of departure.
  4. Region-management screen — render the full region tree delivered by RegionInfoUpdateListener.onStructureChanged and let the user browse and select regions by name, showing the estimated download size (RegionState.updateState.downloadSize) and, once installed, the size on disk (RegionState.dataInfo). Call download on the selection. The same screen doubles as the place to delete regions and to surface available updates later.

Whichever entry point you pick, setUpdatesEnabled(true) must have been called first — update processing starts disabled every time the SDK initializes.

Connectivity settings

Whether the SDK is allowed to consume mobile data for region operations is controlled from the regionStoreConfiguration.updateConfiguration { …​ } block on buildSdkConfiguration. This block exposes a RegionStoreUpdateConfiguration.Builder.

The setting for manual operations is manualUpdateNetworkConfig:

val sdkConfig = buildSdkConfiguration(
context = context,
apiKey = apiKey,
regionStorePath = File(context.filesDir, "region-store"),
telemetryUserConsent = { UserConsent.TelemetryOn },
regionStoreConfiguration = {
keyStorePath = File(context.filesDir, "keystore")
updateConfiguration = {
// Allow manual downloads on metered connections (refused by default).
manualUpdateNetworkConfig =
NetworkConnectivityConfiguration(isMeteredAllowed = true)
}
},
)

The default for manualUpdateNetworkConfig is NetworkConnectivityConfiguration(false)manual downloads are refused on metered connections by default. This matters: an integrator who calls regionUpdater.download(…​) on a device with only a mobile connection will see the download fail unless manualUpdateNetworkConfig was set to NetworkConnectivityConfiguration(true).

manualUpdateNetworkConfig accepts only two effective values:

  • NetworkConnectivityConfiguration(true) — manual downloads proceed on both metered and unmetered connections.
  • NetworkConnectivityConfiguration(false) — manual downloads proceed only on unmetered connections.

The multi-argument constructor of NetworkConnectivityConfiguration that takes a regionAgeForMeteredConnections duration is not valid for manualUpdateNetworkConfig; passing anything other than the two forms above throws IllegalArgumentException at configuration build time.

At runtime, regionStore.setUpdatesEnabled(Boolean) is the master switch: it must have been set to true for any download to run, and setting it to false stops all update processing regardless of connectivity or of manualUpdateNetworkConfig.

Disk usage

mapDiskUsageQuota on the same updateConfiguration block caps the total on-disk size of the region store:

val sdkConfig = buildSdkConfiguration(
context = context,
apiKey = apiKey,
regionStorePath = File(context.filesDir, "region-store"),
telemetryUserConsent = { UserConsent.TelemetryOn },
regionStoreConfiguration = {
keyStorePath = File(context.filesDir, "keystore")
updateConfiguration = {
mapDiskUsageQuota = Memory.gibibytes(50)
}
},
)

Defaults to null (unlimited — the region store may grow to fill whatever the underlying filesystem allows). Downloads that would exceed the quota do not proceed; free up space by deleting regions.

The locale field on the same block controls the language used for region names surfaced by RegionInfoUpdateListener — useful if your UI needs region names to match the SDK’s other localized strings. Defaults to the user’s preferred locale.

Next steps

Overview

Conceptual introduction to the two data sources and the behavior when connectivity changes.

Go to page

Configure map data storage

Choose where the online cache and onboard region store live on the device.

Go to page

Map corruption recovery

Detect and recover from a corrupted onboard map or keystore file.

Go to page