はじめに 駅メモ!チームでエンジニアをしている 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, ] } } ) このようにして,地図上に描画した多数のデータから,目的の場所を探しやすくする機能を実現することができました 参考 Sources | Style Specification | Mapbox GL JS | Mapbox Create and style clusters | Mapbox GL JS | Mapbox