ツールド東北リアルタイムマップを支える技術【フロントエンド編】🚴♀️
この記事は Mapbox Advent Calendar 2022 の記事です。
筆者はMapboxの広告配信プラットフォームを作っているインフラ&データエンジニアです。普段はCDKでAWS上でインフラを構築したり、EKS上にデータ提供APIを作ったりしています。
概要
この記事では、ツール・ド・東北 2022という自転車イベントのために、弊社エンジニアが有志で作成したリアルタイムマップのフロントエンドの技術について解説します。
なお、本記事の内容はmapbox/OpenStreetMap Meetup 2022のオンラインイベントでも発表しております。
- 動画: https://youtube.com/clip/UgkxJ8pUl8maUlDxkoO6I_KhMFadlY-mFXhk
- 発表資料: https://speakerdeck.com/mapbox_jp/221028-mbj-meetup
また、バックエンド編の記事もございますので、合わせてご覧いただけると幸いです。
リアルタイムマップとは
自転車イベントに参加しているライダーの現在位置をリアルタイムに表示する地図のことをリアルタイムマップと呼んでいます。なお、大会終了後は、指定した特定の時間の位置情報を表示するリプレイモードにて公開しております。
解説
解説をお読みいただく前に、こちらのリアルタイムマップを少し触っていただき、プロダクトの全体像を把握していただけると幸いです。
クラスタリング
左の画面は最初に開発したプロトタイプの画面です。大会参加ライダー数の1500人を同時にマップ上に表示してみました。ご覧の通り、レースの状況が把握しにくい地図になってしまいました。
プロトタイプ | クラスタリング後 |
---|---|
そこで、ズームレベルが小さい時はライダーをクラスタリングし、どのあたりに何名ライダーがいるか一眼でわかるようにしました。
addSource
のオプションにcluster: true
をつけることで,クラスタ化を行ってくれます。詳しくはこちらのドキュメント(クラスターの作成と設定)をご覧下さい。
map.addSource('riders', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: Array.from(riders.values()),
},
cluster: true,
clusterMaxZoom: 14, // Max zoom to cluster points on
clusterRadius: 128, // Radius of each cluster when clustering points
clusterProperties: {
maxdis: ['max', ['get', 'dis']],
},
});
ライダー検索&追従
リアルタイムマップ上でライダーを検索可能です。検索ウィンドウはMapの外のコンポーネントに実装しています。検索ウィンドウで選択されたライダーの情報をMapコンポーネントに渡します。Mapコンポーネントはライダーの緯度経度をflyTo
に渡し、そのライダーが地図の中心になるようにしています。flyTo
についてはこちらのドキュメント(場所にゆっくり飛ぶ)をご覧下さい。
if (selectdGeoJson) {
map.flyTo({
center: selectdGeoJson.geometry.coordinates,
});
}
ライダーの位置情報更新
データ更新
ライダーの位置情報の更新は、setData
で行っています。
setData
についてはこちらのドキュメント(リアルタイムデータを追加)をご覧下さい。
const updateRiders = (map: mapboxgl.Map) => {
const ridersSource = map.getSource('riders') as mapboxgl.GeoJSONSource;
ridersSource.setData({
type: 'FeatureCollection',
features: Array.from(riders.values()),
});
};
更新タイミング制御
タイミングの制御はバックエンドに任せています。バックエンドとはgRPC-webで通信しており、ライダーの情報はバックエンドからフロントエンドにPushされます。フロントエンドからバックエンドにリクエストを送る際に、Pushのインターバルや倍速のパラメータを指定しています。
バックエンドに更新タイミングを任せることで、フロントエンドの実装はとてもシンプルになりました。フロントエンドのMapコンポーネントはある断面のデータの描画のみを行うのみです。リアルタイム時、リプレイ時、リプレイ時のx倍速再生時も共通の描画のロジックを利用しています。
リアルタイム時 | リプレイ時 |
---|---|
最後に
実は、このリアルタイムマップは、どうにかこうにか、ギリギリで大会に間に合わせることができたという状況でした(当日の朝までバグ修正していましたw)。個人的には、とても辛い時期でしたが、おかげさまでMapboxGLJSやReact、gRPC-webなどのフロントエンドのスキルを身につけることができました。来年は、協力してくれるメンバーを募集して、以下のような改善を行いながら、より良いリアルタイムマップを作り上げたいと考えています。
- より臨場感が湧くようなライダーの表示
- 3D表示
- ライダー目線での表示
- 検索したライダーのスムーズなカメラ追従
- iOS、Androidのアプリ作成
- リファクタリング
- UI Framework:MUIの利用
- React用のmapbox GL JSのライブラリ:visgl/react-map-glの利用
最後までお読み頂きありがとうございました。
Discussion