Offline
Often, you might find your user base spends most of its time off the grid. The Maps SDK enables you to download and store pre-selected regions for usage when the device goes offline. The result of downloading the map is a fully functional map using your styles, tiles, and other resources inside the downloaded region.
A user's device won't always have a strong enough internet connection to download and view map tiles. You might want to build an offline mode into your Android project to account for this situation. The Mapbox Offline Plugin for Android is a convenient way to send information to the Maps SDK's OfflineManager
class and use the manager in a background service to download map tiles for offline use. Once the offline download region is defined and initialized, the plugin handles everything else for you. Because the plugin uses a service, the downloading continues even if when your application is running in the background.
Limitations
An app can download multiple regions for offline use, but the total offline download is capped at a maximum tile count “ceiling” across all downloaded regions. The tile ceiling is set to 6,000 tiles by default but can be raised for paid plans. Use our Tile Count Estimator to calculate the number of tiles required for your offline use case. Six thousand tiles cover a region roughly the size of Greater London within the M25 at zoom levels 0–15 or the contiguous United States at zoom levels 0–9. The size of these tiles on disk will vary according to the selected style.
The Maps SDK places no limit on the number of offline regions that may be created. Your Mapbox-powered application will reuse tiles and resources that are required by multiple regions, conserving network traffic and disk space.
The SDK also places no limit on the download speed of offline regions, nor to disk space used by offline resources. The limits will depend on the storage capacity of the mobile device and the speed of the network to which it is connected.
Our terms of service do not allow you or an end user to redistribute offline maps downloaded from Mapbox servers.
Automatic tile updates
The Maps SDK downloads tiles when any connection is available, including over regular mobile data (2G, 3G, 4G, etc.). Because only individual highly-optimized tiles download, there's no risk of the user incurring an unexpected 100MB download just by opening the map in a region that's already downloaded. That is, of course, unless the user is browsing 100MB worth of tiles.
When the SDK automatically updates offline map tiles, the offline region is not re-download from scratch. The offline tile update process is the same process as with regular map tiles: The map tile's only downloaded if there's a new version of that tile.
Defining a region
Before a region can be used offline, the resources necessary for that region must be downloaded. Based on the parameters you specify when creating the region, the SDK calculates all resource requirements such as fonts, styles, and vector tiles covering the region. If more than one region in the offline database contains the fonts and styles, these will not be re-downloaded when another region's downloaded.
First, you'll need to get the offlineManager
instance, define the region to download, and finally create the definition object. It's important to note a few things:
- The offline map style must match the one being used for your
mapView
. - The definition needs the device's screen density. It's best to get this from the activities resources.
- The bounds used for the download must not go over the 6,000 tile limit.
// Set up the OfflineManagerOfflineManager offlineManager = OfflineManager.getInstance(MainActivity.this); // Create a bounding box for the offline regionLatLngBounds latLngBounds = new LatLngBounds.Builder().include(new LatLng(37.7897, -119.5073)) // Northeast.include(new LatLng(37.6744, -119.6815)) // Southwest.build(); // Define the offline regionOfflineTilePyramidRegionDefinition definition = new OfflineTilePyramidRegionDefinition(mapboxMap.getStyleUrl(),latLngBounds,10,20,MainActivity.this.getResources().getDisplayMetrics().density);
Metadata
Providing a metadata object's encouraged with at least a region name so that various regions your user's download will be distinguishable. Besides the region name, you can store any arbitrary binary region information you'd like. The contents are opaque to the SDK implementation and won't affect your offline region download, it only stores and retrieves a byte array.
// Implementation that uses JSON to store Yosemite National Park as the offline region name.byte[] metadata;try {JSONObject jsonObject = new JSONObject();jsonObject.put(JSON_FIELD_REGION_NAME, "Yosemite National Park");String json = jsonObject.toString();metadata = json.getBytes(JSON_CHARSET);} catch (Exception exception) {Log.e(TAG, "Failed to encode metadata: " + exception.getMessage());metadata = null;}
Besides creating the metadata, you can also update the information stored, allowing your users, for example, to update the region name. The offlineManager
object provides a method called updateMetadata
which takes in both the updated metadata byte array and a callback to be notified when the update is completed, or an error occurs.
Download a region
Now that the bounds and definition object are created, you can use the offlineManager to create an asynchronous download calling createOfflineRegion
. You'll need to pass in the definition and metadata objects we created in both the Defining a region and metadata sections. This will provide you with two methods, onCreate
and onError
. onError occurs if an error starting or while downloading the region occurs. The onCreate
method provides an offlineRegion
object which you can use to check the download and even display the progress to your users. If you need to pause a download, you can use the offlineRegion.setDownloadState()
to handle this.
// Create the region asynchronouslyofflineManager.createOfflineRegion(definition, metadata,new OfflineManager.CreateOfflineRegionCallback() {@Overridepublic void onCreate(OfflineRegion offlineRegion) {offlineRegion.setDownloadState(OfflineRegion.STATE_ACTIVE); // Monitor the download progress using setObserverofflineRegion.setObserver(new OfflineRegion.OfflineRegionObserver() {@Overridepublic void onStatusChanged(OfflineRegionStatus status) { // Calculate the download percentagedouble percentage = status.getRequiredResourceCount() >= 0? (100.0 * status.getCompletedResourceCount() / status.getRequiredResourceCount()) :0.0; if (status.isComplete()) {// Download completeLog.d(TAG, "Region downloaded successfully.");} else if (status.isRequiredResourceCountPrecise()) {Log.d(TAG, percentage);}} @Overridepublic void onError(OfflineRegionError error) {// If an error occurs, print to logcatLog.e(TAG, "onError reason: " + error.getReason());Log.e(TAG, "onError message: " + error.getMessage());} @Overridepublic void mapboxTileCountLimitExceeded(long limit) {// Notify if offline region exceeds maximum tile countLog.e(TAG, "Mapbox tile count limit exceeded: " + limit);}});} @Overridepublic void onError(String error) {Log.e(TAG, "Error: " + error);}});
Managing downloaded regions
Once you or your user has downloaded a region, the Maps SDK provides a few options to handle gathering list, positioning the camera inside the downloaded region, and a method for deletion of a region.
List offline regions
The listing of regions is useful for presenting downloaded information to your user or gathering information inside the code itself. The offlineManager
offers a listOfflineRegions
method which provides both a method onList
and onError
. Use the OfflineRegion
array to do all the actions in a specific region.
// Get the region bounds and zoom and move the camera.LatLngBounds bounds = ((OfflineTilePyramidRegionDefinition)offlineRegions[regionSelected].getDefinition()).getBounds();double regionZoom = ((OfflineTilePyramidRegionDefinition)offlineRegions[regionSelected].getDefinition()).getMinZoom(); // Create new camera positionCameraPosition cameraPosition = new CameraPosition.Builder().target(bounds.getCenter()).zoom(regionZoom).build(); // Move camera to new positionmapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
Delete region
To remove an offline region from the database, you'll need to first receive the list of offline regions as explained in the earlier section. The onList
method will provide you with an array of the present offline regions downloaded on the device, this object being the offlineRegions
. You'll then use this object to select the region to be deleted and call delete
on it which will provide you with a callback to be notified when the region is successfully deleted or if an error occurs.
Deleting a region will result in also removing the least recently requested resources not required by other regions until the database shrinks below a certain size.
offlineRegions[0].delete(new OfflineRegion.OfflineRegionDeleteCallback() {@Overridepublic void onDelete() {// Once the region is deleted, remove the// progressBar and display a toastprogressBar.setVisibility(View.INVISIBLE);progressBar.setIndeterminate(false);Toast.makeText(MainActivity.this, "Region deleted", Toast.LENGTH_LONG).show();} @Overridepublic void onError(String error) {progressBar.setVisibility(View.INVISIBLE);progressBar.setIndeterminate(false);Log.e(TAG, "Error: " + error);}});
Offline sideloading
Sideloading is a two-part process for making offline regions available to a mobile app. The first step is to package the tiles and resources necessary to create an offline region outside of an SDK, and the second step is to merge the created offline package into the offline database of an SDK.
This document describes how to generate these offline packages and how to make them available to your mobile app.
Applications built with the Mapbox Maps SDK for Android can download maps of pre-selected regions for use when the device does not have network connectivity. This process is documented in detail in the Offline maps troubleshooting page.
This system works well for smaller regions where you don’t have a large number of resources to download. (You can use the offline tile count estimator to understand the number of tiles in a specific region.) However, because tiles are downloaded individually, this approach can be too slow for larger regions and might result in a poor user experience. In cases like this, offline sideloading can provide a better solution.
Generate the offline package
To generate the offline package that contains the tiles and resources for a specific region, you have two options. You can either use a command line tool, or you can use the macOS graphical interface. The CLI option is appropriate for any operating system, while the macOS option is only available in macOS environments.
Note that the extent of offline capabilities in your mobile app is limited to 6,000 tiles by default. If your requirements are different, contact Mapbox sales about moving to an Enterprise plan. For more details on this "tile ceiling", see the Limitations section of this documentation.
Use the command line tool
Using a command line tool is ideal if you want to build your offline packages server-side (for example, in a container) or as part of an automated system like CI in which a graphical interface is either not necessary or not desirable.
Requirements
- In order to compile the CLI, you need to set up your development environment as described in the Building and developing from source documentation.
CLI build instructions
- Clone the Maps SDK:
git clone https://github.com/mapbox/mapbox-gl-native.git
- Change to the root folder:
cd mapbox-gl-native
- Checkout the release commit equivalent to the version of the Maps SDK that you are using:
git checkout {commit hash}
(all release commits can be found here) - Compile the binary:
make offline
Once the build is complete, you'll see the message Build Succeeded
in the terminal. The binary is available under the build
folder (for example, on a Mac the file will be in build/macos/Debug/mbgl-offline
).
Use the CLI
In the command line, move into to the folder where the mbgl-offline
file is (cd build/macos/Debug
), then run:
./mbgl-offline --north [north coordinate] --west [west coordinate] --south [south coordinate] --east [east coordinate] --minZoom [minimum zoom] --maxZoom [maximum zoom] --output [output file path] --style [style URL] --token [mapbox token]
The tiles that you requested will begin downloading.
Example command:
./mbgl-offline --north 71.44117085172385 --west -26.015625 --south 28.07198030177986 --east 28.916015625 --minZoom 4 --maxZoom 4 --output ~/europe.db --style mapbox://styles/developer/stylename --token developertoken
To see a few examples that you can reference when creating your region, take a look at /bin/offline.sh
.
Optional flags
Use the following optional flags to customize your offline package. To see all available options, use the --help
flag.
Flag | Description |
---|---|
--style | The map style URL. |
--north | The northern-most coordinate in the bounding box. |
--south | The southern-most coordinate in the bounding box. |
--east | The eastern-most coordinate in the bounding box. |
--west | The western most coordinate in the bounding box. |
--minZoom | The minimum zoom level you want your region to have. |
--maxZoom | The maximum zoom level you want your region to have. |
--pixelRatio | Pixel ratio (or pixel density) is a device-dependent value provided by the OS. To see sample values for popular devices, see the Material Design device metrics page. |
--token | The Mapbox access token to use for downloading tiles. |
--output | The directory and file name you want your database to be called. |
--help | See all available flag options. |
Warning: Take the available memory size into consideration when you choose your bounding coordinates and the minZoom
and maxZoom
levels.
Use the macOS graphical interface
Using the macOS graphical interface is a good approach if you create offline packages relatively infrequently, if you prefer a visual tool to generate your packages, or both.
This method of generating offline packages is only applicable to macOS users. For users with other operating systems, including Linux, we recommend using the CLI as described in the Use the command line tool section of this documentation.
Requirements:
To generate offline packages using the macOS graphical interface, you need a typical environment for developing native iOS or macOS applications.
Generate an offline package
- Go to the Mapbox GL Native repository’s release page and download the latest release of the Mapbox Maps SDK for macOS.
- Unzip
Mapbox.GL.app.zip
and openMapbox GL.app
. - Choose the style using the View menu or the menu in the toolbar. Use View ‣ Custom Style to choose a custom style designed in Mapbox Studio.
- Resize the window and adjust the map so that the extents of the desired offline package are fully visible in the window.
- Go to Window ‣ Offline Packs.
- Click the + button to create a new offline package. Enter a name for this offline package, then click Add. Repeat this step for any offline package you want to download.
Merge the offline package
Once you’ve created a new offline package, copy the file to the device so that the Offline Merging API can merge its contents into the main Maps SDK database. The file can be copied locally using adb
or by downloading it from a server — both processes are described below. The Maps SDK loads all tiles and resources from one main database, and provides the Offline Merging API to merge the contents of the new secondary database into the main one.
Prior to v6.6.0 of the Maps SDK for Android, when the Offline Merging API was not available, the workaround for sideloading was to delete the main database file and add your own offline database. While this method is still technically possible, it is no longer recommended because it doesn't allow merging multiple databases and it is prone to crashing.
To merge a secondary database into the main Maps SDK database:
Move the secondary database onto the device. You have two options for doing so:
- Physically copy it over USB using
adb
. For example,adb push my-offline-region.db /path/to/app/files
. - Download the file from your server using a library like
OkHttp
.
- Physically copy it over USB using
In your code, call
OfflineManager.mergeOfflineRegions(String path, OfflineManager.MergeOfflineRegionsCallback callback)
.path
provides the secondary database (writable) path. If the app’s process doesn’t have write-access to the provided path, the file will be copied to the temporary, internal directory for the duration of the merge. (The secondary database may need to be upgraded to the latest schema. This is done in-place and requires write-access to the provided path.)callback
is the completion/error callback. When the merge is completed, or fails, theOfflineManager.MergeOfflineRegionsCallback
will be invoked on the main thread.
For a complete example, take a look at MergeOfflineRegionsActivity
and the sample database. For further details, see the mergeOfflineRegions
documentation.
Database location on Android
On Android, you can move the location of the main Maps SDK database from internal storage to external storage. To do so, make sure the application has the WRITE_EXTERNAL_STORAGE
permission inside the AndroidManifest.xml
and the following flag:
<application>
<meta-data
android:name="com.mapbox.SetStorageExternal"
android:value="true" />
...
</application>