カスタムコントロールのセレクトボックスで選択した都道府県をハイライト表示する

都道府県のポリゴンデータを取得する

<div id="map"></div>

で用意した div タグ内に地図を表示するため、ID である map を指定し geolonia.Map のインスタンスを生成します。

地図が読み込まれたら、geolonia / prefecture-tiles で公開している prefectures.geojson を取得し、レスポンスの json データを geojson に格納します。

const map = new geolonia.Map('#map')
map.on('load', async () => {
  const resp = await fetch('https://raw.githubusercontent.com/geolonia/prefecture-tiles/master/prefectures.geojson')
  const geojson = await resp.json()
  //...

都道府県のレイヤーを地図に追加する

続いて、addLayer で取得した GeoJSON データを地図に追加します。

GeoJSON データは、全都道府県のポリゴンデータがまとめて入った FeatureCollection タイプのデータなのですが、個別に指定して表示/非表示が切り替えられるように、それぞれの都道府県(Featureタイプのデータ)に都道府県コード (すでに properties の一つにセットされている) を Feature IDとして扱う必要がある。こういった場合は、 source を指定する時に promoteId オプションを使います。 promoteId オプションは、GeoJSON の Feature の properties から指定した値を Feature ID として利用するようになります。

map.addLayer({
  id: 'prefectures',
  type: 'fill',
  source: {
    type: 'geojson',
    promoteId: 'code',
    data: geojson,
  },
  layout: {},
  paint: {
    'fill-color': '#ff0000',
    'fill-opacity': [
      'case', ['boolean', ['feature-state', 'active'], false], 1, 0
    ]
  }
})

paint プロパティでは、'fill-color': '#ff0000' で赤色に塗りつぶすように指定し、'fill-opacity' で透明度を指定しているのですが、その値には

[
  'case', ['boolean', ['feature-state', 'active'], false], 1, 0
]

という、MapLibre GL JS の StyleSpecification で定義されている Expressions のひとつである case 文を使っています。

この値は、各都道府県の feature-stateactive が true のときには1(不透明)が、false のときには0(透明)がセットされるということを意味しています。つまり、active を true にセットするとその都道府県がハイライトされます。デフォルトは false なので、最初はどの都道府県もハイライトされていない状態になります。

都道府県セレクトボックス(カスタムコントロール)を追加

後述する PrefectureSelectBox クラスに GeoJSON データを引数として渡して初期化したあと、カスタムコントロールとして地図に追加します。

const prefectureSelectBox = new PrefectureSelectBox(prefectures)
map.addControl(prefectureSelectBox)

カスタムコントロール用のクラスを定義

カスタムコントロール用のクラス PrefectureSelectBox を定義します。

class PrefectureSelectBox {
  constructor(prefectures) {
    this._prefectures = prefectures
    this._selectedPrefectureCode = undefined
  }
  //...

コンストラクタで、引数と渡ってくる prefectures をインスタンス変数の _prefectures にセットし、選択された都道府県コードを格納するための _selectedPrefectureCode も用意します。

カスタムコントロール用のクラスを作るときは、コントロールが追加されたときに呼ばれる onAdd 関数を定義しなければなりません。ここでは this._prefecture に格納された都道府県データを利用して、都道府県名を選択できるセレクトボックスを作っています。

onAdd(map) {
  this._map = map

  // 都道府県を選ぶセレクトボックスを作る
  this._container = document.createElement('div')
  this._container.className = 'maplibregl-ctrl'
  this._container.innerHTML = '<select name="prefecture">'
    + '<option value=""></option>'
    + this._prefectures.map(pref => { return `<option value="${pref.properties.code}">${pref.properties.name}</option>` })
    + '</select>';
  //...

都道府県のどれかが選択されたときの処理を、セレクトボックスの change イベントに割り当てます。

前回選択した都道府県がある場合には、this._selectedPrefectureCode に格納された都道府県コードを ID とする Feature の feature-stateactive の値を false に変えています。これによって前述した fill-opacity の値が 0 になって透明に、つまりハイライト表示が無効になります。

次に、セレクトボックスで選択された都道府県にひもづく都道府県コードを this._selectedPrefectureCode に格納し直し、同じ要領で feature-stateactive の値を true に変えることで、選ばれた都道府県のハイライト表示を有効にします。

// 都道府県が選択されたときに呼ばれる
this._container.addEventListener("change", (e) => {

  // 前回選択した都道府県があれば、ハイライト表示を無効にする
  if (this._selectedPrefectureCode) {
    this._map.setFeatureState(
      { source: 'prefectures', id: this._selectedPrefectureCode },
      { active: false }
    );
  }

  // 選択した都道府県のハイライト表示を有効にする
  this._selectedPrefectureCode = e.target.value
  this._map.setFeatureState(
    { source: 'prefectures', id: this._selectedPrefectureCode },
    { active: true }
  );

  // 選択された都道府県をフォーカスする
  const selectedPrefecture = this._prefectures.find(prefecture => prefecture.properties.code === this._selectedPrefectureCode)
  const prefectureGeojson = {
    type: 'FeatureCollection',
    features: [selectedPrefecture]
  }
  this._map.fitBounds(geojsonExtent(prefectureGeojson))
})

後半では mapbox / geojson-extent というライブラリを使い、選択された都道府県の GeoJSON データからバウンディングボックス情報(その都道府県を囲む最小の四角形のポリゴンの座標の集まり)を抽出し、fitBounds に渡すことで選択した都道府県にフォーカスするように移動できるようにしています。

onAdd の最後では、このようにして作成した this._container を返り値として返しています。

return this._container

カスタムコントロールが削除されるときに呼ばれる onRemove も定義します。

onRemove() {
  this._container.parentNode.removeChild(this.container)
  this._map = undefined
}

コード全体を見るには、以下の CodePen のサンプルコードを参照してください。

See the Pen カスタムコントロールのセレクトボックスで選択した都道府県をハイライト表示する by Geolonia (@geolonia) on CodePen.

CodePen でサンプルコードを編集