【MapLibre】レイヤの表示/非表示を切り替える

## はじめに
:::note info
この記事は [MapLibre Advent Calendar 2023](https://qiita.com/advent-calendar/2023/maplibre) 9日目の記事です。
:::

FOSS4G 2023 JapanでMapLibre GL JS初級編のハンズオンを実施しました。

その後、受講生が自分でWebGISを作っていて、「レイヤの表示/非表示を切り替える方法を教えて欲しい」と質問があり、個別に回答しました。
インターネットにはあまり載っていなかったので、記事にします。

## レイヤの表示/非表示の切り替え

### サンプルのスタイル
以下はサンプルのスタイルです。
2つのレイヤが定義されており片方のレイヤが表示状態になっています。


```main.js
const map = new maplibregl.Map({
  container: 'map',
  center: [139.767125, 35.681236], // 中心座標
  zoom: 15, // ズームレベル
  style: {
    // スタイル仕様のバージョン番号。8を指定する
    version: 8,
    // データソース
    sources: {
      // 地理院地図淡色地図
      pale: {
        // ソースの種類。vector、raster、raster-dem、geojson、image、video のいずれか
        type: 'raster',
        // タイルソースのURL 今回は地理院の淡色地図タイル
        tiles: ['https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png'],
        // タイルの解像度。単位はピクセル、デフォルトは512
        tileSize: 256,
        // データの帰属
        attribution: "地図の出典：<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>国土地理院</a>",
      },
      // 地理院地図航空写真
      photo: {
        type: 'raster',
        tiles: ['https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg'],
        tileSize: 256,
        attribution: "地図の出典：<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>国土地理院</a>",
      },
    },
    // 表示するレイヤ
    layers: [
      {
        // 一意のレイヤID
        id: 'pale-layer',
        // レイヤの種類。background、fill、line、symbol、raster、circle、fill-extrusion、heatmap、hillshade のいずれか
        type: 'raster',
        // データソースの指定
        source: 'pale',
      },
      {
        id: 'photo-layer',
        type: 'raster',
        source: 'photo',
        layout: {
          visibility: 'none',
        },
      },
    ],
  },
});
```

ここからは、表示/非表示切り替えでキーとなるプロパティ、関数を紹介します。


### visibility

レイアウト用のプロパティです。
"visible"、"none "のいずれかを指定します。デフォルトは "visible"です。

visibleの場合は、レイヤを表示します。
noneの場合は、レイヤは表示されません。

https://maplibre.org/maplibre-style-spec/layers/


ここでは、`photo-layer`の`visibility`が`none`になっているため、地理院地図航空写真は表示されません。
`pale-layer`の`visibility`は設定されていませんが、デフォルトでは`visible`のため、地理院地図淡色地図が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667439/c6e17689-6c8a-e0b3-9d59-3a83ecf8b887.png)

### setLayoutProperty

指定したレイヤのレイアウトプロパティの値を設定します。

https://maplibre.org/maplibre-gl-js/docs/API/classes/maplibregl.Map/#setlayoutproperty

### 非表示レイヤを表示する
レイヤIDを指定し、`visibility`を`visible`とすることで、非表示となっているレイヤを表示することができます。
ここでは、非表示になっていた航空写真のレイヤが表示されるようになります。

```js
map.setLayoutProperty('photo-layer', 'visibility', 'visible');
```

### 表示されているレイヤを非表示にする
レイヤIDを指定し、`visibility`を`none`とすることで、表示されているレイヤを非表示にすることができます。
ここでは、表示されていた淡色地図のレイヤが非表示になります。

```js
map.setLayoutProperty('pale-layer', 'visibility', 'none');
```


### サンプルコード
ドロップダウンリストを使って背景地図を切り替えます。

```index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>表示/非表示切り替え</title>
    <meta name="description" content="sample" />
    <link href="style.css" rel="stylesheet" />
    <!-- MapLibre GL JS -->
    <link rel="stylesheet" href="https://unpkg.com/maplibre-gl/dist/maplibre-gl.css" />
    <script src="https://unpkg.com/maplibre-gl/dist/maplibre-gl.js"></script>
  </head>
  <body>
    <div id="map"></div>
    <div id="layerControl">
      <span>背景地図: </span>
      <select name="layer-control" onChange="changeMap(this.value)">
        <option value="pale-layer">淡色地図</option>
        <option value="photo-layer">写真</option>
      </select>
    </div>
    <script src="main.js"></script>
  </body>
</html>
```

```style.css
body {
  margin: 0;
  padding: 0;
}
html,
body,
#map {
  height: 100%;
}
#layerControl {
  position: absolute;
  top: 20px;
  left: 20px;
  padding: 20px;
  margin-bottom: 20px;
  width: 15%;
  background-color: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
  border-radius: 3px;
}
```

```main.js
const map = new maplibregl.Map({
  container: 'map',
  center: [139.767125, 35.681236], // 中心座標
  zoom: 15, // ズームレベル
  style: {
    // スタイル仕様のバージョン番号。8を指定する
    version: 8,
    // データソース
    sources: {
      // 地理院地図淡色地図
      pale: {
        // ソースの種類。vector、raster、raster-dem、geojson、image、video のいずれか
        type: 'raster',
        // タイルソースのURL 今回は地理院の淡色地図タイル
        tiles: ['https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png'],
        // タイルの解像度。単位はピクセル、デフォルトは512
        tileSize: 256,
        // データの帰属
        attribution: "地図の出典：<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>国土地理院</a>",
      },
      // 地理院地図航空写真
      photo: {
        type: 'raster',
        tiles: ['https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg'],
        tileSize: 256,
        attribution: "地図の出典：<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>国土地理院</a>",
      },
    },
    // 表示するレイヤ
    layers: [
      {
        // 一意のレイヤID
        id: 'pale-layer',
        // レイヤの種類。background、fill、line、symbol、raster、circle、fill-extrusion、heatmap、hillshade のいずれか
        type: 'raster',
        // データソースの指定
        source: 'pale',
      },
      {
        id: 'photo-layer',
        type: 'raster',
        source: 'photo',
        layout: {
          visibility: 'none',
        },
      },
    ],
  },
});

function changeMap(selectedMap) {
  let unSelectedMap = null;
  if (selectedMap === 'photo-layer') {
    unSelectedMap = 'pale-layer';
  } else {
    unSelectedMap = 'photo-layer';
  }
  map.setLayoutProperty(unSelectedMap, 'visibility', 'none');
  map.setLayoutProperty(selectedMap, 'visibility', 'visible');
}
```

ドロップダウンリストで選択されたレイヤを表示、選択されなかったレイヤを非表示にすることで、背景地図の切り替えを実現しています。

![Animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667439/4b45c1af-1c27-f74c-4d91-685c6b0fe8f9.gif)

今回紹介したサンプルコードはGitHubで公開しています。

https://github.com/asahina820/MapLibre-Layer-Control-Sample

## おわりに
他にもmaplibre-gl-opacityというプラグインを使う方法もあります。

https://www.memo.dayjournal.dev/memo/try-057/

FOSS4G TOKAI 2023, FOSS4G 2023 JapanでMapLibre GL JSのハンズオン講師を担当しました。
テキスト、ソースコードなどは[こちら](https://asahina-lab.vercel.app/posts/20230917_foss4g_japan_handson)に載っているのでぜひご覧ください。

また、Zennで無料で読めるMapLibre初級編の本を公開しています。
よければ[こちら](https://zenn.dev/asahina820/books/c29592e397a35b)もご覧くださいませ！