Offline
There are two main kinds of offline functionality: predictive caching and offline regions. Predictive caching is fully automatic and based on the driver’s behavior. It allows offline navigation around the user’s current location, the destination, and the route itself. Offline regions need to be created and loaded ahead of time, enabling routing functionality in non-connected environments.
Predictive caching
To use predictive caching, you will need to initialize a PredictiveCacheController
and, optionally, configure PredictiveCacheMapsOptions
and PredictiveCacheNavigationOptions
. Once it's configured, the Navigation SDK will automatically create caches and make them available to the user.
To start caching map data, call createStyleMapControllers
with a MapboxMap
instance and, optionally, pass a list of style URI to cache. Styles must be hosted on Mapbox, and URI must start with “mapbox://”
(for example, “mapbox://mapbox.mapbox-terrain-v2”
). If you do not pass any styles to createStyleMapControllers
, current map's style will be cached.
PredictiveCacheOptions predictiveCacheOptions = new PredictiveCacheOptions.Builder()
.predictiveCacheMapsOptionsList(predictiveCacheMapsOptions)
.predictiveCacheNavigationOptions(predictiveCacheNavigationOptions)
.build();
PredictiveCacheController predictiveCacheController = new PredictiveCacheController(predictiveCacheOptions);
// cache only passed styles for the mapboxMap instance
List<String> stylesToCache = new ArrayList<>();
stylesToCache.add("mapbox://mapbox.mapbox-terrain-v2");
boolean cacheCurrentStyle = false;
predictiveCacheController.createStyleMapControllers(mapboxMap, cacheCurrentStyle, stylesToCache);
// cache current style for the anotherMapboxMap instance
predictiveCacheController.createStyleMapControllers(anotherMapboxMap);
val predictiveCacheOptions = PredictiveCacheOptions.Builder()
.predictiveCacheMapsOptionsList(predictiveCacheMapsOptions)
.predictiveCacheNavigationOptions(predictiveCacheNavigationOptions)
.build()
val predictiveCacheController = PredictiveCacheController(predictiveCacheOptions)
// cache only passed styles for the mapboxMap instance
val stylesToCache: MutableList<String> = ArrayList()
stylesToCache.add("mapbox://mapbox.mapbox-terrain-v2")
val cacheCurrentStyle = false
predictiveCacheController.createStyleMapControllers(mapboxMap, cacheCurrentStyle, stylesToCache)
// cache current style for the anotherMapboxMap instance
predictiveCacheController.createStyleMapControllers(anotherMapboxMap)
Lifecycle
The controller and its MapboxNavigation
instance have a different lifecycle than the MapboxMap
instance, so make sure to call removeMapControllers
whenever the MapView
is destroyed to avoid leaking references or downloading unnecessary resources.
predictiveCacheController.removeMapControllers(mapboxMap);
predictiveCacheController.removeMapControllers(mapboxMap)
When the navigation session is finished and predictive caching is no longer needed, call onDestroy
to remove all map and navigation references.
@Override
protected void onDestroy() {
predictiveCacheController.onDestroy();
mapView.onDestroy();
mapboxNavigation.onDestroy();
super.onDestroy();
}
override fun onDestroy() {
predictiveCacheController.onDestroy()
mapView.onDestroy()
mapboxNavigation.onDestroy()
super.onDestroy()
}
Configure predictive caching
When predictive caching is enabled, the Navigation SDK will create a cache of data within three configurable boundaries from PredictiveCacheLocationOptions
(passed via PredictiveCacheNavigationOptions
):
- Radius around the user's location. Defaults to 20,000 meters. Use
currentLocationRadiusInMeters
to configure this value. - Buffer around the route. Defaults to 5,000 meters. Use
routeBufferRadiusInMeters
to configure this value. - Radius around the destination. Defaults to 50,000 meters. Use
destinationLocationRadiusInMeters
to configure this value.
To configure predictive caching, pass values, in meters, to any of the PredictiveCacheLocationOptions
.
PredictiveCacheLocationOptions predictiveCacheLocationOptions = new PredictiveCacheLocationOptions.Builder()
.currentLocationRadiusInMeters(1000)
.routeBufferRadiusInMeters(2000)
.destinationLocationRadiusInMeters(3000)
.build();
PredictiveCacheNavigationOptions predictiveCacheNavigationOptions = new PredictiveCacheNavigationOptions.Builder()
.predictiveCacheLocationOptions(predictiveCacheLocationOptions)
.build();
PredictiveCacheOptions predictiveCacheOptions = new PredictiveCacheOptions.Builder()
.predictiveCacheNavigationOptions(predictiveCacheNavigationOptions)
.build();
PredictiveCacheController predictiveCacheController = new PredictiveCacheController(predictiveCacheOptions);
val predictiveCacheLocationOptions = PredictiveCacheLocationOptions.Builder()
.currentLocationRadiusInMeters(1000)
.routeBufferRadiusInMeters(2000)
.destinationLocationRadiusInMeters(3000)
.build()
val predictiveCacheNavigationOptions = PredictiveCacheNavigationOptions.Builder()
.predictiveCacheLocationOptions(predictiveCacheLocationOptions)
.build()
val predictiveCacheOptions = PredictiveCacheOptions.Builder()
.predictiveCacheNavigationOptions(predictiveCacheNavigationOptions)
.build()
val predictiveCacheController = PredictiveCacheController(predictiveCacheOptions)
TileStore
handles offline regions data for both navigation and maps. By default, there is no limit to the size of a tile store. A quota can be configured with the TileStoreOptions.DISK_QUOTA
option.
TileStore
will refuse to store new data on disk if it would cause the quota to be exceeded. To make room for new content, tiles packs with the nearest expiration dates that aren't shared by another tile region will be removed before the quota is reached. By default, the eviction threshold is 50 MB or 10% of the TileStoreOptions.DISK_QUOTA
(whichever is smaller). If your disk quota is 100 MB then eviction threshold will be 10 MB (it will try to keep TileStore
size < 90 MB). And if the disk quota is 10 GB then default eviction threshold is 50 MB.
For instance, if you set TileStoreOptions.DISK_QUOTA
to 600 MB then nothing will be removed until the tile store size reaches 550 MB. After that, tile store will start evicting tile packs with the nearest expiration date which are not part of any region to try to keep the size under 550 MB. In case all tile packs belong to some region and nothing can be evicted, tile store may keep growing and use the entire quota of 600 MB. After that no new tile packs can be added to the tile store and some regions would need to be removed or TileStoreOptions.DISK_QUOTA
would need to be increased.
TileStore tileStore = TileStore.create();
tileStore.setOption(TileStoreOptions.DISK_QUOTA, new Value(600 * 1024 * 1024));
val tileStore = TileStore.create()
tileStore.setOption(TileStoreOptions.DISK_QUOTA, Value(600 * 1024 * 1024))
Estimate predictive caching data use
Data usage depends on the configured radius and on map style. For the Mapbox Streets style, cache size is:
- Berlin, 15km radius: 136 MB maps, 42 MB navigation
- Chicago, 15km radius: 74 MB maps, 43 MB navigation
- London, 15km radius: 145 MB maps, 90 MB navigation
- Atlanta, 100 miles radius: 659 MB maps, 156 MB navigation
Offline regions
TileStore
allows users to create and download offline regions ahead of time, enabling routing functionality in non-connected environments. In areas with no cellular connectivity, or on a device with no SIM card, your users can use turn-by-turn navigation and request new routes. If they go off-route, the system can reroute and keep them headed to their destination without requiring network connectivity. TileStore
copies the routing data from the server onto the user’s device, so there’s no need to make HTTP API calls for routing information.
TileStore
handles offline regions data for both navigation data for generating routes and map tiles for displaying a map in your application. Find more details on how to use TileStore
for map data in the Maps SDK documentation.Initialize TileStore
instance
To initialize a TileStore
instance, provide a path to a cache directory where tiles will be stored.
TileStore tileStore = TileStore.create("path/to/cache/directory/");
val tileStore = TileStore.create("path/to/cache/directory/")
Pass TileStore
instance to maps and navigation
After the TileStore
instance is initialized, pass it to both MapboxNavigation
and MapView
via the corresponding options.
TileStore
instance to the Maps SDK's ResourceOptions
and the Navigation SDK's RoutingTilesOptions
. This will guarantee the same tile regions will be used for both maps-related and navigation-related data.Pass the TileStore
instance to RoutingTilesOptions
options and pass those options to NavigationOptions
to be applied to the MapboxNavigation
object:
RoutingTilesOptions routingTilesOptions = new RoutingTilesOptions.Builder()
.tileStore(tileStore)
.build();
NavigationOptions navOptions = new NavigationOptions.Builder(context)
.routingTilesOptions(routingTilesOptions)
.build();
MapboxNavigation mapboxNavigation = MapboxNavigationProvider.create(navOptions);
val routingTilesOptions= RoutingTilesOptions.Builder()
.tileStore(tileStore)
.build()
val navOptions = NavigationOptions.Builder(context)
.routingTilesOptions(routingTilesOptions)
.build()
val mapboxNavigation = MapboxNavigationProvider.create(navOptions)
Pass the TileStore
instance to MapboxMapsOptions
options to be applied to the MapView
object:
MapboxMapsOptions.setTileStore(tileStore);
MapboxMapsOptions.tileStore = tileStore
Download a tile region
To load a new tile region or update an existing one, create an asynchronous tile region download by calling the tile store's loadTileRegion
method and passing in the TileRegionLoadOptions
.
To create a new tile region, start by defining TileRegionLoadOptions
. You must provide at least two pieces of information to TileRegionLoadOptions
:
geometry
:GeoJson
object describing some area (for examplePolygon
orMultiPolygon
).descriptors
: tileset descriptor is a bundle that encapsulates tilesets creation for the tile store implementation. Maps tileset descriptor contains metadata about the tilesets, zoom ranges, and pixel ratio that cached tile packs should include. Navigation tileset descriptor contains version of the navigation tiles that cached tile packs should include.
If one or both pieces of information are missing, the load request will fail with RegionNotFound
error.
To begin loading the new tile region, pass the options to loadTileRegion
. Each tile region requires a unique tile region id. This can be any alphanumeric string. To create a new tile region, provide a new tile region id.
To update an existing tile region, use the tile region id of the existing region. When you call loadTileRegion
, expired resources will be updated and any missing resources will be loaded to the existing offline region. When updating a tile region, you do not have to provide TileRegionLoadOptions
, but you can if you need to alter the geometry
or descriptors
. A failed load request can be reattempted with another loadTileRegion()
call.
If there is already a pending loading operation for the tile region with the given id the pending loading operation will fail with an error of Canceled
type.
OfflineManager offlineManager = new OfflineManager();
TilesetDescriptor mapsTilesetDescriptor = offlineManager.createTilesetDescriptor(
new TilesetDescriptorOptions.Builder()
.styleURI(Style.OUTDOORS)
.minZoom((byte) 0)
.maxZoom((byte) 16)
.build()
);
TilesetDescriptor navTilesetDescriptor = mapboxNavigation.getTilesetDescriptorFactory().getLatest();
List<TilesetDescriptor> tilesetDescriptors = new ArrayList<>();
tilesetDescriptors.add(mapsTilesetDescriptor);
tilesetDescriptors.add(navTilesetDescriptor);
TileRegionLoadOptions tileRegionLoadOptions = new TileRegionLoadOptions.Builder()
.geometry(TOKYO)
.descriptors(tilesetDescriptors)
.build();
Cancelable tileRegionCancelable = tileStore.loadTileRegion(
TILE_REGION_ID,
tileRegionLoadOptions,
(TileRegionLoadProgressCallback) progress -> { /* Handle the download progress */ },
(TileRegionCallback) expected -> { /* Handle the download result */ }
);
val offlineManager = OfflineManager()
val mapsTilesetDescriptor = offlineManager.createTilesetDescriptor(
TilesetDescriptorOptions.Builder()
.styleURI(Style.OUTDOORS)
.minZoom(0)
.maxZoom(16)
.build()
)
val navTilesetDescriptor = mapboxNavigation.tilesetDescriptorFactory.getLatest()
val tileRegionLoadOptions = TileRegionLoadOptions.Builder()
.geometry(TOKYO)
.descriptors(listOf(mapsTilesetDescriptor, navTilesetDescriptor))
.build()
val tileRegionCancelable = tileStore.loadTileRegion(
TILE_REGION_ID,
tileRegionLoadOptions,
{ progress ->
// Handle the download progress
},
{ expected ->
// Handle the download result
},
)
List tile regions
Call TileStore#getAllTileRegions
to get a list of available tile regions. This will return either an object (Expected
) that includes a list of available tile regions or an error.
TileStore
-controlled worker thread. It is the responsibility of the developer to dispatch them to a controlled thread.
tileStore.getAllTileRegions(expected -> {
// Handle regions
});
tileStore.getAllTileRegions { expected ->
// Handle regions
}
Delete tile regions
You can delete a tile region with TileStore#removeTileRegion
.
tileStore.removeTileRegion(TILE_REGION_ID);
tileStore.removeTileRegion(TILE_REGION_ID)
Observe tile regions state
You can add TileStoreObserver
to be notified when the state of any tile region changes.
TileStoreObserver tileStoreObserver = new TileStoreObserver() {
@Override
public void onRegionLoadProgress(@NonNull String id, @NonNull TileRegionLoadProgress progress) {
// Called whenever the load progress of a TileRegion changes.
}
@Override
public void onRegionLoadFinished(@NonNull String id, @NonNull Expected<TileRegionError, TileRegion> region) {
// Called once a TileRegion load completes successfully, or is aborted due to cancellation or errors.
}
@Override
public void onRegionRemoved(@NonNull String id) {
// Called when a TileRegion was removed
}
@Override
public void onRegionGeometryChanged(@NonNull String id, @NonNull Geometry geometry) {
// Called when the geometry of a TileRegion was modified.
}
@Override
public void onRegionMetadataChanged(@NonNull String id, @NonNull Value value) {
// Called when the user-provided metadata associated with a TileRegion was changed.
}
};
tileStore.addObserver(tileStoreObserver);
// remove observer when you don't need it
tileStore.removeObserver(tileStoreObserver);
val tileStoreObserver = object : TileStoreObserver() {
override fun onRegionLoadProgress(id: String, progress: TileRegionLoadProgress) {
// Called whenever the load progress of a TileRegion changes.
}
override fun onRegionLoadFinished(id: String, region: Expected<TileRegionError, TileRegion>) {
// Called once a TileRegion load completes successfully, or is aborted due to cancellation or errors.
}
override fun onRegionRemoved(id: String) {
// Called when a TileRegion was removed
}
override fun onRegionGeometryChanged(id: String, geometry: Geometry) {
// Called when the geometry of a TileRegion was modified.
}
override fun onRegionMetadataChanged(id: String, value: Value) {
// Called when the user-provided metadata associated with a TileRegion was changed.
}
})
tileStore.addObserver(tileStoreObserver)
// remove observer when you don't need it
tileStore.removeObserver(tileStoreObserver)