ベクトルタイルのつくりかた（2024年版）

## はじめに

近年、一気にコモディティ化が進んだベクトルタイルですが、その作り方はエンジニアのノウハウに依るところが大きいのが現状です。ベクトルタイルの生成周りはOSSで構成されているので、ドキュメントを読めば使い方はわかります。ただそこに、経験上いろいろとコツがあることがわかっており、しかしそれが明文化されていない状況です。本記事は、ベクトルタイルの基本的な生成方法から、典型的な特殊ケースにどう対処すべきかなど、網羅的に整理することを目的とします。

## ベクトルタイル生成に用いるツール

ベクトルタイルは下記のツールを用いて生成できます。

- **tippecanoe**
- GDAL/OGR
- PostgreSQL(PostGIS)

上記のうち、手持ちのGISデータをタイル化する際に最も優れているのは**tippecanoe**です。GDAL/OGRは地物の簡素化に難があります。PostgreSQLでは`ST_AsMVT`関数を用いればジオメトリをベクトルタイルへ変換して出力できますが、これ自体は簡素化や地物数の制限などを考慮しないため、典型的なタイル生成処理とは使い道が異なります（有望な使い道の参考：[PostGISベクトルタイルサーバーの比較: tegola/martin/pg_tileserv](https://qiita.com/Kanahiro/items/bcbf26c65322385fd003)）。

### tippecanoe

https://github.com/felt/tippecanoe

#### 参考: tippecanoeのフォーク

tippecanoe（ティピカノー）は、[Mapbox社が開発・メンテナンス](https://github.com/mapbox/tippecanoe)していた、BSD2条項ライセンスで頒布されるオープンソースソフトウェアです。メンテナンス「していた」というのがポイントで、2021年以降コードに変更がありませんでした。

ところが2022年、tippecanoeが[felt](https://github.com/felt)という地図サービスを提供する組織にフォークされました。なんとMapbox社でtippecanoeを開発していたエンジニアがfeltへジョインし、活発に開発が再スタートしたのです。多くの新機能・改善が既にリリースされているので、今後はfelt/tippecanoeをupstreamと考えるのが良さそうです。

## tippecanoeの環境構築

### macOS

```sh
brew install tippecanoe
```

### Ubuntu20.04

```sh
apt install libsqlite3-dev zlib1g-dev
git clone https://github.com/felt/tippecanoe.git
cd tippecanoe
make
make install
```

※Windowsでは動作しないので、WSLを使いましょう

下記コマンドが通れば、正常にインストールできています。

```sh
tippecanoe --help

Usage: tippecanoe [options] [file.json ...]
  Output tileset
         --output=output.mbtiles [--output-to-directory=...] [--force]
         [--allow-existing]
  Tileset description and attribution
         [--name=...] [--attribution=...] [--description=...]
  Input files and layer names
...
```

## tippecanoeの使い方

### 入力

tippecanoeが期待する入力は以下です。

- GeoJSON
- GeoJSONSeq（行区切りのGeoJSONのこと、geojsonlなどとも呼ばれる）
- FlatGeobuf
- CSV
- 標準入力（GeoJSON文字列）

なお、ファイルは複数指定することができます。

### 出力

出力はベクトルタイルですが、ベクトルタイルには2つの形式があります。

- MBTiles形式
- PMTiles形式
- ディレクトリ形式（ProtocolBuffers(=pbf)ファイル群）

MBTilesとは、全てのタイルデータ（pbf）を内包した単一ファイルです（実体はSQLite）。一方のディレクトリ形式では、1タイルデータは1ファイルとして書き出されます。

MBTilesは単一ファイルで非常に取り回しやすいですが、実体がデータベースであるため配信にはサーバー実装が必要です。ディレクトリ形式なら、ウェブサーバーだけでタイルを配信でき便利ですが、ファイル数が膨大になるため、アップロードなどファイルの移動でコストがかかります。要件にあった形式を使いましょう。

追記：
2024年現在、tippecanoeはPMTiles形式の出力に対応しています。ディレクトリ形式のファイル数の問題を解決しつつMBTilesとは違いサーバーを必要としないPMTilesは非常に便利です。

### 基本コマンド

```sh
# MBTiles形式
tippecanoe -o output.mbtiles input1.geojson input2.geojson

# PMTiles形式
tippecanoe -o output.pmtiles input1.geojson input2.geojson

# ディレクトリ形式
tippecanoe -e output_dir -pC input1.geojson input2.geojson
```

ここで、ディレクトリ形式の場合は必ず`-pC`オプションをつける必要があります。このオプションは「gzip圧縮を行わない」ことを意味します。これは、クライアント側のレンダラー実装が、**gzip圧縮されていないベクトルタイルを期待している**ためです（Mapbox/MapLibre、QGIS）。このオプションがないと適切にデータを読み込めないので注意が必要です。それ以外のコマンドはMBTiles/ディレクトリ形式で差はありません。以降、本記事ではMBTiles形式での例とします。

tippecanoeは標準入力に対応しているので

```sh
input.geojson > tippecanoe -o output.mbtiles
```

と書くことも出来ます。これは、ファイル形式の変換と同時にタイル化を行う際に非常に便利です（後述）。

### オプション

READMEを読むと、tippecanoeには非常に多くのオプションがあることがわかります。全てを把握することは困難ですし、そのうち頻出なのはわずかです。第一、tippecanoeはオプションを指定しない状態でもかなり良く動作するよう作られており、その結果が芳しくないときに、適切なオプション値を設定することを考えるのが良いです。

ここでは、頻出のオプションだけ紹介し、後述のユースケース別レシピ集で、特殊なオプションを紹介します。

#### `-P` 並列読み込み

入力ファイルがGeoJSONSeq（行区切りGeoJSON）の場合、`-P`をつけるだけで、入力ファイルの読み込みが並列化されます。単純に高速化が図れるでしょう。

:::note info
`-P`オプションが有効なのは、GeoJSONSeqのみでした。
Twitterにて@smellmanさんから情報提供していただきました。
https://twitter.com/smellman/status/1601143207794208770
:::

#### `-z`, `-Z`ズームレベル指定

`-Z2 -z10`とすると、ズームレベル2-10の範囲でタイルを生成します。指定しない場合は自動的に設定されます。大文字のZが最小ズーム、小文字のzが最大を示すことに注意が必要です。

#### `-pf` 地物数制限を無視する

`tippecanoe`は、デフォルトでタイルデータが大きくなりすぎないよう、1タイルあたり地物数・ファイルサイズを考慮して、自動的に地物・頂点を間引きます。`-pf`オプションは1タイルあたり地物数制限を無視します（デフォルトは20万）。

#### `-pk` ファイルサイズ制限を無視する

`-pf`と同様に、`-pk`はファイルサイズ制限を無視します（デフォルトは500KB）。

#### `-M` ファイルサイズ制限を設定する

ファイルサイズ制限のデフォルト値である500KBは、そこそこ厳しい値です。今時の高速回線ならもう少し大きなファイルサイズでも十分なパフォーマンスが出ます。

以上のオプションを使うと下記のようになります。

```sh
# ズーム4-12の範囲で、地物数制限を無視して、タイルのファイルサイズは2MBを上限として、並列読み込み
tippecanoe -o output.mbtiles -Z4 -z12 -pf -M 2000000 -P input.geojsonl
```

## ユースケース別レシピ集

### 複数のファイルをまとめてタイル化（単一レイヤー）

ベクトルタイルは「レイヤー」という概念を持ち、内部に複数のレイヤーを持つことが出来ます。tippecanoeでは複数のファイルを入力することが出来ますが、その場合レイヤー名は自動的に設定されます（input.geojson -> inputgeojson）。複数のファイルをベクトルタイルにする際、単一レイヤーにしたい場合があります。その場合は`-l`オプションを使います。

```sh
tippecanoe -o output.mbtiles -l layername input1.geojson input2.geojson
```

また、入力ファイルがひとつの場合でも自動設定されるレイヤー名ではなく別の命名をしたい場合に有用です。

### 複数のファイルをまとめてタイル化（個別レイヤー）

一方、ファイルごとに別レイヤーとしたい場合ももちろんあります（デフォルトの挙動はこちら）。`-L`オプションを使うと、ファイルごとにレイヤーを指定することが出来ます。

```sh
tippecanoe -o output.mbtiles -L layername1:input1.geojson -L layername2:input2.geojson
```

### メッシュ状のポリゴンデータのタイル化

メッシュデータの扱いは、ベクトルタイルが苦手とするところです。というのも、ベクトルタイルでは、ズームレベルに応じて、見た目が破綻しない範囲で頂点や地物が間引かれますが、メッシュデータは間引ける頂点がないためです。また、メッシュデータは一様に並んでいることに意味があるため、間引かれると良くないデータになってしまいます。

ここでは[500mメッシュ別将来推計人口データ（H30国政局推計）](https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-mesh500h30.html)を例に、メッシュデータでベクトルタイルを作るレシピを考えます。

まずは単純にベクトルタイルを作ってみます。

```sh
ogr2ogr -f GeoJSONSeq mesh.geojsonl 500m_mesh_2018_01.shp
tippecanoe -o output.mbtiles mesh.geojsonl -P
```

```
tippecanoe -o output.mbtiles -P mesh.geojsonl 
For layer 0, using name "meshgeojsonl"
41717 features, 48746670 bytes of geometry, 8 bytes of separate metadata, 19264811 bytes of string pool
tile 3/7/2 size is 1917329 with detail 12, >500000    
tile 4/14/5 size is 7088462 with detail 12, >500000    
tile 4/14/5 size is 1917335 with detail 11, >500000    
tile 5/28/11 size is 22149001 with detail 12, >500000    
tile 5/28/11 size is 7088178 with detail 11, >500000    
tile 5/28/11 size is 1917303 with detail 10, >500000    
...
could not make tile 9/457/187 small enough
tile 9/456/187 size is 1166559 with detail 11, >500000    
tile 9/456/187 size is 1166041 with detail 10, >500000    
tile 9/456/187 size is 1167580 with detail 9, >500000    
tile 9/456/187 size is 1158607 with detail 8, >500000    
  64.2%  9/456/187  


*** NOTE TILES ONLY COMPLETE THROUGH ZOOM 8 ***
````

大量のコンソール出力ののち、不穏なログが出力されました。データを見てみます。

![Screen Shot 2022-11-30 at 16.53.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/31c2fc0c-dff4-b3f2-725b-55e41dec09db.png)

高いズームレベルで、正常にタイルを作れませんでした。これはファイルサイズ制限によります。tippecanoeはファイルサイズ制限に合わせるため、頂点・地物を間引きます。しかし今回のデータはメッシュデータであり前述のとおりベクトルタイル化が難しいため、そもそもタイルを生成出来なかった、というのが上記のログになります。なので次は、地物数・ファイルサイズ制限を無視してタイルを生成してみましょう。

```sh
tippecanoe -o output2.mbtiles mesh.geojsonl -pf -pk -P
```

![Screen Shot 2022-11-30 at 16.58.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/2c4d82bf-9dd8-25a5-e329-4342437a6d97.png)

品質のよいタイルが生成できました。ただし注意が必要で、1タイルに非常に多くの地物が格納されることとなるため、ファイルサイズが非常に大きくなっています（そういうオプションを指定したから当たり前ですが）。たとえばzxy=5-28-11のタイルのファイルサイズは**22.1MB**でした。明らかに過大で、このまま配信するとデータのダウンロードに時間がかかりパフォーマンスが低下するでしょう。

とはいえ、メッシュデータの品質を保ちつつ上記の問題を解決するには、タイルのズームレベルを調節するしかありません（どうやっても1タイルの領域にたくさんの地物が入ってきてしまうため）。ファイルサイズを確認すると、最小ズームレベルはファイルサイズが最大でも2MB程度に収まる9あたりがよさそうです。

そんなわけで、下記のコマンドに落ち着きます。

```sh
tippecanoe -o output2.mbtiles mesh.geojsonl -Z9 -pf -pk -P
```

ベクトルタイル生成では、データ品質とファイルサイズのバランスを取ることが重要です。

### ファイル形式を変換しつつタイル化

現在、世間で最も広く使われているGISデータ形式はシェープファイルですが、tippecanoeは対応していません。シェープファイルに限らず、事前にGeoJSONへの変換が必要です。ベクトルデータの形式変換には`GDAL/OGR`を使うのが一般的です（ここではGDALのインストール等は割愛します）。

シェープファイルからGeoJSONへの変換は、最もシンプルに下記のように実行できます。

```sh
ogr2ogr input.geojson input.shp
```

ここで、GDAL/OGRの[Virtual File System](https://gdal.org/user/virtual_file_systems.html)という仕組みを利用して、変換後のGeoJSONを標準出力します。

```sh
ogr2ogr -f GeoJSONSeq /vsistdout input.shp
```

するとGeoJSON文字列が標準出力されます。tippecanoeは標準入力を受けられるため

```sh
ogr2ogr -f GeoJSONSeq /vsistdout input.shp | tippecanoe -o output.mbtiles
```

と、パイプでつなぐことで、中間ファイルを書き出すことなく・ワンライナーでシェープファイルからベクトルタイルへの変換が可能となります。GeoJSONはテキスト形式のためファイルサイズが大きくなりがちでファイル書き出しのコストが大きいため、上記は非常に有用です。