はじめに
駅メモ!チームでエンジニアをしている id:wgg00sh です.
駅メモ!では2021年11月に「未取得の駅を地図で確認できる機能」をリリースしました. 今回はこの機能を実現するにあたって発生した問題の一例と,その問題をどのように解決したかについて,使用した地図ライブラリである Mapbox GL JS の使い方と合わせて紹介していきます
地図上に全ての駅を表示する
駅メモ!には,9300を超える駅が使用されているので,まずはその全てを表示してみます
↓のような駅情報を持っていて,
export const stationList = [ [1,'函館',140.xxxxxx,41.yyyyyy], [2,'五稜郭',140.xxxxxx,41.yyyyyy], ... ]
このデータを使って地図上に全ての駅を表示してみます
const stationGeoJSON = convertGeoJSON (stationList) // 座標データを GeoJSON に変換する処理 map.addSource('station', { type: 'geojson', data: stationGeoJSON, }) map.addLayer({ id: 'station', type: 'circle', source: 'station', paint: { 'circle-color': '#FF0000', 'circle-radius': 6, } }) // クリックイベントの追加 map.on('click', 'station', (e) => { new mapboxgl.Popup({anchor: 'bottom',}) .setLngLat(e.lngLat) .setHTML(e.features[0].properties.name) .addTo(map); })
遠くから見た場合 | 近くで見た場合 | クリック |
---|---|---|
問題点
クリックして駅の詳しい情報などを見ることを考えると,遠くから見た場合は駅数が多すぎてまともに操作できないです.
また,これだけの量を描画するのは,パフォーマンスの観点から見てもよく無さそうです
解決法
そこで,複数の駅が密集している場合は,それらを纏めて別の表示にするクラスタ化を行います
Mapbox GL JS では, addSource
のオプションに cluster: true
をつけることで,クラスタ化を行ってくれます
クラスタ化に関するオプションとして, clusterRadius
clusterMinPoints
clusterMaxZoom
がありますが,これらは実際に使用するデータによって良い感じに見えるパラメータを模索するのが良いと思います
// 駅データの投入 map.addSource('station', { type: 'geojson', data: stationGeoJSON, cluster: true, // クラスタ化を行うオプション clusterRadius: 120, // クラスタ化を行う半径 clusterMinPoints: 30, // クラスタ化に必要な最小の要素数 }) // クラスタ化されていない座標データの描画 map.addLayer({ id: 'station', type: 'circle', source: 'station', filter: ['!', ['has', 'point_count']], paint: { 'circle-color': '#FF0000', 'circle-radius': 6, } }) // クラスタ化されたデータの描画 (円部分) map.addLayer({ id: 'station_cluster', type: 'circle', source: 'station', filter: ['has', 'point_count'], // クラスタ化されている要素かのチェック方法 paint: { 'circle-color': [ 'step', ['get', 'point_count'], // クラスタに含まれる要素数に応じて色を変更 '#FF4000', 30, '#FF8000', 50, '#FFB000', 100, '#FFFF00', 300, '#FFFFFF', ], 'circle-radius': [ 'step', ['get', 'point_count'], // クラスタに含まれる要素数に応じて円の大きさを変更 24, 30, 36, 50, 48, 100, 60, 300, 72, ], } }) // クラスタ化されたデータの描画 (テキスト部分) map.addLayer({ id: 'station_cluster_label', type: 'symbol', source: 'station', filter: ['has', 'point_count'], layout: { 'text-field': '{point_count}', 'text-size': [ 'step', ['get', 'point_count'], 14, 30, 18, 50, 24, 100, 30, 300, 36, ] } })
このようにして,地図上に描画した多数のデータから,目的の場所を探しやすくする機能を実現することができました