Mapbox GL JS v3と国土地理院 標高タイルで「自分で作る色別標高図」風の地図を作成してみました

# はじめに
:::note info
この記事は [Mapbox Advent Calendar 2023](https://qiita.com/advent-calendar/2023/mapbox) 2日目の記事です。
:::
- 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形式）を用いて、地理院地図の「[自分で作る色別標高図](https://www.gsi.go.jp/common/000219828.pdf)」風の地図を作成してみました。

:::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/1730185133729489323?s=20

# デモサイト
https://shi-works.github.io/gsi-dem-yourself-relief-map-on-mapbox-gl-js/#13.11/36.75109/139.50918/-27.1/62

# 標高タイルの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/gsi-dem-yourself-relief-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～4000の範囲を指定しています）。なお、[Mapboxのドキュメント](https://docs.mapbox.com/style-spec/reference/layers/#paint-raster-raster-color)によれば、この範囲が256に等分割されて、`raster-color`で利用されるようです（[@mg_kudo](https://qiita.com/mg_kudo)さんに情報提供いただきました）。
```javascript:javascript
      // カラーを定義
      var colors = {
        "-0": '#7B77D8',
        "250": '#72D3FE',
        "500": '#72FED9',
        "750": '#74FF8A',
        "1000": '#AAFE72',
        "1250": '#F9FF74',
        "1500": '#FDB271',
        "1750": '#FF7380',
        "2000": '#FE72D2',
        "2000-": '#F274FC'
      };

      // 標高分解能
      const u = 0.1

      // 標高タイルラスターレイヤ
      map.addLayer({
        id: 'gsi-terrain-raster',
        source: 'gsi-terrain-raster',
        type: 'raster',
        paint: {
          'raster-color': [
            'interpolate',
            ['linear'],
            ['raster-value'],
            0, colors['-0'],
            250, colors['250'],
            500, colors['500'],
            750, colors['750'],
            1000, colors['1000'],
            1250, colors['1250'],
            1500, colors['1500'],
            1750, colors['1750'],
            2000, colors['2000'],
            4000, colors['2000-']
          ],
          'raster-color-mix': [
            2 ** 16 * 255 * u,
            2 ** 8 * 255 * u,
            255 * u,
            -10000
          ],
          'raster-color-range': [0, 4000],
          'raster-opacity': 0.7,
          'raster-resampling': 'nearest'
        }
      })
```

- [dat.GUI](https://github.com/dataarts/dat.gui)で標高レンジに応じてラスターの色を動的に変更できるようにしてあります（ただ、標高レンジも動的に変更したかったのですが、やり方が思い浮かばず断念しました。この記事を見た方に実装していただけることに期待しています（他力本願））。
```javascript:javascript
      // GUIに標高レンジの色設定用コントローラを追加
      var gui = new dat.GUI();
      gui.addColor(colors, '-0').onChange(setReliefColors);
      gui.addColor(colors, '250').onChange(setReliefColors);
      gui.addColor(colors, '500').onChange(setReliefColors);
      gui.addColor(colors, '750').onChange(setReliefColors);
      gui.addColor(colors, '1000').onChange(setReliefColors);
      gui.addColor(colors, '1250').onChange(setReliefColors);
      gui.addColor(colors, '1500').onChange(setReliefColors);
      gui.addColor(colors, '1750').onChange(setReliefColors);
      gui.addColor(colors, '2000').onChange(setReliefColors);
      gui.addColor(colors, '2000-').onChange(setReliefColors);

      // 標高レンジに基づいて色を設定する関数
      function setReliefColors() {
        map.setPaintProperty('gsi-terrain-raster', 'raster-color', [
          'interpolate',
          ['linear'],
          ['raster-value'],
          0, colors['-0'],
          250, colors['250'],
          500, colors['500'],
          750, colors['750'],
          1000, colors['1000'],
          1250, colors['1250'],
          1500, colors['1500'],
          1750, colors['1750'],
          2000, colors['2000'],
          4000, colors['2000-']
        ]);
      }
```

# 参考文献

https://qiita.com/tattii88/items/ea3dcb50494d249206d5

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/54f8d113f0d54279f9db