地図デザイン(タイル)を切り替える
地図の見た目は mapDesignType 1つで決まります。プロバイダごとに用意されたプリセットを選ぶか、スタイルJSON・タイルURLを指定して独自のデザインを定義します。切り替えはプロパティへの代入だけで、地図を作り直す必要はありません。
01 · デザインの型
どのプロバイダのデザインも MapDesignTypeInterface(iOS では MapDesignTypeProtocol)を実装します。共通の3要素だけを覚えれば、プロバイダが変わっても扱い方は同じです。
02 · プリセット
各プロバイダのパッケージに、そのプロバイダで使える標準デザインが定数として入っています。サンプルアプリはこの一覧をそのまま選択肢にしています。
iOS · React
React: MapboxDesign
React は Streets · Outdoors · Light · Dark · SatelliteStreets + OsmBright / MapTiler 系のスタイルJSON
iOS / React: ArcGISMapDesign
React: TomTomDesign
React はさらに StandardLight / Dark · DrivingLight / Dark · MonoLight / Dark
React
React
React
React
プラットフォームで差があるものだけ注記しています。注記のない行は、型名もプリセットの定数名も 3 プラットフォームで同じです。
03 · 切り替える
地図ビューの状態オブジェクトの mapDesignType に代入します。状態は監視されているので、代入した時点で地図の見た目が変わります。
// state は MapViewStateInterface<*>。プロバイダごとの型に絞って代入する
when (state) {
is GoogleMapViewStateInterface ->
state.mapDesignType = GoogleMapDesign.Satellite
is MapLibreViewStateInterface ->
state.mapDesignType = MapLibreDesign.OsmBrightJa
is MapTilerViewStateInterface ->
state.mapDesignType = MapTilerDesign.Toner
}// 初期デザインは State の生成時に渡す
@StateObject private var mapLibreState = MapLibreViewState(
mapDesignType: MapLibreDesign.DemoTiles,
cameraPosition: viewModel.initCameraPosition
)
// 切り替えは @Published なプロパティへの代入だけ
mapLibreState.mapDesignType = MapLibreDesign.OsmBrightJaconst [mapViewState, setMapViewState] =
useState<MapViewStateInterface<MapDesignTypeInterface<unknown>> | null>(null);
const handleDesignChange = (designId: string) => {
const option = mapDesignOptions.find(item => item.design.id === designId);
if (!mapViewState || !option) return;
mapViewState.mapDesignType = option.design;
};04 · カスタムデザインを定義する
プリセットの型はそのままコンストラクタとして公開されています。id と、スタイルJSONのURL(ベクタ系)またはタイルURLテンプレート(ラスタ系)を渡せば、自社スタイルや地理院タイルをプリセットと同じ扱いで使えます。
// MapLibreDesign は data class。スタイル JSON の URL を渡せば独自デザインになる
val GsiPale =
MapLibreDesign(
id = "gsi-pale",
styleJsonURL = "https://example.com/styles/gsi-pale/style.json",
attributionRules =
listOf(
AttributionRule(attribution = "地理院タイル"),
AttributionRule(attribution = "GEBCO", minZoom = 5, maxZoom = 8),
),
)private val mapLibreDesigns =
listOf(
MapDesignOption(label = "OsmBright", design = MapLibreDesign.OsmBright),
MapDesignOption(label = "GSI Pale", design = GsiPale),
)// ネイティブの mapType 定数をそのまま包み、出典表記だけ足す
val NormalWithNotice =
GoogleMapDesign.Custom(
id = MAP_TYPE_NORMAL,
attributionRules = listOf(AttributionRule(attribution = "自社データ 2026")),
)Google Maps のように地図の種類がネイティブ定数で決まるプロバイダでは GoogleMapDesign.Custom を使い、id に定数を渡して出典表記だけを差し替えます。MapTiler・Mapbox・HERE なども同じ形のコンストラクタを持ちます。
// public init(id:styleJsonURL:attributionRules:) がそのまま独自デザインの入口
let gsiPale = MapLibreDesign(
id: "gsi-pale",
styleJsonURL: "https://example.com/styles/gsi-pale/style.json",
attributionRules: [
AttributionRule(attribution: "地理院タイル"),
AttributionRule(attribution: "GEBCO", minZoom: 5, maxZoom: 8),
]
)private let mapLibreDesigns = [
MapDesignOption(label: "DemoTiles", design: MapLibreDesign.DemoTiles),
MapDesignOption(label: "GSI Pale", design: gsiPale),
]
// 適用時はプロバイダの型にキャストする
if let design = option.design as? MapLibreDesign {
mapLibreState.mapDesignType = design
}構造体なので let で定義してどこでも共有できます。ViewModel の MapDesignOption は design: Any を持つため、適用時に as? でプロバイダの型へ絞り込みます。
const GSI_STANDARD_DESIGN = new LeafletDesign({
id: 'gsi-standard',
tileUrl: 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
tileOptions: { minZoom: 5, maxZoom: 18, tileSize: 256 },
attributionRules: GSI_STANDARD_ATTRIBUTION_RULES,
});
export const LEAFLET_DESIGNS: MapDesignOption[] = [
{ label: 'OpenStreetMap', design: LeafletDesign.OpenStreetMap },
{ label: 'GSI Standard', design: GSI_STANDARD_DESIGN },
{ label: 'None', design: LeafletDesign.None },
];// new MapLibreDesign(id, styleJsonURL, attributionRules?)
const GSI_PALE = new MapLibreDesign(
'gsi-pale',
'https://example.com/styles/gsi-pale/style.json',
[{ attribution: GSI_ATTRIBUTION }],
);
export const MAPLIBRE_DESIGNS: MapDesignOption[] = [
{ label: 'OsmBrightJa', design: MapLibreDesign.OsmBrightJa },
{ label: 'GSI Pale', design: GSI_PALE },
];Leaflet はラスタタイル、MapLibre はスタイルJSONで定義します。デザインのメタデータは各プロバイダの重い実行時SDKと同じパッケージに入っているため、サンプルではプロバイダごとに動的 import してチャンクを分離しています。
05 · 出典表記(attributionRules)
独自タイルを使うときは、タイル提供元の表記条件をルールとして持たせます。カメラのズームと表示範囲から必要な表記だけが自動で選ばれます。
06 · サンプルアプリ
3プラットフォームともデザイン切替UIが入っています。UIの作りはプラットフォームごとに素直な実装で、代入する処理だけが共通です。
val mapViewState = rememberMapLibreMapViewState(
mapDesign = MapLibreDesign.DemoTiles,
cameraPosition = MapCameraPosition(
position = GeoPoint.fromLatLong(21.382314, -157.933097),
zoom = 12.0,
),
)
val designs = listOf(
"Demo" to MapLibreDesign.DemoTiles,
"OSM Bright" to MapLibreDesign.OsmBrightJa,
"Toner" to MapLibreDesign.MapTilerTonerJa,
)
MapLibreMapView(state = mapViewState, modifier = Modifier.weight(1f))
Row {
designs.forEach { (label, design) ->
// 代入するだけ。地図は作り直されないので、カメラはそのまま
Button(onClick = { mapViewState.mapDesignType = design }) { Text(label) }
}
}