MapCameraPositionInterface (Camera Position)
MapCameraPositionInterface represents the camera’s view position, orientation, and visible region on the map. It defines where the camera is looking, how much of the map is visible, and the viewpoint used to display the map.
Interface and Implementation
MapCameraPositionInterface Interface
interface MapCameraPositionInterface { val position: GeoPointInterface val zoom: Double val bearing: Double val tilt: Double val paddings: MapPaddingsInterface? val visibleRegion: VisibleRegion?}MapCameraPosition
The main implementation provides immutable camera position data:
data class MapCameraPosition( override val position: GeoPoint, override val zoom: Double = 0.0, override val bearing: Double = 0.0, override val tilt: Double = 0.0, override val paddings: MapPaddingsInterface? = MapPaddings.Zeros, override val visibleRegion: VisibleRegion? = null) : MapCameraPositionInterfaceProperties
Camera Position
position: GeoPointInterface: The geographic center point of the camera viewzoom: Double: The zoom level (approximately follows the Google Maps scale)bearing: Double: The compass direction in degrees (0 = north, 90 = east)tilt: Double: The camera tilt angle in degrees (0 = straight down, 90 = horizontal)
View Configuration
paddings: MapPaddingsInterface?: Viewport padding that affects the visible regionvisibleRegion: VisibleRegion?:(read-only)The geographic bounds actually displayed on screen
Creation Methods
Default Position
// Default camera position at the originval defaultPosition = MapCameraPosition.DefaultCustom Position
// Basic camera positionval sanFrancisco = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), zoom = 15)
// Camera with bearing and tiltval aerialView = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), zoom = 18, bearing = 45, // Northeast direction tilt = 60 // Angled view)Zoom Levels
MapConductor zoom levels approximately follow the Google Maps scale, but they may vary slightly between map SDKs:
- 0-2: World view, continents are visible
- 3-5: Country level
- 6-9: State/region level
- 10-12: City level
- 13-15: District/neighborhood level
- 16-18: Street level
- 19-21: Building level (high detail)
// Different zoom levels for different use casesval worldView = MapCameraPosition( position = GeoPoint.fromLatLong(0, 0), zoom = 2 // Show continents)
val cityView = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), zoom = 12 // Show the entire city)
val streetView = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), zoom = 17 // Show individual streets)Bearing and Tilt
Bearing (Rotation)
Bearing rotates the map around the center point:
// North up (default)val northUp = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), bearing = 0)
// East upval eastUp = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), bearing = 90)
// Follow the route bearingval routeBearing = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), bearing = 135, // Southeast zoom = 18.0)Tilt (3D Viewpoint)
Tilt provides a 3D viewing angle:
// Top-down view (default)val topDown = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), tilt = 0)
// Tilted view for depthval angled = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), tilt = 30, zoom = 16)
// Maximum street-level tiltval streetLevel = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), tilt = 80, bearing = 45, zoom = 19)Negative tilt Values - Looking Forward
When tilt is set to a negative value, the camera uses an upward-looking elevation view above the horizon. Positive tilt is treated as a view looking down at the ground, while negative tilt is treated as a view looking forward in the bearing direction.
// 通常の俯瞰ビュー(従来の動作)MapCameraPosition( position = GeoPoint(35.68, 139.76), zoom = 15.0, tilt = 60.0, // 地面を60度の角度で見下ろす)
// 前方(水平線上方)を見るMapCameraPosition( position = GeoPoint(35.68, 139.76), zoom = 15.0, tilt = -30.0, // 水平線より30度上方を見る bearing = 45.0,)
// ArcGIS はネイティブ対応- ArcGIS: ArcGIS のカメラ仕様と同じく、負の tilt にネイティブ対応します。
- Google Maps / Mapbox / MapLibre / HERE: 上向きピッチを直接表現できないため、カメラ位置を固定し、bearing 方向の前方へターゲットを移動してシミュレーションします。ネイティブの仰角ビューと完全に同一にはなりません。
Visible Region
The visible region describes the actual geographic area displayed on screen after accounting for the camera position, zoom level, bearing, tilt, and viewport padding.
VisibleRegion Class
data class VisibleRegion( val bounds: GeoRectBounds, // Overall bounding rectangle val nearLeft: GeoPointInterface?, // Lower-left corner (side closest to the camera) val nearRight: GeoPointInterface?, // Lower-right corner (side closest to the camera) val farLeft: GeoPointInterface?, // Upper-left corner (side farthest from the camera) val farRight: GeoPointInterface? // Upper-right corner (side farthest from the camera))Properties
bounds: GeoRectBounds: Geographic bounds of the rectangle that contains the entire visible region. This is the smallest bounding rectangle that contains all visible content.nearLeft: GeoPointInterface?: Geographic coordinates of the lower-left corner of the visible region. “near” refers to the side closest to the camera position.nearRight: GeoPointInterface?: Geographic coordinates of the lower-right corner of the visible region.farLeft: GeoPointInterface?: Geographic coordinates of the upper-left corner of the visible region. “far” refers to the side farthest from the camera position.farRight: GeoPointInterface?: Geographic coordinates of the upper-right corner of the visible region.
Using VisibleRegion
@Composablefun VisibleRegionExample() {
val mapViewState = rememberMapboxMapViewState( cameraPosition = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), zoom = 15.0 ), ) var visibleRegionInfo by remember { mutableStateOf<VisibleRegion?>(null) }
// Replace MapView with your chosen map SDK, such as GoogleMapView or MapboxMapView MapboxMapView( state = mapViewState, modifier = modifier, onCameraMoveEnd = { cameraPosition -> cameraPosition.visibleRegion?.let { visibleRegion -> visibleRegionInfo = visibleRegion } }, ) { // Display visible region information visibleRegionInfo?.let { region -> // Show the bounds as a polygon region.bounds.let { bounds -> if (!bounds.isEmpty) { val sw = bounds.southWest!! val ne = bounds.northEast!!
Polygon( points = listOf( sw, GeoPoint.fromLatLong(sw.latitude, ne.longitude), ne, GeoPoint.fromLatLong(ne.latitude, sw.longitude), sw ), strokeColor = Color.Blue, strokeWidth = 2.dp, fillColor = Color.Blue.copy(alpha = 0.1f) ) } } } }}Animations and Transitions
Smooth Camera Movement
@Composablefun AnimatedCameraExample() { val locations = listOf( GeoPoint.fromLatLong(37.7749, -122.4194), // San Francisco GeoPoint.fromLatLong(40.7128, -74.0060), // New York GeoPoint.fromLatLong(51.5074, -0.1278) // London )
val mapViewState = rememberHereMapViewState( cameraPosition = MapCameraPosition( position = locations[0], zoom = 6.0 ), )
var currentIndex by remember { mutableStateOf(0) }
// Animate to the next location every 5 seconds LaunchedEffect(Unit) { while (true) { delay(5000) currentIndex = (currentIndex + 1) % locations.size
val targetPosition = MapCameraPosition( position = locations[currentIndex], zoom = 6.0, bearing = 0.0, tilt = 0.0 ) mapViewState.moveCameraTo(targetPosition, 1000) } }
// Replace MapView with your chosen map provider, such as GoogleMapView, MapboxMapView HereMapView( state = mapViewState, modifier = modifier, ) { // Add markers for each location locations.forEachIndexed { index, location -> Marker( position = location, icon = ColorDefaultIcon( fillColor = if (index == currentIndex) Color.Red else Color.Gray, label = when (index) { 0 -> "SF" 1 -> "NYC" 2 -> "LON" else -> "$index" } ) ) } }}Interactive Camera Control
@Composablefun CameraControlExample(modifier: Modifier = Modifier) { val mapViewState = rememberMapLibreMapViewState( mapDesign = MapLibreDesignType( id = "debug-tiles", styleJsonURL = "https://demotiles.maplibre.org/debug-tiles/style.json", ), cameraPosition = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), zoom = 15.0 ), )
Column( modifier = modifier, ) { // Camera controls Row { Button( onClick = { val newZoom = mapViewState.cameraPosition.copy( zoom = (mapViewState.cameraPosition.zoom + 1) .coerceAtMost(21.0), ) mapViewState.moveCameraTo(newZoom, 500) } ) { Text("Zoom In") }
Button( onClick = { val newZoom = mapViewState.cameraPosition.copy( zoom = (mapViewState.cameraPosition.zoom - 1) .coerceAtMost(21.0), ) mapViewState.moveCameraTo(newZoom, 500) } ) { Text("Zoom Out") }
Button( onClick = { val newZoom = mapViewState.cameraPosition.copy( bearing = (mapViewState.cameraPosition.bearing + 45) % 360, ) mapViewState.moveCameraTo(newZoom, 500) } ) { Text("Rotate") } }
// Tilt slider Slider( value = mapViewState.cameraPosition.tilt.toFloat(), onValueChange = { tilt -> val newZoom = mapViewState.cameraPosition.copy( tilt = tilt.toDouble(), ) mapViewState.moveCameraTo(newZoom) }, valueRange = 0f..80f )
// Replace MapView with your chosen map SDK, such as GoogleMapView or MapboxMapView MapLibreMapView( state = mapViewState, ) { Marker( position = mapViewState.cameraPosition.position, icon = ColorDefaultIcon(fillColor = Color.Red) ) } }}Fitting Bounds
Use fitBounds to move and zoom the camera so that a GeoRectBounds is fully visible:
val bounds = GeoRectBounds( southWest = GeoPoint.fromLatLong(37.7649, -122.4294), northEast = GeoPoint.fromLatLong(37.7849, -122.4094))mapViewState.fitBounds(bounds, padding = 32)The padding parameter adds screen-space inset in pixels on all sides. Each provider calculates the resulting zoom level natively.
Showing Multiple Points
To frame a set of markers or route points, extend a bounds and then fit:
// Calculate bounds containing all points, then set appropriate zoomval allPoints = listOf(/* your points */)val bounds = GeoRectBounds()allPoints.forEach { bounds.extend(it) }
val centerCamera = MapCameraPosition( position = bounds.center ?: GeoPoint.fromLatLong(0.0, 0.0), zoom = calculateZoomForBounds(bounds), // Use fitBounds instead of manual zoom calculation bearing = 0.0, tilt = 0.0)The fitBounds call replaces the manual zoom calculation shown above:
val bounds = GeoRectBounds()allPoints.forEach { bounds.extend(it) }if (!bounds.isEmpty) { mapViewState.fitBounds(bounds, padding = 48)}