ベクター/ラスタータイルの生成と配信

この記事は [FOSS4G Advent Calendar 2018](https://qiita.com/advent-calendar/2018/foss4g) の13日目の記事です

# はじめに

ここでは [マピオン](https://www.mapion.co.jp/) において、地図タイルの生成と配信で使用しているプロプライエタリソフトウェアとその内部で利用している [FOSS4G](http://d.hatena.ne.jp/wata909/20131203) を紹介します

# 利用している [FOSS4G](http://d.hatena.ne.jp/wata909/20131203)

## [Mapbox Vector Tile](https://www.mapbox.com/vector-tiles/specification/)
通称 [MVT](https://www.mapbox.com/vector-tiles/specification/)
Mapboxが提唱するオープンなベクタータイルの仕様
[vector-tile-spec](https://github.com/mapbox/vector-tile-spec)
[マピオン](https://www.mapion.co.jp/) ではこの仕様に沿ってベクタータイルを生成し活用している

## [node-mapnik](https://github.com/mapnik/node-mapnik)
[GeoJSON](http://geojson.org/) から [MVT](https://www.mapbox.com/vector-tiles/specification/) への変換で使用している
初期の頃は [MVT](https://www.mapbox.com/vector-tiles/specification/) のフォーマットである [Protocol buffers](https://developers.google.com/protocol-buffers/) を直に作っていた
今は [PostGIS](https://postgis.net/) に [ST_AsMVT](https://postgis.net/docs/ST_AsMVT.html) が追加されたのでそれを使うのがよい

## [node-mapbox-gl-native](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/node)
ベクタータイルからラスタータイルへの変換で使用している

# 作成した（主な）プロプライエタリソフトウェア

## mvt-plant
Postgres(PostGIS)に格納された位置データからベクタータイルを大量に作り出す [node](https://nodejs.org) モジュール
内部で [node-mapnik](https://github.com/mapnik/node-mapnik) を使用している
出力先はS3 or [MBTiles](https://www.mapbox.com/help/define-mbtiles/) or ローカル

<details><summary>図</summary><div>
![tile-plant.png](https://qiita-image-store.s3.amazonaws.com/0/56991/f8b696a7-4eec-8f16-7511-c8bb76131b42.png)
</div></details>

<details><summary>使い方</summary><div>

```js
const plant = new MvtPlant(config);
await plant.run();
```

</div></details>

## raster-plant
ベクタータイルからラスタータイルを大量に作り出す [node](https://nodejs.org) モジュール
内部で [node-mapbox-gl-native](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/node) を使用している
出力先はS3 or [MBTiles](https://www.mapbox.com/help/define-mbtiles/) or ローカル

<details><summary>図（mvt-plantと同じ）</summary><div>
![tile-plant.png](https://qiita-image-store.s3.amazonaws.com/0/56991/f8b696a7-4eec-8f16-7511-c8bb76131b42.png)
</div></details>

<details><summary>使い方</summary><div>

```js
const plant = new RasterPlant(config);
await plant.run();
```

</div></details>

<details><summary>ポイント</summary><div>

ローカルで利用できるものはローカルに置く
タイル境界付近でラベルが切れる問題はバッファを取って対処

```js
const image = sharp(buffer, {
  raw: {
    width: width+bufferSize*2,
    height: height+bufferSize*2,
    channels: 4
  }
}).extract({ left: bufferSize, top: bufferSize, width: width, height: height });
```

imageminでファイルサイズを最適化

```js
const minBuffer = await imageminPngquant({quality: 100})(buffer);
```

</div></details>

## map-center
地図運用で必要な作業を一手に担う [node](https://nodejs.org) CLI

- 位置情報DBの構築とアップデート
- ベクタータイルの全生成
- ラスタータイルの全生成
- ベクタータイルの差分更新
- ラスタータイルの差分更新
- など

内部で `mvt-plant` `raster-plant` を使用している

<details><summary>使い方</summary><div>

```sh
// ベクタータイルを生成する場合
$ PROCESS_LIST=vtile \
    TILE_GENERATOR_THREAD_COUNT=4 \
    TILE_GENERATOR_PROCESS_COUNT= \
    TILE_GENERATOR_LOG_LEVEL=debug \
    TILE_GENERATOR_TILE_IDS=./tile/sample-tile_ids.sqlite \
    TILE_GENERATOR_BUCKET_VECTOR=sample-tiles \
    TILE_GENERATOR_USER_VECTOR=mapion \
    TILE_GENERATOR_TILESET_NAME=honjo-v20181212 \
    TILE_GENERATOR_TILE_HOST=localhost \
    TILE_GENERATOR_SEQ_HOST=localhost \
    TILE_GENERATOR_SEQ_NAME_VECTOR=seq_vector \
    DB_USER=**** \
    DB_PW=**** \
    ...
    ./index.sh

// 設定はdotenvを使用
// TILE_GENERATOR_PROCESS_COUNTを指定するとその数だけプロセスが立ち上がり並列で実行される
// TILE_GENERATOR_PROCESS_COUNTを指定しない（空欄）とCPUの数が設定される（マシンのリソースを最大限活用する）
```

```sh
// ラスタータイルを生成する場合
$ PROCESS_LIST=rtile \
    TILE_GENERATOR_THREAD_COUNT=1 \
    TILE_GENERATOR_PROCESS_COUNT= \
    TILE_GENERATOR_LOG_LEVEL=debug \
    TILE_GENERATOR_TILE_IDS=./tile/sample-tile_ids.sqlite \
    TILE_GENERATOR_BUCKET_RASTER=sample-tiles \
    TILE_GENERATOR_USER_RASTER=mapion \
    TILE_GENERATOR_TILESET_NAME=honjo-v20181212 \
    TILE_GENERATOR_SEQ_HOST=localhost \
    TILE_GENERATOR_SEQ_NAME_RASTER=seq_raster \
    ...
    ./index.sh

// 設定はdotenvを使用
// TILE_GENERATOR_PROCESS_COUNTを指定するとその数だけプロセスが立ち上がり並列で実行される
// TILE_GENERATOR_PROCESS_COUNTを指定しない（空欄）とCPUの数が設定される（マシンのリソースを最大限活用する）
```

</div></details>

<details><summary>ポイント</summary><div>

現状、日本全国のベクタータイル（z4-z18）生成に36時間ほどかかるが、サーバを増やせばその分早くなる
同じく、日本全国のラスタータイル（z5-z19）生成には33時間ほどかかるが、サーバを増やせばその分早くなる

</div></details>

## gl-server
ベクタータイルやラスタータイルなどを配信するアプリケーションサーバ

- ベクタータイルの配信
- ラスタータイルの配信
- スタイルファイルの配信
- フォントファイルの配信
- スプライトファイルの配信
- StaticMapの配信
- 複数のベクタータイルを合成して配信
- 複数のフォントファイルを合成して配信
- [JWT](https://jwt.io/) による認証機能

内部で [node-mapnik](https://github.com/mapnik/node-mapnik) と [node-mapbox-gl-native](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/node) を使用している

<details><summary>図</summary><div>
![gl-server.png](https://qiita-image-store.s3.amazonaws.com/0/56991/ca906fd9-7ec6-0094-4f96-7d51e3c071fb.png)
</div></details>