Beta
Maps SDK for Android v10

Offline

If your user base spends most of their time off the grid, use the Mapbox Maps SDK's offline features to download and store pre-selected regions for use when there is a loss of connectivity. The maps they download will be fully functional and include the styles, tiles, and other resources you specify.

An offline map requires two main components:

  1. Style data, which is organized into style packs. Use the Offline Manager API to manage style packs.
  2. Map data, which is organized into preassembled tile packs. Use the tile store to manage tile packs.

These are a few key terms and concepts you may need to reference when working with offline maps:

  • Offline Manager: The Offline Manager API provides a configuration interface and entrypoint for offline map functionality. It is used to manage style packs and to produce tileset descriptors that can be used with a tile store.
  • Tileset descriptor: A tileset descriptor contains metadata about the tilesets, zoom ranges, and pixel ratio that cached tile packs should include. You can create tileset descriptors with the Offline Manager. Each time you create a tile pack, you will provide both a tileset descriptor and a tile region before tile data can be fetched for offline use.
  • Tile Region: A tile region is a geographic area for which offline data is needed. Tile regions are used by tile stores to fetch and manage tile packs necessary to provide offline support in that area. To create a tile region, a developer must specify a geometry describing each region's boundaries, source tileset(s), and zoom range(s).
  • Tile Pack: Tile packs are binary files made up of a parent tile and its descendant tiles covering a specific range of zoom levels and over a predefined geographic area. Because they have less per-tile and per-connection overhead more efficient to transfer over the network and store on the file system than individual tiles. Tile packs only store tiles. The Offline Manager API handles metadata and descriptors related to tilesets.
  • Tile Store: The tile store manages retrieving and organizing tile packs. By default, the Maps SDK only uses the tile store for tile regions. The tile store does not share tiles with the disk cache (see note on disk cache below). Although the Offline Manager handles creating tileset descriptors, it is the job of the tile store to manage tile regions. You should retain the TileStore instance by keeping a reference to it while using the SDK to avoid unnecessary re-initialization and possible loss of options you provided earlier. When a user creates a tile store instance, the user is responsible for initializing the TileStore with proper options(using TileStore#setOption() API), including an access token.
  • Style pack: A style pack contains a map style and the non-tile resources it depends upon, including loaded sources, fonts, and sprites. Collectively, these resources are used to transform map data stored in tiles into a rendered map with a particular look and feel. Style packs are identified by their style URL. Style packs are stored in the disk cache that is employed during normal online map use, but their resources are not subject to the processes that typically remove objects from the cache to control its size.
Disk cache

When a user loads and interacts with a map on their device, any visible tiles and style resources (style JSON, fonts, sprites, etc.) are placed in the device's disk cache, which is located in the maps data directory defined in the ResourceOptions#dataPath. The disk cache observes when these resources are used and makes intelligent guesses about which resources may be needed again. When a user revisits a place on the map, it will load more quickly if the associated resources are still present in the disk cache, because the device will not have to request those resources from the server to render the map. Resources that do not belong to a style pack are not permanently stored in the disk cache. There are no guarantees on how long these resources will be cached, but the least-recently used resources are normally removed to make room for newer resources.

Limits

The cumulative amount of unique maps tile packs used in the offline regions cannot be greater than 750. The SDK will not perform tile region loading if it would lead to exceeding the tile pack limit.

Our terms of service do not allow developers or end users to redistribute offline maps downloaded from Mapbox servers. Users of the SDK must retrieve Mapbox data intended for offline use from Mapbox servers--the data may not be preloaded, bundled or otherwise redistributed.

Specifying the style and areas that will be used offline

To render a complete map in an offline environment, the device needs to fetch two main components: a style and the required map tiles, each of which needs to be configured ahead of time.

Define a style pack

To render a map offline, the SDK must be able to fetch the non-tile resources that the map requires. This includes loaded sources, fonts, styles and sprites. The SDK calculates which resources it needs based on the StylePackLoadOptions you specify and pass to the Offline Manager API.

val stylePackLoadOptions = StylePackLoadOptions.Builder()
        .glyphsRasterizationMode(GlyphsRasterizationMode.IDEOGRAPHS_RASTERIZED_LOCALLY)
        .metadata(Value(STYLE_PACK_METADATA))
        .build()

Define a tileset descriptor and tile region

The tileset descriptor associates tile data (stored in a tile pack) to a given style (stored in a style pack). Use the tileset descriptor to define a tileset's zoom ranges and pixel ratio.

The tile region is a geographic region and its metadata. It is used to calculate which tile packs will be necessary to provide offline functionality in that region.

val offlineManager: OfflineManager = OfflineManager(MapInitOptions.getDefaultResourceOptions(this))

val tilesetDescriptor = offlineManager.createTilesetDescriptor(
  TilesetDescriptorOptions.Builder()
    .styleURI(Style.OUTDOORS)
    .minZoom(0)
    .maxZoom(16)
    .build()
)
val tileRegionLoadOptions = TileRegionLoadOptions.Builder()
    .geometry(TOKYO)
    .descriptors(listOf(tilesetDescriptor))
    .metadata(Value(TILE_REGION_METADATA))
    .acceptExpired(true)
    .networkRestriction(NetworkRestriction.NONE)
    .build()

Metadata

You may provide arbitrary metadata for both style packs and tile regions. You may want to provide metadata to distinguish the various regions your users download from one another or for other record-keeping requirements within your app. This data will be associated with the style packs and regions that the system fetches and stores locally. While optional, providing a metadata object with at least a region name is encouraged. In addition to a region name, you can store any arbitrary serialisable information you'd like as long as it can be wrapped into a Value class. For example, you might wish to store the record ID of a travel itinerary that a user has created in another part of your app, and for which the offline region is being retrieved, to link the two together. The contents of the metadata you specify will only be available inside your app and will not affect your tile region download.

Add metadata to the style pack with StylePackLoadOptions.

val stylePackLoadOptions = StylePackLoadOptions.Builder()
      ...
      .metadata(Value(STYLE_PACK_METADATA))
      .build()

Add metadata to the tile region with TileRegionLoadOptions.

val tileRegionLoadOptions = TileRegionLoadOptions.Builder()
      ...
      .metadata(Value(TILE_REGION_METADATA))
      .build()

Working with downloads

Once the above steps are complete, the system has the information necessary to know what to resources must be downloaded. The next step is to start downloads for each of your style packs and tile packs.

Download a style pack

Now that the StylePackLoadOptions has been created, you can use the OfflineManager to create a style pack asynchronously by calling loadStylePack. loadStylePack returns a Cancelable object, allowing you to abort the download by calling cancel().

To initialize the download, pass in the StylePackLoadOptions you created in both the Define a style pack and Metadata sections.

This will provide you with two callbacks:

  1. StylePackLoadProgressCallback tracks the progress of the download.
  2. StylePackCallback tracks the completion of the download or any errors that occur. It returns a StylePack object when the download completes successfully, which you can use to check the downloaded style package, including its expiration date.
val stylePackCancelable = offlineManager.loadStylePack(
  Style.OUTDOORS,
  // Build Style pack load options
  stylePackLoadOptions,
  { progress ->
    // Handle the download progress
  },
  { expected ->
    if (expected.isValue) {
      expected.value?.let { stylePack ->
        // Style pack download finished successfully
      }
    }
    expected.error?.let {
      // Handle errors that occurred during the style pack download.
    }
  }
)
// Cancel the download if needed
stylePackCancelable.cancel()

Download a tile region

In the previous step, you used the Offline Manager API to download a style pack. You will use the tile store in a similar way to download any tile packs required to render your map offline.

To create an asynchronous tile region download, call the tile store's loadTileRegion method and pass in the TileRegionLoadOptions you created in the Define a tileset descriptor and tile region and Metadata sections. loadTileRegion will return a Cancelable object, which may be used to abort the download.

loadTileRegion will provide you with two callbacks:

  1. TileRegionLoadProgressCallback tracks the download progress.
  2. TileRegionCallback tracks the completion of the download or any errors that occur. It provides a TileRegion object when the download completes successfully, which you can use to check the downloaded tile region, including its expiration date.
// You need to keep a reference of the created tileStore and keep it during the download process.
// You are also responsible for initializing the TileStore properly, including setting the proper access token.
val tileStore = TileStore.create().also {
      // Set default access token for the created tile store instance
      it.setOption(
        TileStoreOptions.MAPBOX_ACCESS_TOKEN,
        TileDataDomain.MAPS,
        Value(getString(R.string.mapbox_access_token))
      )
    }
val tileRegionCancelable = tileStore.loadTileRegion(
  TILE_REGION_ID,
  TileRegionLoadOptions.Builder()
    .geometry(TOKYO)
    .descriptors(listOf(tilesetDescriptor))
    .metadata(Value(TILE_REGION_METADATA))
    .acceptExpired(true)
    .networkRestriction(NetworkRestriction.NONE)
    .build(),
  { progress ->
    // Handle the download progress
  }
) { expected ->
  if (expected.isValue) {
    // Tile region download finishes successfully
    }
  }
  expected.error?.let {
    // Handle errors that occurred during the tile region download.
  }
}

// Cancel the download if needed
tileRegionCancelable.cancel()

Manage offline data

Once a download has completed, you may want to give your users the option to view and manage their downloads in one place. The Offline Manager API and tile store each provide methods for listing and managing offline data.

List offline style packs and regions

Call OfflineManager#getAllStylePacks to get a list of available style packs. This will return an object (Expected) that includes a list of available style packs or an error.

// Get a list of style packs that are currently available.
offlineManager.getAllStylePacks { expected ->
  if (expected.isValue) {
    expected.value?.let { stylePackList ->
      Log.d("Existing style packs: $stylePackList")
    }
  }
  expected.error?.let { stylePackError ->
    Log.e("StylePackError: $stylePackError")
  }
}

Similarly, use TileStore#getAllTileRegions to get a list of available tile regions. This will return an object (Expected) that includes a list of available tile regions or an error.

 // Get a list of tile regions that are currently available.
tileStore.getAllTileRegions { expected ->
  if (expected.isValue) {
    expected.value?.let { tileRegionList ->
      Log.d("Existing tile regions: $tileRegionList")
    }
  }
  expected.error?.let { tileRegionError ->
    Log.e("TileRegionError: $tileRegionError")
  }
}

Update a style pack

To maintain the integrity of a style package, the map style that belongs to an offline pack will not be automatically updated if a user returns to an area with network services and views the map while connected. This includes loaded styles and sprites.

You can manually refresh style packs using OfflineManager#loadStylePack, passing it the same StyleUri and an empty StylePackLoadOptions (note that if the acceptExpired flag is set the existing outdated resources will not be refreshed). During the refresh, any missing resources will be loaded and expired resources will be updated.

Update a region

Downloaded tile regions can be refreshed using the same TileStore#loadTileRegion call you made when you initialized your download. Provide the same tile region ID (TILE_REGION_ID in the download initialization example above) and an empty TileRegionLoadOptions (note that if the acceptExpired flag is set the existing outdated resources will not be refreshed). During the refresh, any missing resources will be loaded and any expired resources will be updated.

Normally, the map does not load new tile packs from the network, unless ResourceOptions::TileStoreUsageMode is set to READ_AND_UPDATE. If ResourceOptions::TileStoreUsageMode is set to READ_AND_UPDATE, the map will replace the outdated visible tile packs with fresh ones for all the tile regions that refer to these tile packs.

Delete style packs and regions

To remove offline data from the database, you must first receive the list of tile regions as explained in the List offline styles packs and regions section. Once this list has been retrieved it may be used to select the style pack and/or tile regions to be deleted.

Delete a style pack using the OfflineManager#removeStylePack API and the style's URI.

offlineManager.removeStylePack(StyleUri)

Note this may not immediately delete the downloaded style pack. Instead, it will mark the resources as not being a part of an offline style pack and they will be removed from the disk cache during its normal cleanup process.

You can delete a tile region with TileStore#removeTileRegion.

tileStore.removeTileRegion(TILE_REGION_ID)

Note that this may not immediately delete the downloaded tile packs. Instead, it will mark the tileset as not being a part of an offline tile region and they will be removed from the disk cache during its normal cleanup process.

You can fully remove tiles that have been downloaded by setting the disk quota to zero. This will make sure tile regions are fully removed.

// This removes the tiles that do not belong to any tile regions.
tileStore.setOption(TileStoreOptions.DISK_QUOTA, Value(0))

Understanding and configuring offline behavior

Understanding the default behavior and options of the Maps SDK's offline functionality can allow you to optimize your app's offline functionality and manage it more flexibly.

Each Map instance stores files for both the disk cache and tile store in a default location (normally, inside the application cache folder). To override the default, you can provide a custom data path for the disk cache and a tile store instance. There is only ever one tile store for each unique file path.

The Offline Manager can be configured using ResourceOptions. You can completely disable the tile store by setting ResourceOptions.TileStoreUsageMode to DISABLED. In this case, a tile store will not be created and tile region functionality will not be available.

By default, a tile store is read-only and the Maps SDK only interacts with it to check for tiles it needs. If the necessary tile is not in the tile store, the SDK will request the individual tile and store it in the disk cache instead of the tile store. By default, the SDK will not fetch tile packs in scenarios like this, where a tile is not available locally and must be retrieved.

You can enable implicit tile pack loading by setting the ResourceOptions.TileStoreUsageMode resource option to READ_AND_UPDATE. When set to READ_AND_UPDATE, the map engine will use the tile store for loading tile packs rather then load tiles individually. Only tile packs will be used. This option can be useful if the map trajectory is predefined and the user cannot pan freely (e.g. navigation use cases). In these circumstances, it is more efficient to use tile packs for all map data loading instead of allowing the map to request arbitrary tiles.

Tile store disk quota

The maximum size of a tile store may be configured. When this quota is reached, the least-recently used tile packs that aren't shared by another tile region will be removed to make room for newer tile packs.

If the tile store is only used for tile regions and there is no need to keep tile packs bound to those regions, you can set the tile store disk quota to zero, forcing disk clean up after a tile region is deleted.

Tile count granularity

The tile store loads and stores tiles in tile packs. Each tile pack has a pre-defined zoom range and contains all child tiles within that range. The currently used tile pack zoom ranges are:

  • Global coverage: 0 - 5
  • Regional information: 6 - 10
  • Local information: 11 - 14
  • Streets detail: 15 - 16

When specifying minimum and maximum zoom levels as part of the TilesetDescriptorOptions, the actual zoom levels of the retrieved data will be determined by these ranges. For example, if you set minZoom to 8 and maxZoom to 15, the downloaded tiles will include zoom levels ranging from 6 to 16.

Supported tiled sources

Tile regions support only the tiled sources whose tile endpoint URLs correspond to the Mapbox Vector/Raster tile v4 URL schema. Your tiled source probably uses the v4 URL schema unless it is more than a few years old.