It enables a MapboxMap
(from Mapbox Android SDK) to use MBTiles as Source
.
Inspired from similar Swift approach in iOS SDK, made by @namannik.
In you APP, when a MBTilesSource
instance activates, it starts a localhost MBTilesServer
and works like a common Mapbox Raster/Vector Source.
MBTilesServer
is a Singleton. It can hosts multiple MBTilesSource
. The URL for each MBTilesSource
is http://localhost:8888/[Source ID]/{z}/{x}/{y}.[jpg|png|pbf|mvt]
Since MBTilesSource
only works as common Mapbox Raster/Vector Source, you always need to add layer or set style for MapboxMap by yourself. Mapbox Style is a bit more complex. If your MBTiles contains vector tile (.mvt), make sure your specify correct value for source-layer
. See spec here.
Also, you may check an example by MapTiler, an OSM vector tiles provider.
- Add
MBTilesSource.kt
andMBTilesServer.kt
(from this gist) into your Android project.
To make sure MBTilesServer
works without network connection above Android 9 (API level 28), you have to allow HTTP traffic to localhost.
For more information, see Android Developers.
- Add network configuration into
AndroidManifest.xml
android:networkSecurityConfig="@xml/network_security_config" >
- A resource file named in
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="false">localhost</domain> </domain-config> </network-security-config>
// Connect to localhost even when device is not connected to internet
Mapbox.setConnected(true)
// Create MBTilesSource
val path = "/path/to/source.mbtiles" // file path
val sourceId = "source1" // used as Mapbox Source ID
val mbSource = try {
MBTilesSource(path, sourceId).apply { activate() }
} catch (e: MBTilesSourceException.CouldNotReadFileException){
// Deal with error if fail to read MBTiles
}
Now, a XYZ Tile Service is working on http://localhost:8888/source1/{z}/{x}/{y}.[mvt|pbf|jpg|png]
.
The file extension of each tile is judged by the format in MBTiles. (Defined in table metadata
, only mvt, pbf, jpg, png are allowed)
Also, if your MBTiles is at asset folder, you may use companion object in MBTilesSource
to copy it into internal storage, and get the path directly.
val path = MBTilesSource.readAsset(context, "FILENAME.mbtiles")
mbSource.deactivate()
// In callback of Style.OnStyleLoaded
style.addSource(mbSource.instance)
// If mbSource contains raster tiles
val rasterLayer = RasterLayer("raster_layer_id", mbSource.id)
style.addLayer(rasterLayer)
// If mbSource contains vector tiles
val vectorLayer = LineLayer("vector_layer_id", mbSource.id)
style.addLayer(vectorLayer)
I created this for my app, Five More Minutes. This demo use vector MBTiles and style from OpenMapTiles.
-
If you want to create an APP with map which really reads MBTiles directly, consider use Tangram SDK instead of Mapbox SDK. It natively support use a MBTiles as source offline, see Documentation here.
Do not challenge why Mapbox doesn't support a format they created :D
-
Or, use Maplibre-gl-native, a mapbox-gl-native fork which embraces open source!
-
If it won't bother you to store tiles with folders, you can use
asset://
andfile://
protocol with source. See related thread in StackOverflow here.
- 2021-01-26
- Add Alternatives section
- 2020-11-25
- Add network configuration for newer Android version, thanks @Cacaonut !
- Remove Anko from dependencies
- Code refactor
- Exmaple refactor for latest Mapbox API
- 2020-05-26
- Fix connection issue about localhost, thanks @grzesiek2010 !
@mortezaomidi
Thank you for sharing this. For Android external storage, it seems like file protocol (
file://path/to/tile
) is needed. Since Mapbox doesn't mention anything in their docs, I am also very curious about this approach.I am a little busy on this weekend, so I will start to test some cases after next Monday. Will get back to you if anything is confirmed.