ネイティブ拡張
MapConductor は各地図 SDK の全機能をラップしません。統一 API に無い表現が必要になったとき、抽象化を捨てずに下へ降りるための道が 2 つ用意されています。どちらを使っても、その画面の他の部分は共通 API のまま動きます。
01 · 抽象化の終わり
統一 API が扱うのは、どのプロバイダにも共通してある概念だけです。3D 建物の押し出し、特定ベンダーの交通量レイヤー、独自のシェーダーといったものは、意図的に対象外にしています。全部を包もうとすると、共通 API が最小公倍数になって使いにくくなるためです。
02 · ネイティブの地図に触る
状態オブジェクトから getMapViewHolder() を呼ぶと、そのプロバイダの地図ビューと地図インスタンスを持つホルダーが返ります。型を絞り込めば、あとは素の SDK です。座標と画面ピクセルの相互変換も、このホルダーが持っています。
// 型を絞り込めば、そこから先は素の Google Maps SDK
val holder = mapViewState.getMapViewHolder()
if (holder is GoogleMapViewHolder) {
holder.map.isTrafficEnabled = true
}// AnyMapViewHolder から具象ホルダーへ絞り込む
if let holder = mapState.getMapViewHolder() as? MapKitViewHolder {
holder.mapView.showsTraffic = true
}// 共通インターフェースの map は unknown。プロバイダの型へ絞り込んでから使う
const holder = mapViewState.getMapViewHolder();
const map = holder?.map as maplibregl.Map | undefined;
map?.addLayer({ id: 'buildings', type: 'fill-extrusion', source: 'composite' });ホルダーは地図の初期化が終わってから使えます。未初期化のあいだは null が返るため、onMapLoaded の中か、その後の操作で呼んでください。
03 · モジュールを差し込む
追加モジュールが、Core を改造せずに自分の機能を差し込むための口が MapServiceRegistry です。型付きのキーで登録し、必要な側が同じキーで取り出します。キーが型を持つため、取り出した値のキャストは要りません。現在これを使っているのはマーカークラスタリングで、プロバイダが登録した MarkerRenderingSupport を引き当てて描画します。
MapServiceRegistry
MapServiceKey<T> をシングルトンとして定義し、MutableMapServiceRegistry に put、利用側は get で受け取ります。Android では CompositionLocal(LocalMapServiceRegistry)から地図スコープの登録簿を参照します。
MapServiceRegistryScope
キーは MapServiceKey に準拠した型として定義し、MutableMapServiceRegistry に put します。登録簿は状態オブジェクト(MapViewState.serviceRegistry)が持ち、プロバイダは content を組み立てるあいだだけ MapServiceRegistryScope.with(...) でそれを可視にします。SwiftUI の Environment ではないのは、地図の中身が MapViewContent という値であってビュー階層ではないためです。
// Kotlin: キーはシングルトンの object として定義する
object MarkerRenderingSupportKey : MapServiceKey<MarkerRenderingSupport<*>>
registry.put(MarkerRenderingSupportKey, support)
val support = registry.get(MarkerRenderingSupportKey) // 型が付いたまま返る
// Swift: キーは MapServiceKey に準拠した型として定義する
enum MarkerRenderingSupportKey: MapServiceKey {
typealias Value = any MarkerRenderingSupport
}
registry.put(MarkerRenderingSupportKey.self, support)
let support = registry.get(MarkerRenderingSupportKey.self)
// TypeScript: createMapServiceKey<T>() でキーを作る
const MarkerRenderingSupportKey = createMapServiceKey<MarkerRenderingSupport>();ここで挙げた 3 つは、どれもプロバイダ固有のコードを画面に持ち込みます。プロバイダを差し替えたときに動かなくなる部分になるので、使った箇所は分岐の内側にまとめておくことをおすすめします。