プロバイダを切り替える
プロバイダごとに違うのは、依存モジュール・地図ビューの型・状態オブジェクトの型の 3 つだけです。マーカーや図形、カメラ操作、イベントの書き方は変わりません。同じ画面に複数のプロバイダを並べて、実行時に切り替えることもできます。
01 · 何が変わり、何が変わらないか
- 依存モジュール(for-maplibre → for-googlemaps など)
- 地図ビューの型(MapLibreMapView → GoogleMapView)
- 状態オブジェクトの型と、その生成関数
- mapDesignType に渡すデザイン定数
- APIキーの取得方法と初期化の作法
- GeoPoint · GeoRectBounds · MapCameraPosition
- MarkerState · PolylineState · PolygonState · CircleState …
- moveCameraTo() · fitBounds() · イベントハンドラ
- 地理計算(距離・方位・面積)
- 拡張レイヤー(クラスタリング・ヒートマップ・GeoJSON)
状態オブジェクトの型はプロバイダごとに分かれていますが、共通のインターフェース(Android: MapViewStateInterface、iOS: MapViewStateProtocol、React: MapViewStateInterface)を実装します。カメラ操作やデザイン切り替えは、この共通インターフェース越しに書けます。





02 · プロバイダ別の型
各プロバイダが公開する地図ビューと状態オブジェクトの一覧です。歴史的な経緯で名前が完全には揃っていない箇所があるため、実際に export されている名前をそのまま載せています。
rememberGoogleMapViewState
GoogleMapViewState
GoogleMapViewState
useGoogleMapViewState
rememberMapLibreMapViewState
MapLibreViewState
MapLibreViewState
useMapLibreViewState
rememberMapboxMapViewState
MapboxViewState
MapboxViewState
useMapboxViewState
rememberHereMapViewState
HereViewState
HereMapViewState
useHereViewState
rememberArcGISMapViewState
ArcGISMapViewState
ArcGISMapViewState
useArcGISViewState
rememberMapTilerMapViewState
MapTilerViewState
MapTilerViewState
useMapTilerViewState
rememberTomTomMapViewState
TomTomMapViewState
TomTomMapViewState
useTomTomViewState
rememberLongdoMapViewState
LongdoViewState
LongdoViewState
useLongdoViewState
MapKitViewState
useMapKitViewState
useLeafletMapViewState
useOpenLayersMapViewState
useAzureMapsViewState
useCesiumMapViewState
「/ 2D」は 3D 表示と 2D 表示の 2 つのビューがあることを示します。どちらも同じ状態オブジェクトを共有し、2D では tilt が常に 0 になります。
03 · 切り替えの書き方
実行時に切り替えたいときは、状態オブジェクトをプロバイダごとに 1 つずつ持っておき、表示するビューだけを分岐します。オーバーレイの記述は分岐の外に置けます。サンプルアプリはこの形です。
@Composable
fun MapViewContainer(
modifier: Modifier = Modifier,
state: MapViewStateInterface<*>? = null,
content: (@Composable MapViewScope.() -> Unit)? = null,
) {
when (state) {
is GoogleMapViewState -> GoogleMapView(modifier, state, content = content)
is MapLibreViewState -> MapLibreMapView(modifier, state, content = content)
is MapboxViewState -> MapboxMapView(modifier, state, content = content)
is HereViewState -> HereMapView(modifier, state, content = content)
else -> { /* not selected */ }
}
}
// 呼び出し側はオーバーレイだけを書く
MapViewContainer(state = selectedState) {
Markers(markerStates)
}@StateObject private var googleState = GoogleMapViewState(cameraPosition: initial)
@StateObject private var mapLibreState = MapLibreViewState(cameraPosition: initial)
// 共通インターフェース越しならカメラ操作は分岐なしで書ける
private var activeState: any MapViewStateProtocol {
provider == .googleMaps ? googleState : mapLibreState
}
@ViewBuilder
private var mapView: some View {
switch provider {
case .googleMaps:
GoogleMapView(state: googleState, sdkInitialize: {
GMSServices.provideAPIKey(apiKey)
}) { Marker(state: markerState) }
case .mapLibre:
MapLibreMapView(state: mapLibreState) { Marker(state: markerState) }
}
}// フックはコンポーネントごとに固定で呼ぶ。ビューだけを差し替える
const maplibre = useMapLibreViewState({ id: 'maplibre', cameraPosition: INIT });
const google = useGoogleMapViewState({ id: 'google', cameraPosition: INIT });
switch (provider) {
case 'maplibre':
return <MapLibreMapView2D state={maplibre}>{children}</MapLibreMapView2D>;
case 'google-maps':
return <GoogleMapView2D state={google}>{children}</GoogleMapView2D>;
}React ではフックの呼び出し順を変えられないため、プロバイダごとのビューを別コンポーネントに切り出し、そのコンポーネントの中でフックを呼ぶ形が安全です。サンプルアプリの providers/ ディレクトリがその構成です。
// プロバイダごとに状態を 1 つずつ持つ
val maplibre = rememberMapLibreMapViewState(
mapDesign = MapLibreDesign.OsmBrightJa,
cameraPosition = MapCameraPosition(
position = GeoPoint.fromLatLong(35.6812, 139.7671),
zoom = 12.0,
),
)
val googlemaps = rememberGoogleMapViewState(
cameraPosition = MapCameraPosition(
position = GeoPoint.fromLatLong(35.6812, 139.7671),
zoom = 12.0,
),
)
var useMapLibre by remember { mutableStateOf(true) }
// オーバーレイの宣言は分岐の外に置く。差し替えても書き直さない
val overlays: @Composable MapViewScope.() -> Unit = {
Marker(markerState)
Polyline(routeState)
}
// 切り替えた側へ、直前まで見えていたカメラを引き継ぐ
LaunchedEffect(useMapLibre) {
if (useMapLibre) {
maplibre.moveCameraTo(googlemaps.cameraPosition)
} else {
googlemaps.moveCameraTo(maplibre.cameraPosition)
}
}
if (useMapLibre) {
MapLibreMapView(state = maplibre, content = overlays)
} else {
GoogleMapView(state = googlemaps, content = overlays)
}04 · プロバイダ差の扱い
すべての地図 SDK が同じ機能を持つわけではありません。MapConductor は差を埋められるところは埋め、埋められないところは代替表現を用意します。
キーと初期化はプロバイダごと
MapConductor は APIキーを預かりません。各地図 SDK が求める作法(Android のマニフェスト、iOS の sdkInitialize、Web の設定オブジェクト)でそのまま渡します。MapLibre・Leaflet・OpenLayers のようにキーが不要なプロバイダもあり、その場合はタイル配信元だけを決めます。