MapCameraPositionInterface(カメラ位置)
MapCameraPositionInterface は、カメラの視野位置、方向、および地図上の可視領域を表します。カメラがどこを見ているか、地図のどの程度が表示されるか、地図を表示する視点を定義します。
インターフェースと実装
MapCameraPositionInterface インターフェース
interface MapCameraPositionInterface { val position: GeoPointInterface val zoom: Double val bearing: Double val tilt: Double val paddings: MapPaddingsInterface? val visibleRegion: VisibleRegion?}MapCameraPosition
メイン実装は不変のカメラ位置データを提供します:
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) : MapCameraPositionInterfaceプロパティ
カメラ位置
position: GeoPointInterface: カメラの視野の地理的中心点zoom: Double: ズームレベル(おおよそ Google Maps のスケールに従います)bearing: Double: コンパスの方向(度数)(0 = 北、90 = 東)tilt: Double: カメラの傾斜角度(度数)(0 = 真上から、90 = 水平)
ビュー構成
paddings: MapPaddingsInterface?: 可視領域に影響するビューポートのパディングvisibleRegion: VisibleRegion?:(読み込み専用)画面上で実際に表示される地理的境界
生成メソッド
デフォルト位置
// 原点のデフォルトカメラ位置val defaultPosition = MapCameraPosition.Defaultカスタム位置
// 基本的なカメラ位置val sanFrancisco = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), zoom = 15)
// 方位角と傾きを持つカメラval aerialView = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), zoom = 18, bearing = 45, // Northeast direction tilt = 60 // Angled view)ズームレベル
MapConductor のズームレベルは、おおよそ Google Maps のスケールに従いますが、地図SDK間で若干異なる場合があります:
- 0-2: 世界ビュー、大陸が表示される
- 3-5: 国レベル
- 6-9: 州/地域レベル
- 10-12: 都市レベル
- 13-15: 地区/近隣レベル
- 16-18: ストリートレベル
- 19-21: 建物レベル(高詳細)
// Different zoom levels for different use casesval worldView = MapCameraPosition( position = GeoPoint.fromLatLong(0, 0), zoom = 2 // 大陸を表示)
val cityView = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), zoom = 12 // 都市全体を表示)
val streetView = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), zoom = 17 // 個別の通りを表示)方位角と傾き
方位角(回転)
方位角は中心点を中心に地図を回転させます:
// 北向き(デフォルト)val northUp = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), bearing = 0)
// 東向きval eastUp = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), bearing = 90)
// ルートの方位角に従うval routeBearing = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), bearing = 135, // 南東 zoom = 18.0)傾き(3D 視点)
傾きは 3D 表示角度を提供します:
// 真上からの眺め(デフォルト)val topDown = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), tilt = 0)
// 奥行きを出すための傾斜val angled = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), tilt = 30, zoom = 16)
// ストリートレベルの最大傾斜val streetLevel = MapCameraPosition( position = GeoPoint.fromLatLong(37.7749, -122.4194), tilt = 80, bearing = 45, zoom = 19)負の tilt 値 — 前方を見る
tilt にマイナス値を指定すると、水平線より上方を向く仰角ビューになります。正の tilt は地面を見下ろす視点、負の tilt は bearing 方向の前方を見る視点として扱われます。
// 通常の俯瞰ビュー(従来の動作)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 方向の前方へターゲットを移動してシミュレーションします。ネイティブの仰角ビューと完全に同一にはなりません。
可視領域
可視領域は、カメラ位置、ズームレベル、方位角、傾き、およびビューポートのパディングを考慮した後、画面上に表示される実際の地理的エリアを記述します。
VisibleRegion クラス
data class VisibleRegion( val bounds: GeoRectBounds, // 全体の境界矩形 val nearLeft: GeoPointInterface?, // 左下角(カメラに近い側) val nearRight: GeoPointInterface?, // 右下角(カメラに近い側) val farLeft: GeoPointInterface?, // 左上角(カメラから遠い側) val farRight: GeoPointInterface? // 右上角(カメラから遠い側))プロパティ
bounds: GeoRectBounds: 可視領域全体を包含する矩形の地理的境界。これは、すべての可視コンテンツを含む最小の境界矩形です。nearLeft: GeoPointInterface?: 可視領域の左下角の地理的座標。「near」はカメラ位置に最も近い側を指します。nearRight: GeoPointInterface?: 可視領域の右下角の地理的座標。farLeft: GeoPointInterface?: 可視領域の左上角の地理的座標。「far」はカメラ位置から最も遠い側を指します。farRight: GeoPointInterface?: 可視領域の右上角の地理的座標。
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) }
// MapView を GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください MapboxMapView( state = mapViewState, modifier = modifier, onCameraMoveEnd = { cameraPosition -> cameraPosition.visibleRegion?.let { visibleRegion -> visibleRegionInfo = visibleRegion } }, ) { // 可視領域情報を表示 visibleRegionInfo?.let { region -> // 境界をポリゴンとして表示 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) ) } } } }}アニメーションと遷移
スムーズなカメラ移動
@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) }
// 5秒ごとに次の位置にアニメーション 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" } ) ) } }}インタラクティブなカメラ制御
@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 )
// MapView を GoogleMapView、MapboxMapView などの選択した地図SDKに置き換えてください MapLibreMapView( state = mapViewState, ) { Marker( position = mapViewState.cameraPosition.position, icon = ColorDefaultIcon(fillColor = Color.Red) ) } }}バウンズへのフィット
fitBounds を使うと、GeoRectBounds が画面内に収まるようにカメラを移動・ズームできます:
val bounds = GeoRectBounds( southWest = GeoPoint.fromLatLong(37.7649, -122.4294), northEast = GeoPoint.fromLatLong(37.7849, -122.4094))mapViewState.fitBounds(bounds, padding = 32)padding パラメーターは全辺にスクリーンスペースのインセット(ピクセル単位)を追加します。ズームレベルの計算は各プロバイダーがネイティブに行います。
複数ポイントの表示
マーカーやルートポイントを画面内に収めるには、バウンズを拡張してからフィットします:
// 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), // 手動ズーム計算の代わりに fitBounds を使う bearing = 0.0, tilt = 0.0)上記の手動ズーム計算は fitBounds で置き換えられます:
val bounds = GeoRectBounds()allPoints.forEach { bounds.extend(it) }if (!bounds.isEmpty) { mapViewState.fitBounds(bounds, padding = 48)}