Add images and icons
A SymbolLayer draws an icon at each feature in its source. Its
iconImage parameter takes an image expression, which chooses the
icon.
Draw icons from a Compose painter
Section titled “Draw icons from a Compose painter”Pass any Compose Painter to image(). You can load one with
painterResource or draw your own. This painter draws a badge using your
Material theme’s colors and typography:
@Composableprivate fun rememberBadgePainter(label: String): Painter { val colors = MaterialTheme.colorScheme val style = MaterialTheme.typography.labelLarge.copy(color = colors.onPrimaryContainer) val text = rememberTextMeasurer().measure(label, style = style) val padding = with(LocalDensity.current) { 12.dp.toPx() } return remember(colors.primaryContainer, text, padding) { object : Painter() { override val intrinsicSize = Size(text.size.width + padding * 2, text.size.height + padding)
override fun DrawScope.onDraw() { drawRoundRect(colors.primaryContainer, cornerRadius = CornerRadius(size.height / 2)) drawText( text, topLeft = Offset((size.width - text.size.width) / 2, (size.height - text.size.height) / 2), ) } } }}Use the painter as an icon on a SymbolLayer:
val position = Position(longitude = -74.0120, latitude = 40.7067)val state = rememberMapState(initialCameraPosition = CameraPosition(center = position, zoom = 15.5)) { val source = rememberGeoJsonSource(GeoJsonData.Features(Feature(Point(position), properties = null))) SymbolLayer( id = "badge", source = source, iconImage = image(rememberBadgePainter("12 min")), iconAllowOverlap = const(true), ) }MaplibreMap(state = state)image() uses the painter’s intrinsic size; pass size to set a different
icon size. To color a single-color icon from the layer,
pass drawAsSdf = true to image() and set the layer’s iconColor.
The map hides an icon that would overlap another icon or label. Set
iconAllowOverlap = const(true) on the layer to draw every icon.
Place an image on the map
Section titled “Place an image on the map”rememberImageSource pins one image to the map at four corner
coordinates, and a RasterLayer draws it. The image moves and
scales with the map. This example lays a 1660 plan of New Amsterdam over lower
Manhattan:
val state = rememberMapState { val corners = PositionQuad( topLeft = Position(longitude = -74.0178, latitude = 40.7040), topRight = Position(longitude = -74.0118, latitude = 40.7100), bottomRight = Position(longitude = -74.0060, latitude = 40.7067), bottomLeft = Position(longitude = -74.0120, latitude = 40.7006), ) val plan = rememberImageSource(position = corners, uri = Res.getUri("files/castello-plan.jpg")) RasterLayer(id = "castello-plan", source = plan, opacity = const(0.8f))}MaplibreMap(state = state)The corners do not need to form a rectangle that faces north, so you can
rotate the image to fit the map. To draw pixels that your app creates, pass a
PreparedImage instead of a URI.
Supply a missing image
Section titled “Supply a missing image”A layer can ask for an icon that the style does not contain, for example when
your data names an icon for each feature. The map then draws no icon for that
feature. Set MapState.missingImageResolver to supply the image
when the map asks for it. This example draws a fallback painter for every
missing icon:
val mapState = rememberMapState()val density = LocalDensity.currentval layoutDirection = LocalLayoutDirection.currentDisposableEffect(mapState, fallback, density, layoutDirection) { mapState.missingImageResolver = { ResolvedStyleImage.fromPainter(fallback, density, layoutDirection) } onDispose { mapState.missingImageResolver = null }}MaplibreMap(state = mapState)The resolver receives a MissingImageRequest with the icon’s ID.
Return a ResolvedStyleImage, or null to leave the icon empty.
To add or replace an image under a fixed ID from code, call
StyleImages.set on mapState.style.images.