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:
val mapState = rememberMapState( initialCameraPosition = CameraPosition(target = Position(latitude = 45.521, longitude = -122.675), zoom = 13.0) )MaplibreMap(state = mapState)Animate the camera
Section titled “Animate the camera”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:
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.
Fit a bounding box
Section titled “Fit a bounding box”MapState.animateCameraToBounds fits a BoundingBox in the current
viewport. Padding adds space between the box and the map edges:
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.
Read the visible area
Section titled “Read the visible area”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:
val viewport = mapState.viewportif (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:
val screenOffset = mapState.screenLocationFromPosition(mapState.cameraPosition.target)val geoPosition = mapState.positionFromScreenLocation(DpOffset(x = 100.dp, y = 150.dp))The repeated world and the antimeridian
Section titled “The repeated world and the antimeridian”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.