Use Offline Maps

With preloaded offline maps you can access whole countries or even continents completely offline without using any internet connection or consuming OTA bandwidth.

Offline maps (also known as persistent maps or preloaded maps) offer the same features as the map data that is available online: You can search for places, calculate routes, start guidance (only available for the Navigate Edition), and - of course - you can interact with the MapView in the same way as with online map data.

Overview

In addition to the MapView, there are two engines that support offline map data:

  • Use the OfflineSearchEngine as equivalent to the online operable SearchEngine to search for places.
  • Use the OfflineRoutingEngine as equivalent to the online operable RoutingEngine to calculate routes.

Note that the above engines need to be executed on already downloaded or cached map data to make use of offline features. Other engines will not return results when they are executed offline - even if offline data is available.

In addition, the Navigator and the VisualNavigator work fully with offline map data to support offline turn-by-turn guidance. Worth to mention: The Navigator works headless (without a MapView), but it also requires map data to deliver events.

Once a Region has been downloaded with the MapDownloader - or when an area has been cached by panning the map - the MapView is fully operable for this area without an internet connection. However, other engines like the SearchEngine or the RoutingEngine will never make use of the cached or downloaded map data.

Below we describe how to get offline map data integrated into your application with the MapDownloader.

Note

This is a beta release of this feature, so there could be a few bugs and unexpected behaviors. APIs may change for new releases without a deprecation process.

Why use offline maps? In most situations - when limited bandwidth and data consumption is not an issue - accessing online map data offers more accurate and up-to-date map data. Also, some features may not be available on offline maps, for example, search results may contain not all data that is available online. However, there can be situations when an internet connection may lag, drop - or is completely off for a longer time. Especially, while on-the-go, a stable connection may not always be available. A mobile device may also be offline due to the user's decision to save bandwidth. This means: There can be multiple reasons to use offline maps - and last but not least, offline maps will speed up the user's experience with unbeatable response times.

What is the difference between the map cache and offline maps?

You have two ways to access map data offline:

  • Map Cache: By default, all map data is cached onto the device while using the map. This storage is persisted between sessions, but the storage size is limited and old cache data may be replaced with newer map data. Note: You can change the default cache path and the size with the SDKOptions you can pass into the SDKNativeEngine when you initialize the HERE SDK programmatically. Cached map data is stored in the local map cache storage. More about the cache can be found here. Note that you can use a RoutePrefetcher to actively fill data into the cache (see below.)

  • Offline Maps: With offline maps you can download entire regions or even continents to preload their map data for offline use - including places, routing and other data. A dedicated MapDownloader enables you to get and to maintain this data. Offline maps data is persisted between sessions and the data will not be deleted unless the user decides so. Offline maps are stored in the local persistent map storage. This is a second map storage and fully separated from the map cache. Usually, downloading offline maps happens over Wi-Fi as there are gigabytes of data to be loaded. Offline map data is used as a complete alternative to online services and the cached map when there is no connectivity.

Note

Offline maps work for all map schemes that are vector based. Satellite based map schemes are not part of the downloaded map data.

Can I enforce my app to use offline map data to operate radio-silent?

An application must decide which map storage to use as there is no automatic switching logic for features such as routing or search. However, when showing a map view an app can enforce offline map usage using an offline switch - for this mode it does not matter if the device operates with or without connectivity: When the offline switch is activated, the HERE SDK will initiate no internet connection.

By default, when interacting with a map view, the HERE SDK will automatically make use of online data, the local map cache and the persistent map storage to provide the best possible user experience:

  1. The HERE SDK will first check if map data can be shown from the map cache storage for the current viewport area - even if the device is online.
  2. If no cached data is available for the current viewport, the HERE SDK will look for offline maps data in the persistent map storage - even if the device is online. If no offline maps data is found:
    • If the device is offline: The map will be shown with less details or even no details.
    • If the device is online: New map data will be shown and downloaded into the cache, unless the offline switch is active. When the cache is full, a least recently used (LRU)) strategy is applied.

For example, when the Berlin region is installed on a device and the device is online, then no map data will be downloaded into the map cache when panning the Berlin map area - and the installed region data will be used instead.

Note

When the HERE SDK uses backend services such as for search and routing, you need to use dedicated offline engines to access cached or pre-downloaded offline map data. Use the OfflineSearchEngine and OfflineRoutingEngine to access map data offline. Their counterparts, the SearchEngine and RoutingEngine, will only provide results when an online connection is available - otherwise you will get an error. Therefore, you need to decide which engine to use.

The dedicated offline engines, like the OfflineRoutingEngine, will never download any online data, regardless of the connectivity of the device or the global offline switch.

While the HERE SDK offers dedicated online and offline feature engines, for turn-by-turn navigation there is no such distinction: The Navigator and VisualNavigator engines require map data either from the map cache or from offline maps data in the persistent map storage - if available. If no data is found, the data will be downloaded automatically into the cache, unless the global offline switch is active.

Get Started

Downloading and using offline maps can be achieved in two simple steps.

1) Download a list of Region objects. Optionally, this list can be localized with local region names. Once you have this list, you can pick the RegionId you want to download and pass it as download request to the MapDownloader.

2) Use the MapDownloader to download a single Region or a list of regions. You can also download several regions in parallel. Show the download progress to the user by setting a DownloadRegionsStatusListener.

3) Use the MapUpdater to update already downloaded regions and the map cache to a newer map version.

Once the download has completed, the map is ready to be used. If a device is offline, it will automatically show the downloaded region when the camera's target is pointed to that region.

Note

If a download failed, the HERE SDK will still be in a fully operable state. Just try to download again, until the progress has reached 100% and the status finally indicated that the operation has completed. If the issues are more severe, you can try to repair: See the repair section below for more details.

It is not recommended to keep downloading or updating map data while an app is running in background. As best practice, users should be notified to pause() or cancel() ongoing downloads when they cannot stay. For best practice, consider to offer a resume() option when the app is resumed.

You should also consider to write platform code to keep a device alive (especially GPU and CPU) and prevent the app from going into an idle state - or at least, to inform users to keep the screen on.

Since map data for regions, countries or whole continents can contain several hundreds of megabytes, a download may take a while - depending on factors such as available bandwidth. A download may also fail when a connection gets timed out and cannot recover. For the best user experience, it is recommended to allow the user to cancel ongoing operations and to watch the progress until a map download succeeds.

Note

To get a quick overview of how all of this works, you can take a look at the OfflineMapsExample class. It contains all code snippets shown below and it is part of the OfflineMaps example app you can find on GitHub.

Create a MapDownloader Instance

You can create the MapDownloader once per SDKNativeEngine:

guard let sdkNativeEngine = SDKNativeEngine.sharedInstance else {
    fatalError("SDKNativeEngine not initialized.")
}

// Create MapDownloader in background to not block the UI thread.
MapDownloader.fromEngineAsync(sdkNativeEngine, { mapDownloader in
    self.mapDownloader = mapDownloader
})

Usually, the SDKNativeEngine is automatically initialized when you start the app to show a MapView. Therefore, you can access its instance at runtime and obtain the MapDownloader from it.

Note

Consider to use a separate loader method or class with - for example - a dispatch group to wait until all instances are ready. Optionally, show a loading indicator while waiting. As a benefit, you can then use the instance(s) in your class without nil checks. Usually, the creation of MapDownloader and MapUpdater (see below) happens within a second.

By default, the downloaded map data will be stored to a default location:

// Note that the default storage path can be adapted when creating a new SDKNativeEngine.
let storagePath = sdkNativeEngine.options.cachePath
showMessage("This example allows to download the region Switzerland. StoragePath: \(storagePath).")

As stated in the comment, you can change that storage location, if you wish to do so - but then you need to create a new SDKNativeEngine instance as shown in the Key Concepts section and set the new cache path together with your credentials as part of the SDKOptions. Note that the storage path is unique for your credentials key.

Download a List of Regions

Each downloadable Region is identified by a unique RegionId. In order to know which regions are available and which RegionID belongs to which Region, you need to download a list of all available offline maps. This list contains regions from the entire world.

Note

Each Region can contain multiple children and each child represents a subset of its parent Region - when you download the parent, then the child regions are automatically included. If you are interested only in smaller portions of an area you can traverse the child regions. Usually, the top-level regions represent continents with countries as children. For the sake of simplicity, below we only look for downloadable countries and ignore any children of children and their children (and so on).

The code below downloads the list of downloadable regions and stores the available Region elements in a list for later use:

func onDownloadListClicked() {
    // Download a list of Region items that will tell us what map regions are available for later download.
    _ = mapDownloader.getDownloadableRegions(languageCode: LanguageCode.deDe,
                                             completion: onDownloadableRegionsCompleted)
}

// Completion handler to receive search results.
func onDownloadableRegionsCompleted(error: MapLoaderError?, regions: [Region]?) {
    if let mapLoaderError = error {
        self.showMessage("Downloadable regions error: \(mapLoaderError)")
        return
    }

    // If error is nil, it is guaranteed that the list will not be nil.
    downloadableRegions = regions!

    for region in downloadableRegions {
        print(region.name)
        guard let childRegions = region.childRegions else {
            continue
        }

        // Note that this code ignores to list the children of the children (and so on).
        for childRegion in childRegions {
            let sizeOnDiskinMB = childRegion.sizeOnDiskInBytes / (1024 * 1024)
            print("Child region: \(childRegion.name), ID: \(childRegion.regionId.id), Size: \(sizeOnDiskinMB) MB")
        }
    }

    self.showMessage("Found \(downloadableRegions.count) continents with various countries. Full list: \(downloadableRegions.description).")
}

Note

The response contains either an error or a result: mapLoaderError and regions list can never be nil at the same time - or non-nil at the same time.

Each region can contain child regions. For example, Europe contains Germany, France and Switzerland - and many more child regions. The sizeOnDiskInBytes parameter tells you how much space the downloaded map will occupy on the device's file system when it's uncompressed after download has completed. It makes sense to show this to the user before starting the download - as the available space on a device may be limited.

Note: The getDownloadableRegions() method provides a TaskHandle as return value to cancel the request. For the above code snippet we do not use this and fulfill the contract with an underscore ("_ = ...") instead.

Screenshot: Showing an example how downloadable maps could be indicated to users.

Download a Region

Once you know the RegionId, you can use it to start downloading the map data. Each Region instance contains a localized name and other data, such as the size of the downloaded map. When the map data is downloaded, all data is compressed and will be unpacked automatically onto the device's disk once the download is complete.

Below we search the downloaded list of regions to find the Region element for Switzerland. Note that we have requested the region list to be localized in German in the step above:

// Finds a region in the downloaded region list.
// Note that we ignore children of children (and so on).
private func findRegion(localizedRegionName: String) -> Region? {
    var downloadableRegion: Region?
    for region in downloadableRegions {
        if region.name == localizedRegionName {
            downloadableRegion = region
            break
        }
        guard let childRegions = region.childRegions else {
            continue
        }
        for childRegion in childRegions {
            if childRegion.name == localizedRegionName {
                downloadableRegion = childRegion
                break
            }
        }
    }

    return downloadableRegion
}

Once we know the Region, we can use its RegionId to start the download. We pass the unique ID into a list, so we can download multiple regions with the same request. Here, we download only one region:

func onDownloadMapClicked() {
    // Find region for Switzerland using the German name as identifier.
    // Note that we requested the list of regions in German above.
    let swizNameInGerman = "Schweiz"
    let swizRegion = findRegion(localizedRegionName: swizNameInGerman)

    guard let region = swizRegion else {
        showMessage("Error: The Swiz region was not found. Click 'Regions' first to download the list of regions.")
        return
    }

    // For this example we only download one country.
    let regionIDs = [region.regionId]
    let mapDownloaderTask = mapDownloader.downloadRegions(regions: regionIDs,
                                                          statusListener: self)
    mapDownloaderTasks.append(mapDownloaderTask)
}

// Conform to the DownloadRegionsStatusListener protocol.
func onDownloadRegionsComplete(error: MapLoaderError?, regions: [RegionId]?) {
    if let mapLoaderError = error {
        self.showMessage("Download regions completion error: \(mapLoaderError)")
        return
    }

    // If error is nil, it is guaranteed that the list will not be nil.
    // For this example we downloaded only one hardcoded region.
    showMessage("Map download completed 100% for Switzerland! ID: \(String(describing: regions!.first))")
}

// Conform to the DownloadRegionsStatusListener protocol.
func onProgress(region: RegionId, percentage: Int32) {
    showMessage("Map download progress for Switzerland. ID: \(region.id). Progress: \(percentage)%.")
}

// Conform to the DownloadRegionsStatusListener protocol.
func onPause(error: MapLoaderError?) {
    if (error == nil) {
        showMessage("The download was paused by the user calling mapDownloaderTask.pause().")
    } else {
        showMessage("Download regions onPause error. The task tried to often to retry the download: \(error.debugDescription)")
    }
}

// Conform to the DownloadRegionsStatusListener protocol.
func onResume() {
    showMessage("A previously paused download has been resumed.")
}

The DownloadRegionsStatusListener provides four events. The second one tells us the progress while the download is ongoing, while the first one notifies once the download has completed. Note that the download can also complete with a MapLoaderError. Therefore, it's worth checking if something went wrong.

Note

The response for onDownloadRegionsComplete() contains either an error or a result: mapLoaderError and regions list can never be nil at the same time - or non-nil at the same time.

The pause event notfies when a download was paused by the user or the task itself. Internally, the HERE SDK will retry to download a region when it was interrupted, ie. due to a bad network connection. If this happens too often, the MapLoaderError for onPause() is populated and the download pauses. A paused MapDownloaderTask can only be resumed by the user, which will be also indicated by the related event. Especially for larger regions it may be convenient to pause a download until the connection gets better, for example. When resumed, the download will continue at the progress where it stopped and no already downloaded map data will be lost. Note that calling downloadRegions() for a paused region will have the same effect as calling resume() on the original task and the progress will continue where it left off.

After kicking off the download, we get an immediate return value to be able to cancel the ongoing asynchronous download operation. Above, we store the MapDownloaderTask into a list, as a user might trigger the above code multiple times.

To cancel all ongoing downloads, you can use the following code snippet:

func onCancelMapDownloadClicked() {
    for mapDownloaderTask in mapDownloaderTasks {
        mapDownloaderTask.cancel()
    }
    showMessage("Cancelled \(mapDownloaderTasks.count) download tasks in list.")
    mapDownloaderTasks.removeAll()
}

Note that a MapDownloaderTask that was cancelled cannot be resumed. However, you can start a fresh download request again.

Note

You can find the OfflineMaps example app on GitHub.

Update Map Data and Map Version

With the MapUpdater class you can check if newer map versions are available and - if available - update the already installed regions. Each HERE SDK version comes hardcoded with a specific map version. This defines the map data in the cache and the downloaded regions (if any).

Note that certain HERE SDK features may require a specific map version - as indicated in the related API Reference documentation or in this guide.

When an app update is installed that integrates a new HERE SDK version, it will most likely also feature a new map version. However, the map data will not be automatically updated, unless the MapUpdater is used to perform this task. Of course, map updates can also be installed for older HERE SDK versions.

  • Check the currently used map version by calling mapUpdater.getCurrentMapVersion().
  • Check if an update is available by calling mapUpdater.retrieveCatalogsUpdateInfo().

By calling mapUpdater.updateCatalog() you can download and install the latest map data. An example for this is shown in the offline_maps_app example app on GitHub.

If no offline maps have been installed, calling mapUpdater.updateCatalog() will only clear the cache and the cache will be subsequently filled with new data for the latest available map version.

The cache will always use the same map version as offline maps. If offline maps are updated, the cache will be also updated. The cache version will never be older than the offline maps version.

Note

Once mapUpdater.updateCatalog() has completed, it is required to call getDownloadableRegions() to update the internal catalog data that is needed to download, update or delete existing Region data.

It is not possible to enforce a map update by uninstalling all downloaded regions via deleteRegions() and then downloading the desired regions again. In that case, the same map version would be downloaded. However, if updateCatalog() is executed beforehand then a map update may be indicated and can be installed.

Note

During an ongoing map update and related operations, not all MapDownloader and MapUpdater features may be accessible. While many operations can be performed in parallel, in some cases, a failure may be indicated by an error message. If this happens, wait until the current operation succeeds before trying again.

When you call deleteRegions() and you get a retryable error, then there was probably an internet connectivity issue: In this case, DownloadRegionsStatusListener fires an onPause() event - and the affected download is in a paused state. If any download is in a paused state, it is not possible to delete a region, because the storage might be in an inconsistent state: To solve this issue, unpause the download or cancel it - then try again.

When and how to execute a map update?

Map update checks can be performed at each application start. However, when region data has been installed, this may become a lengthy operation, so users should be informed. Usually, you first check for an update by calling retrieveCatalogsUpdateInfo() - this provides one or more CatalogUpdateInfo items that can be used to call updateCatalog():

private func checkForMapUpdates() {
    guard let mapUpdater = mapUpdater else {
        showMessage("MapUpdater instance not ready. Try again.")
        return
    }

    _ = mapUpdater.retrieveCatalogsUpdateInfo(callback: onCatalogUpdateCompleted)
}

// Completion handler to get notified whether a catalog update is available or not.
private func onCatalogUpdateCompleted(mapLoaderError: MapLoaderError?, catalogList: [CatalogUpdateInfo]?) {
    if let error = mapLoaderError {
        print("CatalogUpdateCheck Error: \(error)")
        return
    }

    // When error is nil, then the list is guaranteed to be not nil.
    if catalogList!.isEmpty {
        print("CatalogUpdateCheck: No map updates are available.");
    }

    logCurrentMapVersion();

    for catalogUpdateInfo in catalogList! {
        print("CatalogUpdateCheck - Catalog name:" + catalogUpdateInfo.installedCatalog.catalogIdentifier.hrn);
        print("CatalogUpdateCheck - Installed map version: \(String(describing: catalogUpdateInfo.installedCatalog.catalogIdentifier.version))");
        print("CatalogUpdateCheck - Latest available map version: \(catalogUpdateInfo.latestVersion)");
        performMapUpdate(catalogUpdateInfo: catalogUpdateInfo);
    }
}

// Downloads and installs map updates for any of the already downloaded regions.
// Note that this example only shows how to download one region.
private func performMapUpdate(catalogUpdateInfo: CatalogUpdateInfo) {
    guard let mapUpdater = mapUpdater else {
        showMessage("MapUpdater instance not ready. Try again.")
        return
    }

    // This method conveniently updates all installed regions if an update is available.
    // Optionally, you can use the returned CatalogUpdateTask to pause / resume or cancel the update.
    _ = mapUpdater.updateCatalog(catalogInfo: catalogUpdateInfo, completion: catalogUpdateListenerImpl)
}

private let catalogUpdateListenerImpl = CatalogUpdateListenerImpl()

private class CatalogUpdateListenerImpl : CatalogUpdateProgressListener {
    // Conform to the CatalogUpdateProgressListener protocol.
    func onPause(error: heresdk.MapLoaderError?) {
        if let mapLoaderError = error {
            print("Catalog update onPause error. The task tried to often to retry the update: \(mapLoaderError).")
        } else {
            print("CatalogUpdate: The map update was paused by the user calling catalogUpdateTask.pause().")
        }
    }

    // Conform to the CatalogUpdateProgressListener protocol.
    func onProgress(region: RegionId, percentage: Int32) {
        print("CatalogUpdate: Downloading and installing a map update. Progress for \(region.id): \(percentage)%.")
    }

    // Conform to the CatalogUpdateProgressListener protocol.
    func onComplete(error: MapLoaderError?) {
        if let mapLoaderError = error {
            print("CatalogUpdate completion error: \(mapLoaderError)")
            return
        }
        print("CatalogUpdate: One or more map update has been successfully installed.")

        // It is recommend to call now also `getDownloadableRegions()` to update
        // the internal catalog data that is needed to download, update or delete
        // existing `Region` data. It is required to do this at least once
        // before doing a new download, update or delete operation.
    }

    // Conform to the CatalogUpdateProgressListener protocol.
    func onResume() {
        print("MapUpdate: A previously paused map update has been resumed.")
    }
}

Note that calling updateCatalog() is internally optimized to update only the parts of a Region that actually have changed. This means that for most cases an update does not require a user to redownload all packages and instead, each region is divided into several internal bundles that are only downloaded again when there was a map data change in it.

What is a catalog?

By default, one global catalog is available that contains regions for the whole world. All map data is part of downloadable regions that are referenced from a catalog. Therefore, the catalog itself does not contain map data, but only links to the data. Each catalog is identified through a HRN. A HRN is a unique HERE Resource Name identifying catalogs or other HERE resources. It can be also used to define a scope (see Key Concepts). Usually, there are only two HRN values used by the HERE SDK to identify the following two catalogs:

  • "hrn:here:data::olp-here:ocm" - A catalog containing references to all available regions, world-wide.
  • "hrn:here:data::olp-here:ocm-japan" - A catalog containing additional references to detailed map data for Japan. This is not enabled, by default. If enabled, it will enhance the base map for Japan with rich details. If your company has an agreement with HERE to use a detailed Japan map, then in this case you can install and use this second catalog, see Maps section for more details.

Keep in mind, that a catalog only contains references to the available regions. The map data for a region may differ based on the catalog that is used or on the version that is downloaded and installed. Practically, you do not update a catalog itself, but only the data that is linked to an existing catalog.

Get Current Map Version

Sometimes, it may be useful to know the map version used for the map cache and the installed Region data. You can use the following code snippet to log the currently used map version:

guard let sdkNativeEngine = SDKNativeEngine.sharedInstance else {
    fatalError("SDKNativeEngine not initialized.")
}

// Create MapUpdater in background to not block the UI thread.
MapUpdater.fromEngineAsync(sdkNativeEngine, { mapUpdater in
    let mapUpdaterInstance = mapUpdater

    do {
        let mapVersionHandle = try mapUpdaterInstance.getCurrentMapVersion()
        let versionInfo = mapVersionHandle.stringRepresentation(separator: ",")
        print("Info: \(String(describing: versionInfo))")
    } catch let mapLoaderException {
        fatalError("Get current map version failed: \(mapLoaderException.localizedDescription)")
    }
})

This information is mostly useful for debugging purposes. Note that the map version is also logged by the HERE SDK when the map view is shown for the first time.

It has the format [cache-version].[offline-maps-version],[japan-cache-version].[japan-offline-maps-version]. An example result my look like "47.47,47.47". It is not possible to get different versions for the map cache and offline maps.

Repair Broken Maps

It is not recommended to keep downloading or updating map data while an app is running in background. Note that the HERE SDK offers methods to pause() and to resume() downloads - for example, when an app is going to background or is resumed. It is recommended to inform users in such cases.

However, it may happen that an app gets closed before a map update operation can be completed - for example, due to a crash. So, in worst case an intermediate state may occur on the disk of the device.

The HERE SDK provides a convenient way to check for such issues with the getInitialPersistentMapStatus() method. It also allows to repair a broken map - if possible.

private func checkInstallationStatus() {
    // Note that this value will not change during the lifetime of an app.
    let persistentMapStatus = mapDownloader.getInitialPersistentMapStatus()
    if persistentMapStatus != PersistentMapStatus.ok {
        // Something went wrong after the app was closed the last time. It seems the offline map data is
        // corrupted. This can eventually happen, when an ongoing map download was interrupted due to a crash.
        print("PersistentMapStatus: The persistent map data seems to be corrupted. Trying to repair.")

        // Let's try to repair.
        mapDownloader.repairPersistentMap(completion: onMapRepairCompleted)
    }
}

// Completion handler to get notified whether map reparation was successful or not.
private func onMapRepairCompleted(persistentMapRepairError: PersistentMapRepairError?) {
    if persistentMapRepairError == nil {
        print("RepairPersistentMap: Repair operation completed successfully!")
        return
    }

    // In this case, check the PersistentMapStatus and the recommended
    // healing option listed in the API Reference. For example, if the status
    // is "pendingUpdate", it cannot be repaired, but instead an update
    // should be executed. It is recommended to inform your users to
    // perform the recommended action.
    print("RepairPersistentMap: Repair operation failed: \(String(describing: persistentMapRepairError))")
}

Note

It is recommended to inform the user that there might be an issue with the downloaded map data. Such a dialog can be shown on app side before performing a repair operation or any other follow-up action that might be necessary if the repair operation fails. However, calling getInitialPersistentMapStatus() and repairPersistentMap() can be performed silently to see if such a notification is necessary or not.

In worst case, if the repair operation fails, the map data needs to be removed and downloaded again. You can try to call deleteRegions() programmatically and clear the map cache via SDKCache by calling clearCache​(). In this case, it is recommend to notify the user and to restart the application.

Alternatively, you can manually delete the data: The path for downloaded regions and the cache can be retrieved from the SDKOptions via the persistentMapStoragePath and cachePath properties.

Indexing

In order to improve search results for the OfflineSearchEngine you can set OfflineSearchIndex.Options. By default, indexing is disabled.

You can set a OfflineSearchIndexListener to track the index building process.

Indexing provides a mechanism to find places in installed Region data - even if they are far away from the provided search center. This will help to match the behavior of the SearchEngine which does this, by default, without the need to create a search index as it operates only online.

Note

This is a beta release of this feature, so there could be a few bugs and unexpected behaviors. APIs may change for new releases without a deprecation process.

Internally, indexing creates additional data on the device to make all installed map content searchable.

If indexing is enabled it will affect all operations that modify persistent storage content:

  • mapDownloader.downloadRegions(..)
  • mapDownloader.deleteRegions(..)
  • mapDownloader.clearPersistentMapStorage(..)
  • mapDownloader.repairPersistentMap(..)
  • mapDownloader.performMapUpdate(..)
  • mapDownloader.performFeatureUpdate(..)
  • mapDownloader.updateCatalog(..)

These methods will ensure that the index is created, deleted or updated to contain entries from Region data that are installed in the persistent storage.

Creating an index takes time and this will make all operations that download or update offline maps longer, but usually not more than a few seconds up to a couple of minutes (depending on the amount of installed offline maps data). The stored index also increases the space occupied by offline maps by around 5%.

Prefetch Map Data

You can use a RoutePrefetcher to load map data into the cache in advance - for example, to improve a turn-by-turn navigation experience.

Look here for more details.

Questions And Answers

  • How does offline search work? By default, the OfflineSearchEngine searches in the persistent offline map data, but in case there is no Region loaded, it will try to use the map cache. Find out more about this here.

  • How does offline routing work? Offline routing is intended to be used with offline maps and not solely with cached map data. If no offline map data is available, the OfflineRoutingEngine tries to find the missing data in the map cache. If map data is missing for any part of a route, then the route calculation will fail. Find out more about this here.

  • How is the map cache different from offline maps? Caching happens automatically, when the map view is panned. On top, with the RoutePrefetcher you can also request data to be fetched into the map cache. The data in the map cache is not meant to be stored permanently and it can be replaced with newer data when there's a need for this - for example, when the cache is full. More about the cache can be found here. Offline maps, on the other hand, are persisted and they are stored at a different path on the device. With dedicated APIs like the MapDownloader, specific areas in the world can be downloaded permanently - and also updated or deleted upon a user's request.

  • Can there be a mismatch of data when I use the online RoutingEngine? When you are using the RoutingEngine, then the calculated route will be always based on the latest available map data - even if on the device itself an older map version is used. For example, a route may take a newly built bridge which is not known in older map versions. This will mostly affect the visual appearance of the map. For example, the bridge may not be rendered on the map view as expected. However, the navigation experience will still be based on the new route - even if the device operates fully offline - or if offline maps for that region have been already installed (and not updated yet). Either way, the navigator will still provide guidance based on the route and navigate over the bridge. The navigator will also not request any new data when it finds existing offline or cached map data - even if the device is online. However, in order to avoid potential mismatches it can help to update to the latest map versions with the MapUpdater before starting a trip. Note that when the provided location from your GPS source is close enough to the route, then no map-matching is done to save resources. Only when the location is farther away from the route, then the HERE SDK will try to map-match the location to a street.

  • Is it possible to pre-install offline maps on a device? Upon request, it is possible to manually create such packages to preload map data. However, there is no API available for this and you need to contact us to clarify the details.

results matching ""

    No results matching ""