Skip to content
This site is a preview of pull request #1363.

Add data to the map

The base style defines the map’s content. To show your own data on top of it, declare sources and layers in the trailing rememberMapState block, then pass that state to MaplibreMap. A source holds the data, and a layer draws data from a source.

getBaseSource looks up a source that the base style defines, so a layer of yours can draw data that the style already loads:

App.kt
val baseState =
rememberMapState(baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty")) {
getBaseSource<VectorTileSource>(id = "openmaptiles")?.let { tiles ->
CircleLayer(id = "example", source = tiles, sourceLayer = "poi")
}
}
MaplibreMap(state = baseState)

rememberGeoJsonSource creates a source from a GeoJSON URI or string. Layers reference the source object directly:

App.kt
val state = rememberMapState {
val amtrakStations =
rememberGeoJsonSource(GeoJsonData.Uri(Res.getUri("files/data/amtrak_stations.geojson")))
val amtrakRoutes =
rememberGeoJsonSource(GeoJsonData.Uri(Res.getUri("files/data/amtrak_routes.geojson")))
LineLayer(
id = "amtrak-routes-casing",
source = amtrakRoutes,
color = const(Color.White),
width = const(6.dp),
)
LineLayer(
id = "amtrak-routes",
source = amtrakRoutes,
color = const(Color.Blue),
width = const(4.dp),
)
}
MaplibreMap(state = state)

Other source types cover vector tiles, raster tiles, and images. The API reference documents them under org.maplibre.compose.sources.

Layer properties accept expressions: formulas that the map evaluates at render time. Wrap a constant value in const. Functions such as interpolate and zoom build dynamic values:

App.kt
val state = rememberMapState {
val detailedAmtrakRoutes =
rememberGeoJsonSource(GeoJsonData.Uri(Res.getUri("files/data/amtrak_routes.geojson")))
LineLayer(
id = "amtrak-routes",
source = detailedAmtrakRoutes,
cap = const(LineCap.Round),
join = const(LineJoin.Round),
color = const(Color.Blue),
width =
interpolate(
type = exponential(1.2f),
input = zoom(),
5 to const(0.4.dp),
6 to const(0.7.dp),
7 to const(1.75.dp),
20 to const(22.dp),
),
)
}
MaplibreMap(state = state)

Expressions in Kotlin explains the expression system. The MapLibre Style Specification documents every layer type and property.

When a paint property changes, the map animates it over the style’s global transition. Pass a TransitionOptions to time one property instead:

App.kt
val state = rememberMapState {
val timedAmtrakStations =
rememberGeoJsonSource(GeoJsonData.Uri(Res.getUri("files/data/amtrak_stations.geojson")))
CircleLayer(
id = "amtrak-stations-timed",
source = timedAmtrakStations,
color = const(if (isSystemInDarkTheme()) Color.Cyan else Color.Blue),
colorTransition = TransitionOptions(duration = 500.milliseconds),
)
}
MaplibreMap(state = state)

Every transitionable paint property has a matching transition parameter, named after the property. The default, null, uses the global transition that StyleTransition reads and sets.

By default, your layers draw above the base style layers. Anchor inserts a layer at another position: at the Bottom, at the Top, or Above or Below a base style layer selected by ID or by a predicate over its LayerHandle:

App.kt
val state = rememberMapState {
val anchoredAmtrakRoutes =
rememberGeoJsonSource(GeoJsonData.Uri(Res.getUri("files/data/amtrak_routes.geojson")))
// Below the base style's labels, whatever the style names them
Anchor.Below({ it.type == "symbol" }) {
LineLayer(id = "amtrak-routes", source = anchoredAmtrakRoutes)
}
// Above one base layer, named by its ID
Anchor.Above("road_motorway") {
LineLayer(id = "amtrak-routes-casing", source = anchoredAmtrakRoutes)
}
}
MaplibreMap(state = state)