Skip to content

Instantly share code, notes, and snippets.

@typebrook
Last active March 18, 2025 05:50

Revisions

  1. Hsieh Chin Fan revised this gist Aug 20, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -85,7 +85,7 @@ style.addLayer(rasterLayer)

    // If mbSource contains vector tiles
    val vectorLayer = LineLayer("vector_layer_id", mbSource.id)
    style.addLayer(vector)
    style.addLayer(vectorLayer)
    ```

    ## How it looks like?
  2. Hsieh Chin Fan revised this gist Jul 19, 2021. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -98,6 +98,9 @@ This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from
    - If you want to create an APP with map which really reads MBTiles directly, consider use [Tangram](https://github.com/tangrams/tangram-es) SDK instead of Mapbox SDK. It natively support use a MBTiles as source offline, see Documentation [here](https://tangrams.readthedocs.io/en/master/Syntax-Reference/sources/#mbtiles).

    **Do not challenge why Mapbox doesn't support a format they created :D**

    - Or, use [Maplibre-gl-native](https://github.com/maplibre/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://` and `file://` protocol with source. See related thread in StackOverflow [here](https://stackoverflow.com/questions/39045059/how-can-i-use-asset-for-non-apk-files-on-android-in-a-mapbox-gl-style/40518151#40518151).

    ## CHANGELOG
  3. Hsieh Chin Fan revised this gist Jan 26, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -95,7 +95,7 @@ This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from

    ## Alternatives

    - If you want to create an APP with map which really use MBTiles, consider use [Tangram](https://github.com/tangrams/tangram-es) SDK instead of Mapbox SDK. It natively support use a MBTiles as source offline, see Documentation [here](https://tangrams.readthedocs.io/en/master/Syntax-Reference/sources/#mbtiles).
    - If you want to create an APP with map which really reads MBTiles directly, consider use [Tangram](https://github.com/tangrams/tangram-es) SDK instead of Mapbox SDK. It natively support use a MBTiles as source offline, see Documentation [here](https://tangrams.readthedocs.io/en/master/Syntax-Reference/sources/#mbtiles).

    **Do not challenge why Mapbox doesn't support a format they created :D**
    - If it won't bother you to store tiles with folders, you can use `asset://` and `file://` protocol with source. See related thread in StackOverflow [here](https://stackoverflow.com/questions/39045059/how-can-i-use-asset-for-non-apk-files-on-android-in-a-mapbox-gl-style/40518151#40518151).
  4. Hsieh Chin Fan revised this gist Jan 26, 2021. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -104,10 +104,10 @@ This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from

    - **2021-01-26**
    - Add Alternatives section
    - **2020-05-26**
    - Fix connection issue about localhost, thanks [@grzesiek2010](https://gist.github.com/typebrook/7d25be326f0e9afd58e0bbc333d2a175#gistcomment-3273056) !
    - **2020-11-25**
    - Add network configuration for newer Android version, thanks [@Cacaonut](https://gist.github.com/typebrook/7d25be326f0e9afd58e0bbc333d2a175#gistcomment-3332003) !
    - Remove Anko from dependencies
    - Code refactor
    - Exmaple refactor for latest Mapbox API
    - **2020-05-26**
    - Fix connection issue about localhost, thanks [@grzesiek2010](https://gist.github.com/typebrook/7d25be326f0e9afd58e0bbc333d2a175#gistcomment-3273056) !
  5. Hsieh Chin Fan revised this gist Jan 26, 2021. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -95,7 +95,9 @@ This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from

    ## Alternatives

    - If you want to create an APP with map, which really use MBTiles, consider use [Tangram](https://github.com/tangrams/tangram-es) SDK instead of Mapbox. It natively support use a MBTiles as source, see Documentation [here](https://tangrams.readthedocs.io/en/master/Syntax-Reference/sources/#mbtiles). **Do not challenge why Mapbox doesn't support a format they created :D**
    - If you want to create an APP with map which really use MBTiles, consider use [Tangram](https://github.com/tangrams/tangram-es) SDK instead of Mapbox SDK. It natively support use a MBTiles as source offline, see Documentation [here](https://tangrams.readthedocs.io/en/master/Syntax-Reference/sources/#mbtiles).

    **Do not challenge why Mapbox doesn't support a format they created :D**
    - If it won't bother you to store tiles with folders, you can use `asset://` and `file://` protocol with source. See related thread in StackOverflow [here](https://stackoverflow.com/questions/39045059/how-can-i-use-asset-for-non-apk-files-on-android-in-a-mapbox-gl-style/40518151#40518151).

    ## CHANGELOG
  6. Hsieh Chin Fan revised this gist Jan 26, 2021. 1 changed file with 8 additions and 0 deletions.
    8 changes: 8 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -93,7 +93,15 @@ style.addLayer(vector)
    I created this for my app, [Five More Minutes](https://github.com/typebrook/FiveMoreMinutes).
    This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from OpenMapTiles.

    ## Alternatives

    - If you want to create an APP with map, which really use MBTiles, consider use [Tangram](https://github.com/tangrams/tangram-es) SDK instead of Mapbox. It natively support use a MBTiles as source, see Documentation [here](https://tangrams.readthedocs.io/en/master/Syntax-Reference/sources/#mbtiles). **Do not challenge why Mapbox doesn't support a format they created :D**
    - If it won't bother you to store tiles with folders, you can use `asset://` and `file://` protocol with source. See related thread in StackOverflow [here](https://stackoverflow.com/questions/39045059/how-can-i-use-asset-for-non-apk-files-on-android-in-a-mapbox-gl-style/40518151#40518151).

    ## CHANGELOG

    - **2021-01-26**
    - Add Alternatives section
    - **2020-05-26**
    - Fix connection issue about localhost, thanks [@grzesiek2010](https://gist.github.com/typebrook/7d25be326f0e9afd58e0bbc333d2a175#gistcomment-3273056) !
    - **2020-11-25**
  7. Hsieh Chin Fan revised this gist Jan 26, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -91,7 +91,7 @@ style.addLayer(vector)
    ## How it looks like?

    I created this for my app, [Five More Minutes](https://github.com/typebrook/FiveMoreMinutes).
    This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from OpenMaptiles.
    This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from OpenMapTiles.

    ## CHANGELOG
    - **2020-05-26**
  8. Hsieh Chin Fan revised this gist Jan 26, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -61,7 +61,7 @@ Now, a XYZ Tile Service is working on `http://localhost:8888/source1/{z}/{x}/{y}

    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 compnion object in `MBTilesSource` to copy it into internal storage, and get the path directly.
    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.

    ```kotlin
    val path = MBTilesSource.readAsset(context, "FILENAME.mbtiles")
  9. Hsieh Chin Fan revised this gist Jan 26, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@ Inspired from similar [Swift approach](https://gist.github.com/namannik/3b7c8b69

    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](https://kotlinlang.org/docs/reference/object-declarations.html). It can hosts more than 1 `MBTilesSource`. The URL for each `MBTilesSource` is `http://localhost:8888/[Source ID]/{z}/{x}/{y}.[jpg|png|pbf|mvt]`
    `MBTilesServer` is a [Singleton](https://kotlinlang.org/docs/reference/object-declarations.html). 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](https://www.mapbox.com/mapbox-gl-js/style-spec) here.

  10. Hsieh Chin Fan revised this gist Nov 27, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -61,7 +61,7 @@ Now, a XYZ Tile Service is working on `http://localhost:8888/source1/{z}/{x}/{y}

    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 compnion object in MBTilesSOurce to copy it into internal storage, and get the path directly.
    Also, if your MBTiles is at asset folder, you may use compnion object in `MBTilesSource` to copy it into internal storage, and get the path directly.

    ```kotlin
    val path = MBTilesSource.readAsset(context, "FILENAME.mbtiles")
  11. Hsieh Chin Fan revised this gist Nov 27, 2020. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -2,15 +2,15 @@

    ## What it does?

    It enables a `MapboxMap` (from [Mapbox Android SDK](https://www.mapbox.com/android-docs/)) to display [MBTiles](http://wiki.openstreetmap.org/wiki/MBTiles).
    It enables a `MapboxMap` (from [Mapbox Android SDK](https://www.mapbox.com/android-docs/)) to use [MBTiles](http://wiki.openstreetmap.org/wiki/MBTiles) as [`Source`](https://docs.mapbox.com/archive/android/maps/api/9.4.0/com/mapbox/mapboxsdk/style/sources/Source.html).

    Inspired from similar [Swift approach](https://gist.github.com/namannik/3b7c8b69c2d0768d0c2b48d2ed5ff71c) in iOS SDK, made by @namannik.

    ## How it works?

    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](https://kotlinlang.org/docs/reference/object-declarations.html). It can hosts more than 1 `MBTilesSource`. The URL for each `MBTilesSource` is `http://localhost:8888/[Souece ID]/{z}/{x}/{y}.[jpg|png|pbf|mvt]`
    `MBTilesServer` is a [Singleton](https://kotlinlang.org/docs/reference/object-declarations.html). It can hosts more than 1 `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](https://www.mapbox.com/mapbox-gl-js/style-spec) here.

  12. Hsieh Chin Fan revised this gist Nov 26, 2020. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions MBTilesSource.kt
    Original file line number Diff line number Diff line change
    @@ -15,8 +15,8 @@ import kotlin.properties.Delegates
    */

    sealed class MBTilesSourceException : Exception() {
    class CouldNotReadFileError : MBTilesSourceException()
    class UnsupportedFormatError : MBTilesSourceException()
    class CouldNotReadFileException : MBTilesSourceException()
    class UnsupportedFormatException : MBTilesSourceException()
    }

    class MBTilesSource(filePath: String, sourceId: String? = null) {
    @@ -26,7 +26,7 @@ class MBTilesSource(filePath: String, sourceId: String? = null) {
    private val db: SQLiteDatabase = try {
    SQLiteDatabase.openOrCreateDatabase(filePath, null)
    } catch (e: RuntimeException) {
    throw MBTilesSourceException.CouldNotReadFileError()
    throw MBTilesSourceException.CouldNotReadFileException()
    }
    val instance: Source by lazy {
    if (isVector) VectorSource(id, TileSet(null, url))
    @@ -56,7 +56,7 @@ class MBTilesSource(filePath: String, sourceId: String? = null) {
    isVector = when (format) {
    in validVectorFormats -> true
    in validRasterFormats -> false
    else -> throw MBTilesSourceException.UnsupportedFormatError()
    else -> throw MBTilesSourceException.UnsupportedFormatException()
    }

    } catch (error: MBTilesSourceException) {
  13. Hsieh Chin Fan revised this gist Nov 25, 2020. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -94,7 +94,8 @@ I created this for my app, [Five More Minutes](https://github.com/typebrook/Five
    This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from OpenMaptiles.

    ## CHANGELOG
    - **2020-05-26** Fix connection issue about localhost, thanks [@grzesiek2010](https://gist.github.com/typebrook/7d25be326f0e9afd58e0bbc333d2a175#gistcomment-3273056) !
    - **2020-05-26**
    - Fix connection issue about localhost, thanks [@grzesiek2010](https://gist.github.com/typebrook/7d25be326f0e9afd58e0bbc333d2a175#gistcomment-3273056) !
    - **2020-11-25**
    - Add network configuration for newer Android version, thanks [@Cacaonut](https://gist.github.com/typebrook/7d25be326f0e9afd58e0bbc333d2a175#gistcomment-3332003) !
    - Remove Anko from dependencies
  14. Hsieh Chin Fan revised this gist Nov 25, 2020. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -99,3 +99,4 @@ This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from
    - Add network configuration for newer Android version, thanks [@Cacaonut](https://gist.github.com/typebrook/7d25be326f0e9afd58e0bbc333d2a175#gistcomment-3332003) !
    - Remove Anko from dependencies
    - Code refactor
    - Exmaple refactor for latest Mapbox API
  15. Hsieh Chin Fan revised this gist Nov 25, 2020. 1 changed file with 6 additions and 3 deletions.
    9 changes: 6 additions & 3 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@

    It enables a `MapboxMap` (from [Mapbox Android SDK](https://www.mapbox.com/android-docs/)) to display [MBTiles](http://wiki.openstreetmap.org/wiki/MBTiles).

    Inspired from similar [Swift approach](https://gist.github.com/namannik/3b7c8b69c2d0768d0c2b48d2ed5ff71c) in iOS SDK, made by @namannik
    Inspired from similar [Swift approach](https://gist.github.com/namannik/3b7c8b69c2d0768d0c2b48d2ed5ff71c) in iOS SDK, made by @namannik.

    ## How it works?

    @@ -25,6 +25,8 @@ Also, you may check an [example](https://github.com/openmaptiles/osm-liberty-gl-
    ### Prepare network configuration
    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](https://developer.android.com/training/articles/security-config#CleartextTrafficPermitted).

    1. Add network configuration into `AndroidManifest.xml`
    ```xml
    android:networkSecurityConfig="@xml/network_security_config" >
    @@ -92,7 +94,8 @@ I created this for my app, [Five More Minutes](https://github.com/typebrook/Five
    This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from OpenMaptiles.

    ## CHANGELOG
    - **2020-05-26** Fix connection issue about localhost, thanks @grzesiek2010 !
    - **2020-05-26** Fix connection issue about localhost, thanks [@grzesiek2010](https://gist.github.com/typebrook/7d25be326f0e9afd58e0bbc333d2a175#gistcomment-3273056) !
    - **2020-11-25**
    - Add network configuration for newer Android version, thanks @Cacaonut
    - Add network configuration for newer Android version, thanks [@Cacaonut](https://gist.github.com/typebrook/7d25be326f0e9afd58e0bbc333d2a175#gistcomment-3332003) !
    - Remove Anko from dependencies
    - Code refactor
  16. Hsieh Chin Fan revised this gist Nov 25, 2020. 3 changed files with 146 additions and 100 deletions.
    64 changes: 29 additions & 35 deletions MBTilesServer.kt
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    package com.example.sample.offline

    import android.util.Log
    import java.io.BufferedReader
    import java.io.ByteArrayOutputStream
    @@ -7,16 +9,16 @@ import java.net.ServerSocket
    import java.net.Socket
    import kotlin.math.pow

    /**
    * Created by pham on 2018/1/7.
    /*
    * Localhost tile server with MBTilesSource
    */

    object MBTilesServer : Runnable {

    private var serverSocket: ServerSocket? = null
    const val port = 8888
    private val serverSocket: ServerSocket = ServerSocket(port)
    var isRunning = false
    val sources: MutableMap<String, MBTilesSource> = mutableMapOf()
    const val port = 8888

    fun start() {
    isRunning = true
    @@ -25,35 +27,37 @@ object MBTilesServer : Runnable {

    fun stop() {
    isRunning = false
    serverSocket?.close()
    serverSocket = null
    serverSocket.close()
    }

    override fun run() {
    try {
    serverSocket = ServerSocket(port)
    while (isRunning) {
    val socket = serverSocket?.accept() ?: throw Error()
    Log.d(javaClass.simpleName, "request handled start")
    handle(socket)
    socket.close()
    Log.d(javaClass.simpleName, "request handled in while")
    serverSocket.accept().use { socket ->
    Log.d(javaClass.simpleName, "Handling request")
    handle(socket)
    Log.d(javaClass.simpleName, "Request handled")
    }
    }
    } catch (e: Exception) {
    Log.d(javaClass.simpleName, e.localizedMessage)
    Log.d(
    javaClass.simpleName,
    e.localizedMessage ?: "Exception while running MBTilesServer"
    )
    } finally {
    Log.d(javaClass.simpleName, "request handled")
    }
    }

    @Throws
    private fun handle(socket: Socket) {
    var reader: BufferedReader? = null
    var output: PrintStream? = null

    val reader: BufferedReader = socket.getInputStream().reader().buffered()
    // Output stream that we send the response to
    val output = PrintStream(socket.getOutputStream())

    try {
    var route: String? = null
    reader = socket.getInputStream().reader().buffered()

    // Read HTTP headers and parse out the route.
    do {
    @@ -63,14 +67,11 @@ object MBTilesServer : Runnable {
    route = line.substringAfter("GET /").substringBefore(".")
    break
    }
    } while (!line.isEmpty())
    } while (line.isNotEmpty())

    // the source which this request target to
    val source = sources[route?.substringBefore("/")] ?: return

    // Output stream that we send the response to
    output = PrintStream(socket.getOutputStream())

    // Prepare the content to send.
    if (null == route) {
    writeServerError(output)
    @@ -83,7 +84,7 @@ object MBTilesServer : Runnable {
    }

    // Send out the content.
    output.apply {
    with(output) {
    println("HTTP/1.0 200 OK")
    println("Content-Type: " + detectMimeType(source.format))
    println("Content-Length: " + bytes.size)
    @@ -93,25 +94,18 @@ object MBTilesServer : Runnable {
    flush()
    }
    } finally {
    if (null != output) output.close()
    reader?.close()
    reader.close()
    output.close()
    }
    }

    @Throws
    private fun loadContent(source: MBTilesSource, route: String): ByteArray? {
    private fun loadContent(source: MBTilesSource, route: String): ByteArray? = try {
    val (z, x, y) = route.split("/").subList(1, 4).map { it.toInt() }

    try {
    val output = ByteArrayOutputStream()
    val content = source.getTile(z, x, (2.0.pow(z)).toInt() - 1 - y) ?: return null
    output.write(content)
    output.flush()
    return output.toByteArray()
    } catch (e: FileNotFoundException) {
    e.printStackTrace()
    return null
    }
    source.getTile(z, x, (2.0.pow(z)).toInt() - 1 - y)
    } catch (e: FileNotFoundException) {
    e.printStackTrace()
    null
    }

    private fun writeServerError(output: PrintStream) {
    104 changes: 61 additions & 43 deletions MBTilesSource.kt
    Original file line number Diff line number Diff line change
    @@ -1,37 +1,40 @@
    package com.example.sample.offline

    import android.content.Context
    import android.database.sqlite.SQLiteDatabase
    import org.jetbrains.anko.db.MapRowParser
    import org.jetbrains.anko.db.select
    import com.mapbox.mapboxsdk.style.sources.RasterSource
    import com.mapbox.mapboxsdk.style.sources.Source
    import com.mapbox.mapboxsdk.style.sources.TileSet
    import com.mapbox.mapboxsdk.style.sources.VectorSource
    import java.io.File
    import java.io.FileOutputStream
    import kotlin.properties.Delegates

    /**
    * Created by pham on 2018/1/7.
    /*
    * Mapbox Source backend by localhost tile server
    */

    sealed class MBTilesSourceError : Error() {
    class CouldNotReadFileError : MBTilesSourceError()
    class UnsupportedFormatError : MBTilesSourceError()
    }

    object MetadataParser : MapRowParser<Pair<String, String>> {
    override fun parseRow(columns: Map<String, Any?>): Pair<String, String> =
    columns["name"] as String to columns["value"] as String
    sealed class MBTilesSourceException : Exception() {
    class CouldNotReadFileError : MBTilesSourceException()
    class UnsupportedFormatError : MBTilesSourceException()
    }

    object TilesParser : MapRowParser<ByteArray> {
    override fun parseRow(columns: Map<String, Any?>): ByteArray = columns["tile_data"] as ByteArray
    }
    class MBTilesSource(filePath: String, sourceId: String? = null) {

    class MBTilesSource(filePath: String, id: String? = null) {

    var id = id ?: filePath.substringAfterLast("/").substringBefore(".")
    val id = sourceId ?: filePath.substringAfterLast("/").substringBefore(".")
    val url get() = "http://localhost:${MBTilesServer.port}/$id/{z}/{x}/{y}.$format"
    private val db: SQLiteDatabase = try {
    SQLiteDatabase.openOrCreateDatabase(filePath, null)
    } catch (e: RuntimeException) {
    throw MBTilesSourceError.CouldNotReadFileError()
    throw MBTilesSourceException.CouldNotReadFileError()
    }
    val instance: Source by lazy {
    if (isVector) VectorSource(id, TileSet(null, url))
    else RasterSource(id, TileSet(null, url))
    }

    var isVector = false
    var format = String()
    var isVector by Delegates.notNull<Boolean>()
    lateinit var format: String
    // var tileSize: Int? = null
    // var layersJson: String? = ""
    // var attributions: String? = ""
    @@ -41,47 +44,62 @@ class MBTilesSource(filePath: String, id: String? = null) {

    init {
    try {
    format = db.select("metadata")
    .whereSimple("name = ?", "format")
    .parseSingle(MetadataParser).second
    format = db.query(
    "metadata", null, "name = ?",
    arrayOf("format"), null, null, null
    ).use { cursor ->
    cursor.moveToFirst()
    val index = cursor.getColumnIndex("value")
    cursor.getString(index)
    }

    isVector = when (format) {
    in validVectorFormats -> true
    in validRasterFormats -> false
    else -> throw MBTilesSourceError.UnsupportedFormatError()
    else -> throw MBTilesSourceException.UnsupportedFormatError()
    }

    } catch (error: MBTilesSourceError) {
    } catch (error: MBTilesSourceException) {
    print(error.localizedMessage)
    }
    }

    fun getTile(z: Int, x: Int, y: Int): ByteArray? {
    return db.select("tiles")
    .whereArgs("(zoom_level = {z}) and (tile_column = {x}) and (tile_row = {y})",
    "z" to z, "x" to x, "y" to y)
    .parseList(TilesParser)
    .run { if (!isEmpty()) get(0) else null }
    }
    return db.query(
    "tiles", null, "zoom_level = ? AND tile_column = ? AND tile_row = ?",
    arrayOf("$z", "$x", "$y"), null, null, null
    ).use { cursor ->
    if (cursor.count == 0) return null

    fun activate() {
    val source = this
    MBTilesServer.apply {
    sources[source.id] = source
    if (!isRunning) start()
    cursor.moveToFirst()
    val index = cursor.getColumnIndex("tile_data")
    cursor.getBlob(index)
    }
    }

    fun deactivate() {
    val source = this
    MBTilesServer.apply {
    sources.remove(source.id)
    if (isRunning && sources.isEmpty()) stop()
    }
    fun activate() = with(MBTilesServer) {
    sources[id] = this@MBTilesSource
    if (!isRunning) start()
    }

    fun deactivate() = with(MBTilesServer) {
    sources.remove(id)
    if (isRunning && sources.isEmpty()) stop()
    }

    companion object {
    val validRasterFormats = listOf("jpg", "png")
    val validVectorFormats = listOf("pbf", "mvt")

    fun readAsset(context: Context, asset: String): String =
    context.assets.open(asset).use { inputStream ->
    val path = context.getDatabasePath(asset).path
    val outputFile = File(path)
    FileOutputStream(outputFile).use { outputStream ->
    inputStream.copyTo(outputStream)
    outputStream.flush()
    }
    return path
    }
    }
    }
    78 changes: 56 additions & 22 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,58 +1,89 @@
    # MBTilesSource.kt + MbtilesServer.kt

    ## What it does
    ## What it does?

    It enables a [`MapView`](https://www.mapbox.com/android-docs/) (from the [Mapbox Android SDK](https://www.mapbox.com/android-docs/)) to display [MBTiles](http://wiki.openstreetmap.org/wiki/MBTiles).
    It enables a `MapboxMap` (from [Mapbox Android SDK](https://www.mapbox.com/android-docs/)) to display [MBTiles](http://wiki.openstreetmap.org/wiki/MBTiles).

    Inspired from similar approach by swift in iOS SDK, made by [namannik](https://gist.github.com/namannik/3b7c8b69c2d0768d0c2b48d2ed5ff71c)
    Inspired from similar [Swift approach](https://gist.github.com/namannik/3b7c8b69c2d0768d0c2b48d2ed5ff71c) in iOS SDK, made by @namannik

    ## How it works
    ## How it works?

    When a `MBTilesSource` instance activates, it starts a localhost `MBTilesServer` within your app and works same as common Mapbox Raster/Vector Source.
    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](https://kotlinlang.org/docs/reference/object-declarations.html), could contains more than 1 `MBTilesSource`.
    The URL for MBTilesSource is `http://localhost:8888/[mbSource.id]/{z}/{x}/{y}.[jpg|png|pbf|mvt]`
    `MBTilesServer` is a [Singleton](https://kotlinlang.org/docs/reference/object-declarations.html). It can hosts more than 1 `MBTilesSource`. The URL for each `MBTilesSource` is `http://localhost:8888/[Souece 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](https://www.mapbox.com/android-docs/api/map-sdk/5.4.1/index.html) **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](https://www.mapbox.com/mapbox-gl-js/style-spec) here, also [example](https://github.com/openmaptiles/osm-liberty-gl-style/blob/gh-pages/style.json) by OpenMapTiles, an osm vector MBTiles [provider](https://openmaptiles.com/downloads/dataset/osm/asia/taiwan/taipei/).
    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](https://www.mapbox.com/mapbox-gl-js/style-spec) here.

    Also, you may check an [example](https://github.com/openmaptiles/osm-liberty-gl-style/blob/gh-pages/style.json) by [MapTiler](https://www.maptiler.com/), an OSM vector tiles provider.

    ## Installation

    1. Add `MBTilesSource.kt` and `MBTilesServer.kt` (from this Gist) to your app.
    1. Add the [Anko-SQLite library](https://github.com/Kotlin/anko/wiki/Anko-SQLite) to your app.
    - Add `MBTilesSource.kt` and `MBTilesServer.kt` (from this gist) into your Android project.

    ## Usage

    ### Create a MBTilesSource to read existing Mbtiles file
    ### Prepare network configuration
    To make sure `MBTilesServer` works without network connection above Android 9 (API level 28), you have to allow HTTP traffic to localhost.

    1. Add network configuration into `AndroidManifest.xml`
    ```xml
    android:networkSecurityConfig="@xml/network_security_config" >
    ```
    1. A resource file named in `AndroidManifest.xml`
    ```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>
    ```

    ### Create a `MBTilesSource` to read your MBTiles file

    ```kotlin
    // 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 = "ID" // Nullable, used in Mapbox Source URL
    val sourceId = "source1" // used as Mapbox Source ID
    val mbSource = try {
    MBTilesSource(path, sourceId).apply { activate() }
    } catch (e: MBTilesSourceError.CouldNotReadFileError){
    // Deal with error here
    } 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 compnion object in MBTilesSOurce to copy it into internal storage, and get the path directly.

    ```kotlin
    val path = MBTilesSource.readAsset(context, "FILENAME.mbtiles")
    ```

    ### Remove a source from `MBTilesServer`

    ```kotlin
    mbSource.deactivate()
    ```

    ### Work with MapView
    ### Work with Mapbox Style

    ```kotlin
    // In callback of Style.OnStyleLoaded
    style.addSource(mbSource.instance)

    // If mbSource contains raster tiles
    mapboxMap.addSource(RasterSource(mbSource.id, TileSet(null, mbSource.url), 126))
    val rasterLayer = RasterLayer("raster_layer_id", mbSource.id)
    mapboxMap.addLayer(rasterLayer)
    ```
    ```kotlin
    // if mbSource contains vector tiles
    mapboxMap.setStyleUrl("path/to/styleFile.json")
    style.addLayer(rasterLayer)

    // If mbSource contains vector tiles
    val vectorLayer = LineLayer("vector_layer_id", mbSource.id)
    style.addLayer(vector)
    ```

    ## How it looks like?
    @@ -61,4 +92,7 @@ I created this for my app, [Five More Minutes](https://github.com/typebrook/Five
    This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from OpenMaptiles.

    ## CHANGELOG
    **2020-05-26** Fix connection issue about localhost, thanks @grzesiek2010 !
    - **2020-05-26** Fix connection issue about localhost, thanks @grzesiek2010 !
    - **2020-11-25**
    - Add network configuration for newer Android version, thanks @Cacaonut
    - Code refactor
  17. typebrook revised this gist May 26, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -61,4 +61,4 @@ I created this for my app, [Five More Minutes](https://github.com/typebrook/Five
    This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from OpenMaptiles.

    ## CHANGELOG
    Fix connection issue about localhost, thanks @grzesiek2010 !
    **2020-05-26** Fix connection issue about localhost, thanks @grzesiek2010 !
  18. typebrook renamed this gist May 26, 2020. 1 changed file with 19 additions and 18 deletions.
    37 changes: 19 additions & 18 deletions readme.md → README.md
    Original file line number Diff line number Diff line change
    @@ -1,12 +1,12 @@
    # MBTilesSource.kt + MbtilesServer.kt

    ### What it does
    ## What it does

    It enables a [`MapView`](https://www.mapbox.com/android-docs/) (from the [Mapbox Android SDK](https://www.mapbox.com/android-docs/)) to display [MBTiles](http://wiki.openstreetmap.org/wiki/MBTiles).

    Inspired from similar approach by swift in iOS SDK, made by [namannik](https://gist.github.com/namannik/3b7c8b69c2d0768d0c2b48d2ed5ff71c)

    ### How it works
    ## How it works

    When a `MBTilesSource` instance activates, it starts a localhost `MBTilesServer` within your app and works same as common Mapbox Raster/Vector Source.

    @@ -15,16 +15,18 @@ The URL for MBTilesSource is `http://localhost:8888/[mbSource.id]/{z}/{x}/{y}.[j

    Since `MBTilesSource` only works as common Mapbox Raster/Vector Source, you always need to add layer or set style for [MapboxMap](https://www.mapbox.com/android-docs/api/map-sdk/5.4.1/index.html) **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](https://www.mapbox.com/mapbox-gl-js/style-spec) here, also [example](https://github.com/openmaptiles/osm-liberty-gl-style/blob/gh-pages/style.json) by OpenMapTiles, an osm vector MBTiles [provider](https://openmaptiles.com/downloads/dataset/osm/asia/taiwan/taipei/).

    ### Installation
    ## Installation

    1. Add `MBTilesSource.kt` and `MBTilesServer.kt` (from this Gist) to your app.
    1. Add the [Anko-SQLite library](https://github.com/Kotlin/anko/wiki/Anko-SQLite) to your app.

    ### Usage
    ## Usage

    ##### Create a MBTilesSource to read existing Mbtiles file
    ### Create a MBTilesSource to read existing Mbtiles file

    ```
    ```kotlin
    // Connect to localhost even when device is not connected to internet
    Mapbox.setConnected(true)
    val path = "/path/to/source.mbtiles" // file path
    val sourceId = "ID" // Nullable, used in Mapbox Source URL
    val mbSource = try {
    @@ -34,30 +36,29 @@ val mbSource = try {
    }
    ```

    ##### Remove a source from `MBTilesServer`
    ### Remove a source from `MBTilesServer`

    ```
    ```kotlin
    mbSource.deactivate()
    ```

    ##### Work with MapView
    ### Work with MapView

    ```
    // if mbSource contains raster tiles
    ```kotlin
    // If mbSource contains raster tiles
    mapboxMap.addSource(RasterSource(mbSource.id, TileSet(null, mbSource.url), 126))
    val rasterLayer = RasterLayer("raster_layer_id", mbSource.id)
    mapboxMap.addLayer(rasterLayer)
    ```
    ```
    ```kotlin
    // if mbSource contains vector tiles
    mapboxMap.setStyleUrl("path/to/styleFile.json")
    ```

    ### Known Limitations

    • Although this approach use localhost, you still need to enable network when requesting tile.

    ### How it looks like?
    ## How it looks like?

    I created this for my app, [Five More Minutes](https://github.com/typebrook/FiveMoreMinutes).
    This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from OpenMaptiles.
    This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from OpenMaptiles.

    ## CHANGELOG
    Fix connection issue about localhost, thanks @grzesiek2010 !
  19. Hsieh Chin Fan (Pham) revised this gist Feb 13, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion readme.md
    Original file line number Diff line number Diff line change
    @@ -59,5 +59,5 @@ mapboxMap.setStyleUrl("path/to/styleFile.json")

    ### How it looks like?

    I created this for my app, [Five More Minutes]https://github.com/typebrook/FiveMoreMinutes).
    I created this for my app, [Five More Minutes](https://github.com/typebrook/FiveMoreMinutes).
    This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from OpenMaptiles.
  20. Hsieh Chin Fan (Pham) revised this gist Feb 13, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion readme.md
    Original file line number Diff line number Diff line change
    @@ -13,7 +13,7 @@ When a `MBTilesSource` instance activates, it starts a localhost `MBTilesServer`
    `MBTilesServer` is a [Singleton](https://kotlinlang.org/docs/reference/object-declarations.html), could contains more than 1 `MBTilesSource`.
    The URL for MBTilesSource is `http://localhost:8888/[mbSource.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](https://www.mapbox.com/android-docs/api/map-sdk/5.4.1/index.html) 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](https://www.mapbox.com/mapbox-gl-js/style-spec) here, also [example](https://github.com/openmaptiles/osm-liberty-gl-style/blob/gh-pages/style.json) by OpenMapTiles, an osm vector MBTiles [provider](https://openmaptiles.com/downloads/dataset/osm/asia/taiwan/taipei/).
    Since `MBTilesSource` only works as common Mapbox Raster/Vector Source, you always need to add layer or set style for [MapboxMap](https://www.mapbox.com/android-docs/api/map-sdk/5.4.1/index.html) **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](https://www.mapbox.com/mapbox-gl-js/style-spec) here, also [example](https://github.com/openmaptiles/osm-liberty-gl-style/blob/gh-pages/style.json) by OpenMapTiles, an osm vector MBTiles [provider](https://openmaptiles.com/downloads/dataset/osm/asia/taiwan/taipei/).

    ### Installation

  21. Hsieh Chin Fan (Pham) revised this gist Feb 13, 2018. 2 changed files with 0 additions and 4 deletions.
    2 changes: 0 additions & 2 deletions MBTilesServer.kt
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,3 @@
    package io.typebrook.fivemoreminutes.localServer

    import android.util.Log
    import java.io.BufferedReader
    import java.io.ByteArrayOutputStream
    2 changes: 0 additions & 2 deletions MBTilesSource.kt
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,3 @@
    package io.typebrook.fivemoreminutes.localServer

    import android.database.sqlite.SQLiteDatabase
    import org.jetbrains.anko.db.MapRowParser
    import org.jetbrains.anko.db.select
  22. Hsieh Chin Fan (Pham) revised this gist Feb 12, 2018. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions readme.md
    Original file line number Diff line number Diff line change
    @@ -11,10 +11,9 @@ Inspired from similar approach by swift in iOS SDK, made by [namannik](https://g
    When a `MBTilesSource` instance activates, it starts a localhost `MBTilesServer` within your app and works same as common Mapbox Raster/Vector Source.

    `MBTilesServer` is a [Singleton](https://kotlinlang.org/docs/reference/object-declarations.html), could contains more than 1 `MBTilesSource`.

    The URL for MBTilesSource is `http://localhost:8888/[mbSource.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](https://www.mapbox.com/android-docs/api/map-sdk/5.4.1/index.html) 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](https://www.mapbox.com/mapbox-gl-js/style-spec) and [example](https://github.com/openmaptiles/osm-liberty-gl-style/blob/gh-pages/style.json) by OpenMapTiles, a osm vector MBTiles [provider](https://openmaptiles.com/downloads/dataset/osm/asia/taiwan/taipei/).
    Since `MBTilesSource` only works as common Mapbox Raster/Vector Source, you always need to add layer or set style for [MapboxMap](https://www.mapbox.com/android-docs/api/map-sdk/5.4.1/index.html) 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](https://www.mapbox.com/mapbox-gl-js/style-spec) here, also [example](https://github.com/openmaptiles/osm-liberty-gl-style/blob/gh-pages/style.json) by OpenMapTiles, an osm vector MBTiles [provider](https://openmaptiles.com/downloads/dataset/osm/asia/taiwan/taipei/).

    ### Installation

  23. Hsieh Chin Fan (Pham) revised this gist Feb 12, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion readme.md
    Original file line number Diff line number Diff line change
    @@ -60,5 +60,5 @@ mapboxMap.setStyleUrl("path/to/styleFile.json")

    ### How it looks like?

    I created this for my app, [Five More Minutes](https://i.imgur.com/g9CpSB3.gifv).
    I created this for my app, [Five More Minutes]https://github.com/typebrook/FiveMoreMinutes).
    This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from OpenMaptiles.
  24. Hsieh Chin Fan (Pham) revised this gist Feb 12, 2018. 1 changed file with 2 additions and 3 deletions.
    5 changes: 2 additions & 3 deletions readme.md
    Original file line number Diff line number Diff line change
    @@ -60,6 +60,5 @@ mapboxMap.setStyleUrl("path/to/styleFile.json")

    ### How it looks like?

    I created this for my app, [Five More Minutes](https://github.com/typebrook/FiveMoreMinutes).
    This demo use vector MBTiles and style from OpenMaptiles.
    <img src="https://i.imgur.com/Kjw4Bw8.gif" width="320" />
    I created this for my app, [Five More Minutes](https://i.imgur.com/g9CpSB3.gifv).
    This [demo](https://i.imgur.com/g9CpSB3.gifv) use vector MBTiles and style from OpenMaptiles.
  25. Hsieh Chin Fan (Pham) revised this gist Feb 12, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion readme.md
    Original file line number Diff line number Diff line change
    @@ -62,4 +62,4 @@ mapboxMap.setStyleUrl("path/to/styleFile.json")

    I created this for my app, [Five More Minutes](https://github.com/typebrook/FiveMoreMinutes).
    This demo use vector MBTiles and style from OpenMaptiles.
    <img src="https://i.imgur.com/g9CpSB3.gif" width="320" /><img src="https://i.imgur.com/Kjw4Bw8.gif" width="320" />
    <img src="https://i.imgur.com/Kjw4Bw8.gif" width="320" />
  26. Hsieh Chin Fan (Pham) revised this gist Feb 12, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion readme.md
    Original file line number Diff line number Diff line change
    @@ -62,4 +62,4 @@ mapboxMap.setStyleUrl("path/to/styleFile.json")

    I created this for my app, [Five More Minutes](https://github.com/typebrook/FiveMoreMinutes).
    This demo use vector MBTiles and style from OpenMaptiles.
    <img src="https://i.imgur.com/YQ9HC2x.gifv" width="320" />
    <img src="https://i.imgur.com/g9CpSB3.gif" width="320" /><img src="https://i.imgur.com/Kjw4Bw8.gif" width="320" />
  27. Hsieh Chin Fan (Pham) revised this gist Feb 12, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion readme.md
    Original file line number Diff line number Diff line change
    @@ -62,4 +62,4 @@ mapboxMap.setStyleUrl("path/to/styleFile.json")

    I created this for my app, [Five More Minutes](https://github.com/typebrook/FiveMoreMinutes).
    This demo use vector MBTiles and style from OpenMaptiles.
    <img src="https://i.imgur.com/Ok50rI2.gif" width="320" />
    <img src="https://i.imgur.com/YQ9HC2x.gifv" width="320" />
  28. Hsieh Chin Fan (Pham) revised this gist Feb 12, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion readme.md
    Original file line number Diff line number Diff line change
    @@ -62,4 +62,4 @@ mapboxMap.setStyleUrl("path/to/styleFile.json")

    I created this for my app, [Five More Minutes](https://github.com/typebrook/FiveMoreMinutes).
    This demo use vector MBTiles and style from OpenMaptiles.
    <img src="https://i.imgur.com/Ok50rI2.gifv" width="320" />
    <img src="https://i.imgur.com/Ok50rI2.gif" width="320" />
  29. Hsieh Chin Fan (Pham) revised this gist Feb 12, 2018. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions readme.md
    Original file line number Diff line number Diff line change
    @@ -61,5 +61,5 @@ mapboxMap.setStyleUrl("path/to/styleFile.json")
    ### How it looks like?

    I created this for my app, [Five More Minutes](https://github.com/typebrook/FiveMoreMinutes).

    ![This demo use vector MBTiles and style from OpenMaptiles](https://i.imgur.com/Ok50rI2.gifv)
    This demo use vector MBTiles and style from OpenMaptiles.
    <img src="https://i.imgur.com/Ok50rI2.gifv" width="320" />
  30. Hsieh Chin Fan (Pham) revised this gist Feb 12, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion readme.md
    Original file line number Diff line number Diff line change
    @@ -62,4 +62,4 @@ mapboxMap.setStyleUrl("path/to/styleFile.json")

    I created this for my app, [Five More Minutes](https://github.com/typebrook/FiveMoreMinutes).

    [This demo use vector MBTiles and style from OpenMaptiles](https://i.imgur.com/Ok50rI2.gifv)
    ![This demo use vector MBTiles and style from OpenMaptiles](https://i.imgur.com/Ok50rI2.gifv)