Skip to content

Adding data to the map

Sources and layers

As covered in Styling the map, the data displayed on the map is defined by the style, which is provided as a URI to a JSON object. However, this library provides a way to add additional data to the map at runtime.

A map style primarily consists of sources and layers. Sources contain the data included on the map, and layers configure how that data is displayed. With MapLibre Compose, you can dynamically configure sources and layers with @Composable functions:

MaplibreMap(styleUri = "https://tiles.openfreemap.org/styles/liberty") {
  val tiles = getBaseSource(id = "openmaptiles")
  CircleLayer(id = "example", source = tiles, sourceLayer = "poi")
}

The above example shows how to add a layer referring to a source from the base style, but you can also declare new sources:

val amtrakRoutes =
  rememberGeoJsonSource(
    id = "amtrak-routes",
    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),
)

The full breadth of layer styling options available is out of scope for this guide; see the MapLibre Style Specification for more information.

Expressions

MapLibre styles support expressions, which define a formula for computing the value of a property at runtime. The expressions specification is based on JSON, but this library provides a Kotlin DSL for expressing these formulas with some type safety. Where suitable, Compose types like Dp, Color, and DpOffset are used instead of their corresponding raw JSON-like types. When passing a constant value instead of a formula, wrap it in const() to turn it into an expression.

LineLayer(
  id = "amtrak-routes",
  source = amtrakRoutes,
  cap = const(LineCap.Round),
  join = const(LineJoin.Round),
  color = const(Color.Blue),
  width =
    interpolate(
      type = exponential(const(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),
    ),
)

Anchoring layers

The default behavior for layers defined by Composable functions is to place them at the top of the map, above the base style layers. However, you can insert them at other positions in the stack of layers with Anchor:

Anchor.Above("road_motorway") { LineLayer(id = "amtrak-routes", source = amtrakRoutes) }

Anchors to insert layers at the Bottom, Top, Above a base layer, Below a base layer, or Replace a base layer are provided.

Interacting with layers

Layer composables provide click listeners, similar to the map itself. You can listen for clicks on a layer and consume or pass those click events:

CircleLayer(
  id = "amtrak-stations",
  source = amtrakStations,
  onClick = { features ->
    println("Clicked on ${features[0].json()}")
    ClickResult.Consume
  },
)

Click listeners are called on the map first, then in layer order from the top to the bottom of the map. The first listener to consume the event will prevent it from propagating to subsequent listeners.