Skip to content
This site is a preview of the main branch. The published documentation is at maplibre.org.

Rewrite or serve map requests

MapLibre downloads the style, tiles, fonts, and icons from URLs in the style. You can change these requests from Kotlin code. Do this when:

  • your tile server needs an API key or an Authorization header,
  • requests must go to a different server than the URLs in the style, or
  • the app has tiles or fonts in its resources, in a database, or in a file that MapLibre cannot read directly.

A MapRequestInterceptor changes the URL or headers of a request before MapLibre sends it. A MapResourceProvider returns the data for a request from your code, so MapLibre sends no request. Both work on every platform, and they also apply to snapshots and offline downloads.

This interceptor adds a token to requests for one tile server:

MapRequests.kt
val interceptor =
MapRequestInterceptor(
headers = { request ->
val currentToken = token.value
if (request.url.startsWith("https://tiles.example.com/") && currentToken != null) {
mapOf("Authorization" to "Bearer $currentToken")
} else {
emptyMap()
}
}
)

Check the URL before you add credentials. A style often loads resources from several servers, and a token for your server should not go to the others.

The example reads the token from a StateFlow on each request, so a new token applies to later requests.

rewriteUrl returns the URL to fetch instead, or null to keep the original. This interceptor adds an API key as a query parameter:

MapRequests.kt
val interceptor =
MapRequestInterceptor(
rewriteUrl = { request ->
if (request.url.startsWith("https://tiles.example.com/")) {
val separator = if ('?' in request.url) '&' else '?'
"${request.url}${separator}key=$apiKey"
} else {
null
}
}
)

A provider returns the bytes for URLs it accepts. Give these URLs your own scheme in the style, such as app://tiles/{z}/{x}/{y}.pbf as a vector source’s tile URL. This provider reads them from the app’s Compose resources:

MapRequests.kt
// Serves app://tiles/14/2627/5721.pbf from composeResources/files/tiles/14/2627/5721.pbf
val provider =
MapResourceProvider(scheme = "app") { request ->
Res.readBytes("files/" + request.url.removePrefix("app://"))
}

To choose the requests yourself, pass accepts and load, and return a MapResourceLoad. Return MapResourceLoad.NoContent for a tile that has no data, so MapLibre draws an empty tile and reports no error. If load throws, the request fails:

MapRequests.kt
val provider =
MapResourceProvider(
accepts = { request ->
request.kind == MapResourceKind.Tile && request.url.startsWith("app://tiles/")
},
load = { request ->
when (val bytes = readTile(request.url)) {
null -> MapResourceLoad.NoContent()
else -> MapResourceLoad.Bytes(bytes)
}
},
)

Set them on the default runtime with DefaultMapRuntime.configure. Call it once at app startup, such as in Application.onCreate on Android or in main on desktop, before the app shows a map:

App.kt
fun configureMaps(interceptor: MapRequestInterceptor, provider: MapResourceProvider) {
DefaultMapRuntime.configure {
requestInterceptor = interceptor
resourceProvider = provider
}
}

The settings cannot change after the runtime exists. To change a token, update the value that the interceptor reads.

To use different settings for some maps only, create a separate runtime with createMapRuntime and pass it to rememberMapState. Close the runtime when you no longer need it:

App.kt
val runtime = remember { createMapRuntime { requestInterceptor = interceptor } }
DisposableEffect(runtime) { onDispose { runtime.close() } }
val state =
rememberMapState(
runtime = runtime,
baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty"),
)
MaplibreMap(state = state)