Skip to main content

Offline maps

There is no hosted package service in this fork, and none is needed: a map works offline when its tiles and its style are both on the device. Four ways to get them there, from least to most work:

Cache what was seentiles fetched once are kept, forever if you want
Pre-download an areafill that cache before the user leaves coverage
Ship a tile fileMBTiles or PMTiles, bundled or downloaded
Bundle the stylean asset package, so no style is fetched either

Routing and geocoding have their own offline stories — see routing and geocoding.

Cache what the user already loaded

persistent-cache wraps another source and keeps every tile it fetched in a SQLite file. A hit never reaches the network; expiry headers from the original source are respected. It works for raster and vector tiles alike, and it is what every example does.

{"type": "persistent-cache",
"databasePath": "…/osm-cache.db",
"capacity": 268435456,
"source": {"type": "http", "url": "https://…/{z}/{x}/{y}.pbf", "maxZoom": 14}}

capacity is a byte budget: the oldest tiles are evicted past it. Set cacheOnlyMode to stop the source going to the network at all — that is the "offline mode" switch an app usually wants as a user setting.

Pre-download an area

startDownloadArea fills the same cache ahead of time, for a bounding box and a zoom range:

MapBounds bounds = new MapBounds(
projection.fromWgs84(new MapPos(-77.08, 38.85)),
projection.fromWgs84(new MapPos(-76.94, 38.93)));

cache.startDownloadArea(bounds, 0, 10, new TileDownloadListener() {
@Override public void onDownloadProgress(float progress) {}
@Override public void onDownloadCompleted() {}
});
Check the tile server's policy

Most public tile servers forbid bulk downloading. Use your own server, or a source whose terms allow it. Keep the area small — a few thousand tiles is a reasonable ceiling for a user-initiated download, and the zoom range is what makes that number explode.

MBTiles and PMTiles

A tile file needs no server at all, and both formats hold raster or vector tiles.

{"type": "raster", "source": {"type": "mbtiles", "path": "/sdcard/maps/alps.mbtiles"}}

PMTiles is the newer of the two and usually the better choice: one file, no SQLite, and it can be read directly over HTTP with range requests — so the same file serves an online app from a static host and an offline one from local storage.

Android assets

An MBTiles file cannot be opened from inside the APK — SQLite needs a real file. Copy it to app storage on first run, then open it from there. iOS has no such restriction. PMTiles read through the data/assets chain does not have this problem.

Building the files is someone else's job — tippecanoe, pmtiles convert, or any MBTiles writer.

The style has to be offline too

A vector map with offline tiles and an online style is not offline. Ship the style as an asset package:

SpecWhere the style lives
{"type": "bundle", "path": "styles"}inside the app bundle / APK assets
{"type": "dir", "path": "/sdcard/my-style"}a folder on the device
{"type": "zip", "data": …}a zip, itself from assets, a file or a URL
{"type": "mbvt",
"project": {"type": "project",
"assets": {"type": "bundle", "path": "styles"},
"name": "outdoor"}}

Fonts and sprites come from the same package, so a bundled style needs no network for them either. Converting a MapBox style into one: the style CLI.

Mixing offline and online

ordered and combined sources put one source in front of another — an offline file first, falling back to the network where it has no tile:

{"type": "ordered",
"source": {"type": "mbtiles", "path": "/sdcard/maps/alps.mbtiles"},
"source2": {"type": "http", "url": "https://…/{z}/{x}/{y}.pbf", "maxZoom": 14}}

That is how an app ships a small high-detail area and still shows the rest of the world when online. Every source type and its keys: source reference.