ポリライン
座標の並びと線のスタイルを持つ状態オブジェクトを1つ作って、地図ビューに置くだけです。線の見え方・曲がり方・タップ判定はプロバイダごとにバラバラになりがちですが、MapConductor はどのプロバイダでも同じ結果になるように揃えています。
01 · ポリラインの状態
PolylineState が1本の線を表します。座標列とスタイル、そしてタップハンドラを持ちます。プロパティを書き換えれば線はその場で更新され、ルートの形が変わるアプリでも作り直しは不要です。
val polylineState =
PolylineState(
points = routePoints,
strokeColor = Color.Red,
strokeWidth = 4.dp,
geodesic = true,
onClick = { event -> viewModel.onPolylineClick(event) },
)
// 地図の中に置くだけ
Polyline(polylineState)
// 形の変更は points を書き換えるだけ
polylineState.points = nextPointslet polylineState = PolylineState(
points: routePoints,
strokeColor: .red,
strokeWidth: 4.0,
geodesic: true,
onClick: { event in viewModel.onPolylineClick(event) }
)
content.polylines = [Polyline(state: polylineState)]const polylineState = createPolylineState({
id: 'demo-polyline',
points,
strokeColor: '#e74c3c',
strokeWidth,
geodesic: true,
onClick: event => showToast(event.clicked),
});
<Polyline state={polylineState} />02 · 直線と大圏コース
geodesic を true にすると、2点を地球上の最短経路(大圏コース)で結びます。長距離のフライト経路などは直線ではなく弧を描きます。false のときは地図投影上の直線です。同じ座標列で両方を並べて描き比べられます。
Polyline(polylineState) // geodesic = true(曲線)
Polyline(
polylineState.copy(
id = "${polylineState.id}-straight",
geodesic = false,
strokeColor = Color.Blue,
),
)content.polylines = [
Polyline(state: polylineState), // 曲線
Polyline(state: polylineState.copy(geodesic: false)) // 直線
]const straightPolyline = polyline.copy({
id: `${polyline.id}-straight`,
geodesic: false,
strokeColor: '#0000ff',
});
<Polyline state={polyline} />
<Polyline state={straightPolyline} />03 · どのプロバイダでも同じに見える
地図エンジンには、大圏コースを描けるものと描けないもの、線の太さの単位が違うもの、日付変更線をまたぐ線の扱いが違うものが混在しています。素のSDKを使うと、同じ座標列でも見た目が変わってしまいます。
MapConductor はこの差を内部で吸収します。アプリ側は PolylineState を1つ渡すだけで、どのプロバイダでも同じ弧・同じ太さ・同じタップ範囲になります。
曲線を描けないエンジンにも同じ弧を
大圏コースを直接描けないエンジンでは、線を細かい区間に分けた座標列に変換してから渡します。結果として、どのエンジンでも同じ弧が表示されます。
日付変更線をまたぐ線
経度180度をまたぐ線は、そのまま渡すと地球を逆向きに横断してしまうエンジンがあります。境界で線を分割して渡すことで、どのエンジンでも最短側を通ります。
太さの基準を揃える
線の太さはプラットフォームの論理単位で指定します。端末の画素密度やエンジンごとの単位差を内部で換算するため、実機での見た目の太さが一致します。
タップ判定も共通
タップ判定はエンジンに任せず、共通のロジックで行います。指の大きさぶんの許容範囲をズームに応じたメートルに換算して判定するため、拡大率が違っても押しやすさが変わりません。曲線の場合は弧に沿って判定します。
04 · タップ
onClick は線の状態と、線上でタップされた位置を受け取ります。タップ位置は線に最も近い点に補正された座標なので、その場所にマーカーを置いたり距離を計算したりできます。
// タップ位置にマーカーを落とす
fun onPolylineClick(event: PolylineEvent) {
markers = markers + MarkerState(
position = event.clicked,
animation = MarkerAnimation.Drop,
icon = DefaultMarkerIcon(fillColor = event.state.strokeColor),
)
}func onPolylineClicked(_ event: PolylineEvent) {
markerState = MarkerState(
position: GeoPoint.from(position: event.clicked),
icon: DefaultMarkerIcon(fillColor: event.state.strokeColor)
)
}onClick: event => {
setClickMarkers(prev => [...prev, createMarkerState({
id: `polyline-click-${prev.length}`,
position: event.clicked,
animation: MarkerAnimation.Drop,
icon: new ColorDefaultIcon({ fillColor: event.state.strokeColor }),
})]);
},表示範囲外の線分は判定から除外されるため、長いルートを何本も置いても反応が鈍くなりません。複数の線が重なっている場合は、最も近い線が選ばれます。