3D地図上に電車を走らせると、次にやりたくなるのが「その電車に乗っているような視点」や「ドローンで追いかけるような視点」です。
MapLibre GL JSのカメラ機能を駆使して、移動するオブジェクトを滑らかに追尾する「おいかけモード」を実装したので、その技術的なポイントをまとめます。

easeToによる滑らかな追尾

地図の中心を移動させるメソッドには jumpToflyTo がありますが、リアルタイムに移動する電車を追いかける場合、単純に毎フレームこれらを呼ぶと画面がガタついてしまいます。

そこで採用したのが、easeTo メソッドを短いデュレーション(持続時間)で呼び出し続ける手法です。これにより、計算上の位置と描画上の位置の間で微細な補間がかかり、ヌルッとした滑らかな追従が可能になります。

// アニメーションループ内で実行
map.easeTo({
    center: [lng, lat],
    bearing: camBearing, // カメラの向き
    pitch: 75,           // 見下ろす角度
    zoom: 17.5,
    animate: true,
    duration: 100        // 100msかけて補間移動
});

マルチアングルの計算ロジック

ただ後ろから追いかけるだけでは面白くないので、「運転士視点」や「撮り鉄視点」など、アングルを切り替えられるようにしました。
これは、電車の進行方向(Bearing)に対して、カメラのオフセット角度を加算することで実現しています。

// 進行方向の角度 (Degree)
const trainBearing = (Math.PI - posRot.rotation) * (180 / Math.PI);

// カメラのオフセット設定
const CAM_ANGLES = [
    { label: '運転士', offset: 0 },
    { label: '右斜め前', offset: 150 },
    { label: '左斜め前', offset: 210 }
];

// カメラの向きを決定
const camBearing = trainBearing + CAM_ANGLES[mode].offset;

特に「斜め前」からの視点は、カーブを曲がる際に車両の側面が見え隠れするため、非常にダイナミックな映像になります。Turf.jsで計算した精密な軌道データのおかげで、カメラがブレることなく綺麗に回り込んでくれます。

まとめ

カメラ制御は、ユーザーの没入感を高める最も重要な要素の一つです。今後は、マウスドラッグでカメラを自由に回せる機能や、自動的に見どころスポットを巡るオートパイロット機能なども検討していきたいと思います。