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

Control the camera

The camera defines the visible part of the map: a target position, a zoom level, a bearing, and a tilt. rememberMapState creates a MapState with a read-only camera position. Pass an initial CameraPosition to set the camera at startup:

App.kt
val mapState =
rememberMapState(
initialCameraPosition =
CameraPosition(target = Position(latitude = 45.521, longitude = -122.675), zoom = 13.0)
)
MaplibreMap(state = mapState)

MapState.animateCameraPosition is a suspend function that moves the map to a new position over a duration. Call it from a coroutine. The function waits until the map is attached:

App.kt
LaunchedEffect(mapState) {
mapState.animateCameraPosition(
position =
mapState.cameraPosition.copy(target = Position(latitude = 47.607, longitude = -122.342)),
duration = 3.seconds,
)
}

Use MapState.setCameraPosition to move without an animation. The position is retained when the map is detached and restored when it is displayed again.

MapState.animateCameraToBounds fits a BoundingBox in the current viewport. Padding adds space between the box and the map edges:

App.kt
LaunchedEffect(mapState) {
mapState.animateCameraToBounds(
boundingBox = BoundingBox(west = -123.0, south = 47.0, east = -122.0, north = 48.0),
padding = PaddingValues(32.dp),
)
}

Use MapState.fitCameraToBounds to fit the same bounding box without an animation.

MapState.viewport reports the current rendered size, visible bounds, and visible region. It is null until the map renders its first viewport. A composition that reads it recomposes when the camera moves or the map resizes:

App.kt
val viewport = mapState.viewport
if (viewport != null) {
Text("Visible bounds: ${viewport.visibleBounds}")
}

Convert between screen and geographic coordinates

Section titled “Convert between screen and geographic coordinates”

MapState.screenLocationFromPosition converts a geographic position to an offset from the top-left corner of the map composable. MapState.positionFromScreenLocation converts in the other direction:

App.kt
val screenOffset = mapState.screenLocationFromPosition(mapState.cameraPosition.target)
val geoPosition = mapState.positionFromScreenLocation(DpOffset(x = 100.dp, y = 150.dp))

MapLibre repeats the world horizontally. Geographic values read from the map, such as Viewport.visibleBounds and MapState.positionFromScreenLocation, preserve the world copy: longitudes may extend past ±180°, and the visible bounds may span more than 360°. VisibleBounds.toBoundingBox() converts to a GeoJSON BoundingBox, where an antimeridian crossing follows RFC 7946 with an east longitude less than the west.