Maputnikを使ってベクトルタイルのスタイルを設定してMapLibre GL JSで表示する

## はじめに
:::note info
この記事は [FOSS4G Advent Calendar 2022](https://qiita.com/advent-calendar/2022/foss4g) 17日目の記事です。
:::

MapLibre GL JS大好きな私でございます。

以前、GIS系のハッカソンで@smellman さんにMaputnikを使ったベクトルタイルの設定を教えてもらいました。（今思うと貴重すぎる体験・・）

当時GIS初心者の私には難しく感じて、苦手意識を持っていたMaputnik・・
MapLibreのスタイルファイルに慣れてきて、ベクトルタイルを愛用するようになった今こそ、Maputnikを学び直し、使えるようになるのだ！！

という謎の気合を込めて書いた記事になります。

ということで、国土地理院のベクトルタイルからMaputnikを使って建物データを抽出してスタイリングし、MapLibre GL JSで表示してみようと思います。

### 動作環境
OS: Window 10
MapLibre GL JS: version 2.4.0

### ベクトルタイルとは
地図の内容をテキストとして格納する形式です。
地物の種類に応じてスタイルを設定することができます。

詳しくは、[こちらの記事](https://qiita.com/asahina820/items/fda3e50c6df7f55f5258)を参照ください。

### Maputnikとは
Mapboxのスタイル仕様（マップのスタイルを定義するJSONオブジェクト）のためのオープンソースのエディターです。

https://maputnik.github.io/

有識者に聞いてみたところ、読み方は「まっぷとにっく」だそうです。

https://maputnik.github.io/editor/

使い方は主に3つあります。今回は環境構築が不要な、オンラインのWebエディターを使います。
- [オンラインのWebサイト](https://maputnik.github.io/editor/)を使う
- Maputnik CLIを使う
- Dockerを使う

## 国土地理院のベクトルタイルのスタイルを抽出する
[Maputnikのオンラインエディタ](https://maputnik.github.io/editor/)を開きます。

### データソース追加
上のメニューの「Data Sources」をクリックします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667439/f443cb35-6b38-a2ed-5cc7-b3cbc56dc4ed.png)

Add New Sourceの項目を入力します。

- Source ID
    - データソースを識別する一意のIDを入れます
    - 今回は、gsi-vectorにしました
- Source Type
    - データソースの種類を指定します
    - 国土地理院の[地理院地図Vector（仮称）提供実験](https://github.com/gsi-cyberjapan/vector-tile-experiment)に、`本提供実験によるベクトルタイルは、地理院タイル（ラスタ）と同じ方式で配信します。`とあるので、XYZ方式となります。
- 1st Tile URL
    - タイルのURLを指定します
    - 国土地理院の[地理院地図Vector（仮称）提供実験](https://github.com/gsi-cyberjapan/gsimaps-vector-experiment)に記載のある、`https://cyberjapandata.gsi.go.jp/xyz/experimental_bvmap/{z}/{x}/{y}.pbf`を指定します
- Min Zoom
    - タイルが利用可能な最小ズームレベルを指定します
    - サイトを見ても特に指定がなさそうだったので、今回は0にしました
- Max Zoom
    - タイルが利用可能な最大ズームレベルを指定します
    - 今回は、サイト内で一番大きなズームレベルの18を指定します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667439/43ecc4cd-0f42-07c4-5647-cc1b5bdc2f69.png)


入力が完了したら、「Add Source」ボタンをクリックします。

### レイヤ追加
建物を抽出してみましょう。
Layersタブの、「Add Layer」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667439/a40c493e-dc56-f280-c486-9e1381ea3480.png)

Add Layerの項目を入力します。

- ID
    - レイヤを識別する一意のIDを入れます
    - 今回は、gsi-buildingにしました
- Type
    - レイヤのレンダリングタイプを指定します
    - fill（多角形）、line（線）、symbol（アイコンまたはテキストラベル）、circle（円）、heatmap（ヒートマップ）、fill-extrusion（3Dポリゴン）、raster（ラスター）などを指定できます
    - 今回は、3D表示できるようにfill-extrusionを指定します
- Source
    - このレイヤで使用されるソースを指定します
    - 先ほどソースに追加した、gsi-vectorを指定します
- Source Layer
    - ベクタータイルソースから使用するレイヤを指定します
    - [地物コード及び表示ズームレベル一覧](https://maps.gsi.go.jp/help/pdf/vector/dataspec.pdf)のsource-layerを参考にすればよいでしょう
    - 今回は建物なので、buildingを指定しました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667439/bde24748-de25-b12e-1b19-e797e8c159f8.png)

入力が完了したら、「Add Layer」ボタンをクリックします。


### レイヤ設定変更
ズームしてみると建物が立体表示されていますが、これは追加したレイヤのものではありません。
building-3dのレイヤです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667439/ecaf1322-3270-eea1-0dbf-4127cf25d3c7.png)


building-3dを非表示にしましょう。
:eye: のアイコンをクリックすると、表示/非表示を切り替えられます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667439/64b5fb49-a14b-0c28-6e09-0ffab609d6c5.png)


非表示になり、立体的な建物が消えました。
この黒い2次元ポリゴンが、追加したレイヤになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667439/a3315849-35d4-feec-02cf-a02052275ef0.png)

### スタイル調整
建物の色、高さを調整していきます。
レイヤの一覧から、上で作成したレイヤ（今回だとgsi-vector）をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667439/f2da7e6f-355c-bda3-1f8c-017fde817cce.png)



この画面で、レイヤの設定を変更したり、フィルタをかけたり、表示の設定などができます。

Paint Propertiesを開きます。

- Extrusion-color
    - 地物の色を設定できます
    - 今回は、建物っぽい灰色`#C0C0C0` にしました
- Extrusion-height
    - 地物の高さを設定できます
    - [地物の属性](https://maps.gsi.go.jp/help/pdf/vector/attribute.pdf)を見ると、建物には高さ情報がないので、一律50mにしておきます


地物の色が灰色になり、高さが追加されて
にょきっと建物が生えてきました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667439/6067a237-ad36-c197-2957-9a94947fef73.png)

地物の設定をプレビューで確認しながらスタイルの設定をできるのはいいですね～



## MapLibre GL JSで表示する
### スタイルの確認
JSON Editorに出力されたテキストが、MapLibre GL JSのスタイルのレイヤで使えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667439/95b141e8-06c6-f3d6-8d66-c567f09dd392.png)

「Export」ボタンをクリックすると、JSONファイルをダウンロードすることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667439/7c6136cc-bf0d-f813-e5ae-44f28babd36c.png)

スタイルの書き方は、[こちら](https://maplibre.org/maplibre-gl-js-docs/style-spec/root)を参考にしてください。

### MapLibre GL JSで表示

```index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8" />
        <title>サンプル</title>
        <meta name="description" content="サンプルです" />
        <link href="style.css" rel="stylesheet" />
        <!-- MapLibre -->
        <script src="https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-gl.js"></script>
        <link href="https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-gl.css" rel="stylesheet" />
    </head>
    <body>
        <div id="map"></div>
        <script src="main.js"></script>
    </body>
</html>
```

```style.css
body {
    margin: 0;
    padding: 0;
}
#map {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 100%;
}
```

```main.js
style = {
    version: 8,
    sources: {
        // 地理院タイル（標準地図）
        'gsi-tile': {
            type: 'raster',
            tiles: ['https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png'],
            tileSize: 256,
            attribution: "地図の出典：<a href='https://www.gsi.go.jp/' target='_blank'>地理院タイル（標準）</a>",
        },
        //地理院ベクトルタイル
        'gsi-vector': {
            type: 'vector',
            tiles: ['https://cyberjapandata.gsi.go.jp/xyz/experimental_bvmap/{z}/{x}/{y}.pbf'],
            minZoom: 0,
            maxZoom: 14,
            maxzoom: 18,
            attribution: "建物データの出典：<a href='https://www.gsi.go.jp/' target='_blank'>国土地理院ベクトルタイル提供実験</a>",
        },
    },
    layers: [
        // 背景地図として国土地理院の地理院タイル（標準地図）を追加
        {
            id: 'gsi-tile',
            type: 'raster',
            source: 'gsi-tile',
            minzoom: 0,
            maxzoom: 24,
        },

        // 地理院ベクトルタイルの建物を追加
        {
            id: 'gsi-building',
            type: 'fill-extrusion',
            source: 'gsi-vector',
            'source-layer': 'building',
            layout: { visibility: 'visible' },
            paint: {
                'fill-extrusion-color': '#C0C0C0',
                'fill-extrusion-height': 50,
            },
        },
    ],
};

var map = new maplibregl.Map({
    container: 'map',
    style: style, // 地図のスタイル
    center: [139.7673068, 35.6809591], // 中心座標
    zoom: 15, // ズームレベル
    pitch: 30, // 傾き
});
```

背景地図に地理院タイル（標準地図）を使いました。
国土地理院のベクトルタイルを読み込み、レイヤを追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667439/b53145be-d4df-14cf-c585-e58abf4085b3.png)
MapLibre GL JSで表示できました！

## おわりに
Maputnikと仲良くなれた気がします。
ベクトルタイルからちょちょいと目的のオブジェクトを抜き出して、スタイルを設定できるのでとっても便利なツールだと思いました。
今後の開発でも大活躍間違いなしです。
これからもよろしくね、Maputnik:heart_eyes:

## 参考文献
https://smellman.github.io/vector_tile_book/docs/styling/maputnik.html

https://blog.geolonia.com/2020/03/20/add-source-from-gsi-japan.html