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 seen | tiles fetched once are kept, forever if you want |
| Pre-download an area | fill that cache before the user leaves coverage |
| Ship a tile file | MBTiles or PMTiles, bundled or downloaded |
| Bundle the style | an 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.
- Spec
- Java
- Object API
{"type": "persistent-cache",
"databasePath": "…/osm-cache.db",
"capacity": 268435456,
"source": {"type": "http", "url": "https://…/{z}/{x}/{y}.pbf", "maxZoom": 14}}
map.addLayer("basemap", Spec.of("vector")
.set("source", Spec.of("persistent-cache")
.set("databasePath", new File(getExternalFilesDir(null), "osm-cache.db").getPath())
.set("capacity", 256 * 1024 * 1024)
.set("source", Spec.of("http")
.set("url", "https://…/{z}/{x}/{y}.pbf")
.set("maxZoom", 14)))
.set("style", "outdoor"));
TileDataSource http = new HTTPTileDataSource(1, 19, url);
PersistentCacheTileDataSource cache =
new PersistentCacheTileDataSource(http, getExternalFilesDir(null) + "/mapcache.db");
mapView.getLayers().add(new RasterTileLayer(cache));
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() { … }
});
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.
- Spec
- Java
{"type": "raster", "source": {"type": "mbtiles", "path": "/sdcard/maps/alps.mbtiles"}}
// Raster tiles: nothing else needed.
map.addLayer("offline", Spec.of("raster")
.set("source", Spec.of("mbtiles").set("path", path)));
// Vector tiles: the same source, plus the style that draws them.
map.addLayer("offline", Spec.of("vector")
.set("source", Spec.of("mbtiles").set("path", path))
.set("style", Spec.of("mbvt").set("project", Spec.of("project")
.set("assets", Spec.of("zip").set("data", Spec.of("url").set("url", "assets://style.zip")))
.set("name", "outdoor"))));
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.
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:
| Spec | Where 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.