# FOSS4G Niigata 2019: 国連と始めるベクトルタイル
日時
~ 2019-09-13T15:00/18:00 (3時間)
講師
~ 柴本 歩
この資料のために書き下ろした内容は CC0 です。
---
## 国連と始めるベクトルタイル
### 会場 WiFi
SSID
~ (現場合わせ)
パスワード
~ (現場合わせ)
---
## 国連と始めるベクトルタイル

---
#### 国連ベクトルタイルツールキットとは 1/2
- 既存のオープンソースソフトウェアを組み合わせて、ベクトルデータからベクトルタイルを生産してホストすることと、ベクトルタイルをスタイリングして最適化することを支援するツールキット。
- 国連オープン GIS イニシアティブのプロジェクトで、現在のところ、国連地理空間情報課、国連グローバルサービスセンター、国土地理院、農業環境変動研究センター、OSGeo 日本支部、Mapbox から技術者が参加している。
---
#### 国連ベクトルタイルツールキットとは 2/2
- 目下、ツールキット収録した Docker コンテナイメージ rasv を整備中
- rasv を活用したハンズオンマテリアル ango を開発している。
---
##### 今回つくるもの: 自然災害伝承碑ベクトルタイルサイト
<iframe width="100%" height="500px" src="https://un-vector-tile-toolkit.github.io/ango-static"></iframe>
ズームインするとテキスト表示が切り替わります。Ctrl+ドラッグで斜め表示
---
# 作業開始
---
## 会場 WiFi (再掲)
SSID
~ (現場合わせ)
パスワード
~ (現場合わせ)
---
## Docker イメージを動かそう
```console
$ docker run -ti --rm unvt/ango
root@09dc97356d7e:~#
```
* '09dc97356d7e' の部分は異なる
### うまくいかない場合
```console
$ curl -O http://ango.vectortiles.xyz/ango-amd64.tar.gz
$ gunzip ango-amd64.tar.gz | docker load
```
* Raspberry Pi の場合 amd64 → armhf
---
## コマンドを動かしてみる
```console
(# exit) # さっき試した docker run から抜ける
$ docker run -ti --rm -p 3000:3000 -p 8888:8888 unvt/ango
root@fef54f77bc33:~# tippecanoe -v
tippecanoe v1.34.4
root@fef54f77bc33:~# node -v
v10.15.2
```
- バージョンの表示が出ない場合にはサポートを受けてください
- 先に進んでおきたい場合: https://hackmd.io/@hfu/ango-slides
---
### 上級者向け: ホストボリュームマウント
作業データを保存したり、持ち込みデータを扱う場合はマウントすることをお勧めします。
```
-v (ホスト側のディレクトリの絶対パス):/media
```
```console
$ # /Users/hfu を /media として触れるようにする
$ docker run -ti --rm \
-v /Users/hfu/:/media \
-p 3000:3000 -p 8888:8888 unvt/ango
```
---
#### 収録されているコマンドの紹介 - 生産
```console
$ tippecanoe -v # ベクトルタイルを生産
tippecanoe v1.34.5
$ tile-join # ベクトルタイルを操作
Usage: tile-join [-f] [-i] [-pk] [-pC] [-c joins.csv] [-X] [-x exclude ...] -o new.mbtiles source.mbtiles ...
$ ogr2ogr --version # データ変換
GDAL 2.4.2, released 2019/06/28
$ osmium --version # OpenStreetMap のデータ変換
osmium version 1.10.0 (v1.10.0-76-g26674e1)
...
```
---
#### 収録されているコマンドの紹介 - ホスト
```console
$ node -v # サーバは Node.js で作ります
v10.15.2
$ budo --version # 開発用サーバ
budo v11.6.3
$ pm2 --version # サーバ
3.5.1
$ browserify --version # JavaScript 変換
16.5.0
...
```
---
#### 収録されているコマンドの紹介 - スタイル
```console
$ vi --version # スタイルを HJSON 形式で書く
VIM - Vi IMproved 8.1 (2018 May 18, compiled Jun 15 2019 16:41:15)
...
$ hjson -v # HJSON を JSON に変換する
Hjson.js 3.1.2
$ gl-style-validate # Mapbox Style を検証する
usage: ...
```
---
#### 収録されているコマンドの紹介 - 最適化
```console
$ node ~/vt-optimizer/index.js --help # ベクトルタイルサイズ最適化
Vector Tile Weight Loser...
```
---
## ハンズオン作業
1. 生産
2. ホスト
3. (スタイル)
4. (最適化)
今回のハンズオンでは、基礎編として 1., 2.。
3., 4. は進度に合わせたバリエーションルート。
---
## (1) 生産
GeoJSONS をベクトルタイルに変換
```console
$ cd ango-produce
$ rake
tippecanoe --output=tiles.mbtiles --read-parallel --force --layer=ndm --minimum-zoom=7 --maximum-zoom=10 --prefilter='node prefilter.js' ../geojsons-natural-disaster-monuments/data.geojsons
255 features, 14316 bytes of geometry, 2 bytes of separate metadata, 109216 bytes of string pool
99.9% 10/900/404
tile-join --force --output-to-directory=zxy tiles.mbtiles --no-tile-compression
```
まずは実行してみてください
---
### 実行された内容
```console
$ cat Rakefile
task :default do
sh "tippecanoe --output=tiles.mbtiles\
--read-parallel --force --layer=ndm\
--minimum-zoom=7 --maximum-zoom=10\
--prefilter='node prefilter.js'\
../geojsons-natural-disaster-monuments/data.geojsons"
sh "tile-join --force\
--output-to-directory=zxy tiles.mbtiles\
--no-tile-compression"
end
```
---
### ソースデータ
```console
# more ../geojsons-natural-disaster-monuments/data.geojsons
{"geometry":{"type":"Point","coordinates":[128.042983,26.554941]},"type":"Feature","properties":{"ID":"47209-001","LoreName":"津波襲来の碑","LoreYear":"2012","Address":"沖縄県名護市字大浦","DisasterName":"津波<br>(1960年5月24日)","DisasterKind":"津波","DisasterInfo":"昭和35年(1960)5月南米チリでM8.5の地震が起き大津波が発生、津波は太平洋を横
断し日本近海を襲った。当地には数回に亘り襲来。津波高5mにも及び大浦橋が全壊、護岸も決壊した。","Image":"https://maps.gsi.go.jp/legend/disaster_lore/47209/47209-001.jpg","ImageWidth":"1200","ImageHeight":"900"}}
...
```
一行ごとに GeoJSON Feature が書いてある
[RFC 8142](https://tools.ietf.org/html/rfc8142): ogr2ogr は GeoJSONS と呼ぶ
---
### tippecanoe
```console
tippecanoe --output=tiles.mbtiles\
--read-parallel --force --layer=ndm\
--minimum-zoom=7 --maximum-zoom=10\
--prefilter='node prefilter.js'\
../geojsons-natural-disaster-monuments/data.geojsons
```
output
~ 出力するベクトルタイルパッケージのパス
layer
~ レイヤ名 (protips: 複数レイヤを含む場合には、feature.tippecanoe.layer にセット)
prefilter
~ タイルに切った後にかけるフィルタ
---
#### プレフィルタ: 地物ごとの調整
```javascript
// prefilter.js: 標準入力の各行を `modify()` して出力
const modify = require('./modify.js')
const readline = require('readline')
const rl = readline.createInterface(process.stdin, {})
rl.on('line', (line) => {
console.log(JSON.stringify(modify(JSON.parse(line))))
})
```
```javascript
// modify.js: 与えられた地物を修正して返す
module.exports = (f) => {
for (k of [
'DisasterKind', 'DisasterName', 'DisasterInfo', 'LoreName'
]) {
// 余計な <br> タグをとる
f.properties[k] = f.properties[k].replace(/<br>/g, " ")
}
return f
}
```
---
#### tippecanoe で使用したその他のオプション
read-parallel
~ 地物を並列に読む → 速くなる
force
~ ベクトルタイルパッケージを上書きする
minimum-zoom
~ 生産する最小ズーム (protips: 地物ごとなら feature.tippecanoe.minzoom にセット)
maximum-zoom
~ 生産する最大ズーム (protips: 地物ごとなら feature.tippecanoe.maxzoom にセット)
---
### tile-join
```console
tile-join --force\
--output-to-directory=zxy tiles.mbtiles\
--no-tile-compression
```
force
~ 出力フォルダを上書きする
output-to-directory
~ 出力フォルダ
no-tile-compression
~ ベクトルタイルを gzip 圧縮せず出力
---
#### 雑学: gzip 圧縮とベクトルタイルの関係
1. ウェブではベクトルタイルは __gzip 圧縮されて送られるのが普通__。そのときは content-encoding: gzip ヘッダをつけて送る。
3. このため、MBTiles にはもとから gzip 圧縮して格納するのがデフォルト。
4. プレインなウェブサーバでは content-encoding: gzip つきはデフォルトではなく、__非圧縮__ で送る。
5. よって「MBTiles の中では gzip 圧縮済み、ファイルシステムに展開する場合には gzip 圧縮しない」がグッドプラクティス
---
## (2) ホスト
ベクトルタイルパッケージをブラウザで見ることができるようにする。
```console
$ cd ~/ango-host # ホストプログラムに移動
$ yarn # 必要なライブラリを導入
$ rake build # 地図サイトを構築
$ rake start # サーバをスタート
```
まずは実行してみてください。
→ http://localhost:3000
---
<iframe width="100%" height="500px" src="https://un-vector-tile-toolkit.github.io/ango-static"></iframe>
```console
$ rake stop # サーバを止める
```
---
## 解説: `rake build`
```ruby
task :build do # サイトのファイルを作る
sh "hjson -j htdocs/style.hjson > htdocs/style.json"
sh "browserify -o htdocs/bundle.js -t " +
"[ babelify --presets [ @babel/preset-env ] ] app.js"
end
```
- htdocs/style.hjson → htdocs/style.json
- app.js → htdocs/bundle.js
---
### `htdocs/style.hjson`
```hjson
{
version: 8
center: [135, 35]
zoom: 7
sources: {
v: {
...
255
]
text-halo-width: 1
}
}
]
}
```
[Mapbox Style](https://docs.mapbox.com/mapbox-gl-js/style-spec/) を簡明のため [HJSON](http://hjson.org) で書いたもの
---
#### なぜスタイルを HJSON で手書きするのか
- スタイルは意外と論理的なもの
- Maputnik のような GUI エディタでは見通しが立てにくく、スタイル記述をコンパクトにしにくい
- Maputnik は特有のメタデータを吐き、__[expressions](https://docs.mapbox.com/mapbox-gl-js/style-spec/#expressions)に対応していない__。
- より複雑なスタイルは今後 HOCON で書くかも
---
### `app.js`
```javascript
const map = new mapboxgl.Map({
container: 'map',
style: 'style.json',
attributionControl: true,
hash: true
})
```
単なるウェブ地図起動の JavaScript だが、モダンに書きたいので browserify/babelify で変換している。
---
## 解説: `rake start`
```ruby
task :start do
sh "pm2 start process.yml"
end
```
---
### `process.yml`
```yml
apps:
- script: index.js
name: ango
instances: 4
exec_mode: cluster
```
4インスタンスのクラスタモードで起動
---
## 解説: `rake stop`
```ruby
task :stop do
sh "pm2 stop ango; pm2 delete ango"
end
```
---
## 解説: `rake _mapbox`
```ruby
task :_mapbox do
sh "cp ../mapbox-gl-js/dist/mapbox-gl.js htdocs"
sh "cp ../mapbox-gl-js/dist/mapbox-gl.js.map htdocs"
sh "cp ../mapbox-gl-js/dist/mapbox-gl.css htdocs"
end
```
別途ビルドした Mapbox GL JS から必要ファイルをコピー
---
# バリエーションルート
---
- 別データに挑戦
- gh-pages を用いたホスティング (要 GitHub アカウント)
- Vector Tile optimizer を用いた最適化
- Maputnik を用いたスタイル調整
---
## まとめ
- 成果紹介
- 国連ベクトルタイルツールキットのこれから
- 全体を通しての質疑
- ラップアップ
- 集合写真撮影
---
## おわり
---
# 参考
---

オープンジオデータをベクトルタイルに変換する取り組みの[アンブレラ](https://github.com/optgeo/)です。
{"metaMigratedAt":"2023-06-14T22:56:45.550Z","metaMigratedFrom":"Content","title":"FOSS4G Niigata 2019: 国連と始めるベクトルタイル","breaks":"true","contributors":"[{\"id\":\"fc0f9ab3-2844-43fe-a29f-da028138304f\",\"add\":9817,\"del\":1396},{\"id\":\"370340a2-02b1-4368-b231-d79cd9de7109\",\"add\":2,\"del\":2}]"}