GroundImage ― 画像を地図に貼る
南西・北東の2点で範囲を決めて、そこに1枚の画像を敷きます。施設のフロア図、古地図、航空写真、独自に作った塗り分け図など、タイル化されていない画像をそのまま地図に重ねられます。範囲や透明度は後から書き換えられ、画像はカメラ操作に追随して地面に貼り付いたまま表示されます。
01 · GroundImage の状態
GroundImageState が1枚の画像を表します。範囲・画像・透明度と、タップハンドラを持ちます。プラットフォームごとに自然な形で画像を渡せます(Android は Drawable、iOS は UIImage、Web は URL)。
val groundImageState =
GroundImageState(
id = "groundImage",
bounds =
GeoRectBounds(
southWest = southWestPosition,
northEast = northEastPosition,
),
image = drawable, // Drawable をそのまま渡す
opacity = 0.5f,
onClick = this::onGroundImageClick,
)
GroundImage(groundImageState)let groundImageState = GroundImageState(
bounds: GeoRectBounds(southWest: southWest, northEast: northEast),
image: resources.image, // UIImage をそのまま渡す
opacity: 0.5,
id: "groundImage"
)
content.groundImages = [GroundImage(state: groundImageState)]const image = createGroundImageState({
id: 'ground-image',
bounds: createGeoRectBounds({ southWest, northEast }),
imageUrl: GROUND_IMAGE_URL, // URL で渡す
opacity,
onClick: () => showToast('Ground image clicked.'),
});
<GroundImage state={image} />02 · 範囲と透明度
範囲は矩形です。画像の縦横比と範囲の縦横比が違う場合は、範囲に合わせて引き伸ばされるため、貼りたい図面の縦横比に合わせた範囲を指定してください。範囲・透明度・画像はすべて後から書き換えられ、書き換えた時点で描画に反映されます。
ドラッグで範囲を作り直す
ベース地図と重ねる

// 四隅のマーカーをドラッグして範囲を作り直す
override fun onMarkerDrag(dragged: MarkerState) {
when (dragged.id) {
"south_west" -> southWestPosition = GeoPoint.from(dragged.position)
"north_east" -> northEastPosition = GeoPoint.from(dragged.position)
}
bounds = GeoRectBounds(southWest = southWestPosition, northEast = northEastPosition)
}
// 透明度はそのまま代入するだけ
groundImageState.opacity = sliderOpacityfunc setOpacity(_ value: Double) {
opacity = value
groundImageState.opacity = value
}
// マーカーのドラッグに合わせて範囲を作り直す
groundImageState.bounds = GeoRectBounds(southWest: sw, northEast: ne)// ドラッグ中は状態オブジェクトを直接書き換える(再レンダリングを待たない)
const applyGeometry = (sw: GeoPoint, ne: GeoPoint) => {
image.bounds = createGeoRectBounds({ southWest: sw, northEast: ne });
polylineState.points = framePoints(sw, ne);
};
image.opacity = opacity;03 · どのプロバイダでも同じに見える
GroundImage は、各地図SDKドライバーの中でもっとも実装差が大きい機能です。画像オーバーレイの仕組みはエンジンごとに大きく異なり、同等の機能を持たないものもあります。MapConductor は可能な範囲でこの差を吸収しますが、プロバイダによっては動的な変更に制限が残ります。
同じ範囲・同じ引き伸ばし
画像は指定した範囲に合わせて配置されます。エンジン側の画像オーバーレイの座標系の違いに関係なく、同じ位置・同じ大きさで表示されます。
まずネイティブの機能で描く
各ドライバーは、可能な限りその地図SDK固有の画像オーバーレイ機能を使って最適に描画します(ArcGIS なら MediaLayer + ImageElement、Cesium なら Rectangle の画像マテリアルなど)。どうしても同等の機能がないエンジンに限り、画像を切り出してタイルとして供給し、ラスターレイヤーとして重ねます。どちらの経路でもアプリ側のコードは変わりません。
プロバイダによる制限
範囲の作り直し(ドラッグ中の連続更新)や透明度の変更が、エンジン側の制約で限定的になる場合があります。静的な配置はどのプロバイダでも同じですが、動的に変える用途では対象プロバイダでの確認をおすすめします。
タップ判定も共通
タップ判定は共通ロジックで行うため、エンジンが画像オーバーレイのタップに対応しているかどうかに関係なく onClick が呼ばれます。
04 · タップ
onClick は画像の状態と、タップされた座標を受け取ります。サンプルでは、タップで画像そのものを差し替えて表示を切り替えています。
// タップで画像を差し替える
override fun onGroundImageClick(clicked: GroundImageEvent) {
val isBase = clicked.state.image == imageResources.image
image = if (isBase) imageResources.clickedImage else imageResources.image
}private func onGroundImageClick(_ event: GroundImageEvent) {
let isBase = groundImageState.image === resources.image
groundImageState.image = isBase ? resources.clickedImage : resources.image
}createGroundImageState({
bounds,
imageUrl,
onClick: event => showToast(event.clicked),
});画像の透明な部分もタップ範囲に含まれます。透明部分を無反応にしたい場合は、範囲を画像の実体に合わせるか、ポリゴンを併用してください。