Mapbox GL JS v3と国土地理院 標高タイルで海面上昇シミュレータを作成してみました

# はじめに
:::note info
この記事は [Mapbox Advent Calendar 2023](https://qiita.com/advent-calendar/2023/mapbox) 4日目の記事です。
:::
- Mapbox GL JS v3で、ラスタータイルの色を変更して表示する機能（raster-color、raster-color-mix、raster-color-range）が実装されました。

https://github.com/mapbox/mapbox-gl-js/releases

https://docs.mapbox.com/style-spec/reference/layers/#paint-raster-raster-color

- 今回は、Mapbox GL JS v3のラスタータイルの色を変更できる機能と国土地理院 標高タイル（Terrain-RGB形式）を用いて、海面上昇シミュレータを作成してみました。

:::note info
Mapbox GL JSで利用する標高タイルのエンコーディング（標高換算式）は[Terrain-RGB形式（もしくはTerrarium-RGB形式）](https://docs.mapbox.com/style-spec/reference/sources/#raster-dem)である必要があります。一方で、国土地理院 標高タイルは、Terrain-RGB形式ではないエンコーディングを利用しているため、Terrain-RGB形式に変換する必要があります。Terrain-RGB形式と国土地理院 標高タイルの考え方の違いについては、下記の記事が詳しいです。
https://qiita.com/Kanahiro/items/e22594b738655a189c1d
:::

- Terrain-RGB形式に変換済みの国土地理院 標高タイルを下記のGitHubで公開していますのでこちらを使用しました。

https://github.com/shi-works/gsi-dem-10b-terrain-rgb

# アウトプットイメージ

https://x.com/shi__works/status/1728000842459230325?s=20

# デモサイト
https://shi-works.github.io/japan-sea-level-rise-map-on-mapbox-gl-js/#8.26/35.828/140.048

:::note warn
本シミュレータは地形データで一定の高さ以下の場所を青く塗りつぶす処理をするだけのものであり、ハザードマップとは異なります。 堤防等による防災対策や土砂の堆積等は考慮されていません。 実際の洪水や津波発生時の危険性は各自治体が公表するハザードマップでご確認ください。
:::

# 標高タイルのRGB値の標高換算式（=エンコーディング）
- 標高タイル（Terrain-RGB形式）のRGB値の標高換算式（=エンコーディング）は下記のとおりになります。
- RGBピクセル値を3桁の256進数として捉え、横軸が縦軸を標高とした傾き0.1の単調増加関数になります。
```Terrain-RGB形式:Terrain-RGB形式の標高換算式
標高=-10000+((R値×256×256+G値×256+B値)×0.1)
```
（引用元：[『現場のプロがわかりやすく教える 位置情報エンジニア養成講座』](https://www.shuwasystem.co.jp/book/9784798068923.html)）

# ソースコードと解説
- 全体のソースコードは、下記のGitHubから入手してください。
- MapboxのAPIキーはご自身で用意してください。

https://github.com/shi-works/japan-sea-level-rise-map-on-mapbox-gl-js

- 解説は下記のとおりになります。
- `raster-color`:raster-valueに基づく色を定義します（初期値として透明にしています）。
- `raster-value`:`raster-color`内でのみ利用できる設定で、`raster-color-mix`の設定により得られた値を取得できます。
- `raster-color-mix`:RGB値を一つの値（`raster-value`で取得できるもの）に変換するパラメータを指定します。`[mix.r,mix.g,mix.b,mix.a]`の4つのパラメータを指定します。4番目のパラメータである`mix.a`は透明度ではなく、固定値のオフセットになります。上述の標高タイルのRGB値の標高換算式を`raster-color-mix`で定義すると下記のとおりになります。uは標高分解能（0.1）です。
- `raster-color-range`:`raster-color-mix`で得られる値の範囲を指定します（スライダーの値を0～100mで設定しているので、0～128の範囲を指定しています）。なお、[Mapboxのドキュメント](https://docs.mapbox.com/style-spec/reference/layers/#paint-raster-raster-color)によれば、この範囲が256に等分割されて、`raster-color`で利用されるようです。
```javascript:javascript
      // 標高タイルラスターレイヤ
      map.addLayer({
        id: 'gsi-terrain-raster',
        source: 'gsi-terrain-raster',
        type: 'raster',
        paint: {
          // 初期状態で全ての標高に対して透明を指定
          'raster-color': [
            'interpolate',
            ['linear'],
            ['raster-value'],
            0, 'transparent',
            128, 'transparent'
          ],
          'raster-color-mix': [
            2 ** 16 * 255 * u,
            2 ** 8 * 255 * u,
            255 * u,
            -10000
          ],
          'raster-color-range': [0, 128],
          'raster-opacity': 0.7,
          'raster-resampling': 'nearest'
        }
      })
```
- また、`raster-color`は、下記でスライダー機能を用いて動的に変更できるようにしてあります（標高がスライダーの値以下のときに青に着色されます）。
```html:html
  <div class="map-overlay top">
    <div class="map-overlay-inner">
      <h2>海面上昇シミュレーション</h2>
      <label>海面高さ: <span id="elevation-slider-value">0m</span></label>
      <input id="elevation-slider" type="range" min="0" max="100" step="1" value="0">
      <input id="disclaimer-button" type="button" value="免責事項" onclick="showDisclaimer();">
    </div>
  </div>

```
```javascript:javascript
      // スライダーで標高に応じた色を制御
      document.getElementById('elevation-slider').addEventListener('input', function (e) {
        const elevation = parseFloat(e.target.value);
        document.getElementById('elevation-slider-value').innerText = elevation + 'm';

        // 標高タイルレイヤの色を更新
        map.setPaintProperty('gsi-terrain-raster', 'raster-color', [
          'step',
          ['raster-value'],
          '#000098', elevation,   // 標高がelevation以下の場合の色
          'transparent'           // 標高がelevationより高い場合の色
        ]);

      });
```

# 全球バージョン
- 海面上昇シミュレータの全球バージョンのソースコードとデモサイトになります。
- 標高タイルには、[AWS(Mapzen) Terrain Tiles](https://www.mapzen.com/blog/terrain-tile-service/)を使用しています。

https://github.com/shi-works/global-sea-level-rise-map-on-mapbox-gl-js

# 海外の事例
- ありがたいことに、海面上昇シミュレータは海外でも展開されています。


https://aws-terrain-flood.netlify.app/#6/45/-123 ([@dkwiens](https://x.com/dkwiens/status/1730754715921874988?s=20))


https://x.com/dkwiens/status/1730754717226352758?s=20


# 国内の事例
## JAXA
- 数値表層モデル（DSM：樹木や建物等の高さを含む地表面の高さ）を使った海面上昇のシミュレーション
- Web地図ライブラリー：OpenLayers

https://data.earth.jaxa.jp/app/sea-level-rise/

## 産業技術総合研究所
- ASTER GDEM Ver.2(30m空間分解能)と地理院タイル（10m空間分解能）に基づく海面上昇シミュレーション
- Web地図ライブラリー：Leaflet

https://gbank.gsj.jp/sealevel/sealevel.html

# 参考文献
https://qiita.com/mg_kudo/items/978709ad67bce885bfe5

# おわりに
Mapbox GL JS v3のラスタータイルの色を変更できる機能と国土地理院 標高タイル（Terrain-RGB形式）を用いて、海面上昇シミュレータを作成してみました。Mapbox GL JSでラスタータイルの色を変更できると様々な地図表現が可能になります。最後に、Mapbox GL JS v3のラスタータイルの色を変更できる機能を用いた事例をもう一つ紹介して終わります。

https://qiita.com/shi-works/items/303114b4e19a92d48a55