建物が立ち上がるベクトルタイル地図の作り方（完璧版）

ベクトルタイルに埋め込まれた building の height または building:levels の情報を style.json で処理して、建物が立ち上がるようにしてみました。

https://unvt.github.io/mame/#15.68/41.322155/19.824567/-85.6/57
<img width="1184" alt=" 2020-06-29 22.14.37.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102082/82608ef0-9689-3d2b-b3e0-915aae171b3a.jpeg">

これの作り方を共有します。

# 0. 目的の整理
西バルカン地域をカバーする OSM ベクトルタイル実験サイトを作成すること

# 1. 作成範囲を作る
https://geojson.io を使って、作成範囲を作ります。
作ったものを https://github.com/optgeo/kokoromi-western-balkans/blob/master/extent/area.geojson に置きました。

作成範囲ファイルのある作業場所を clone します。
```zsh
~ $ git clone git@github.com:optgeo/kokoromi-western-balkans
```

# 2. 切り出し用作業場所を clone する
切り出し用の作業場所を clone しました。ここの Rakefile に、planet.osm.pbf をダウンロードする方法と、切り出す方法が書いてあります。
```zsh
~ $ git clone git@github.com:optgeo/waku
~ $ cd waku
~/waku $ cat Rakefile
task :planet do
  sh "curl -L -o src/planet-latest.osm.pbf -C - https://planet.openstreetmap.org/pbf/planet-latest.osm.pbf"
end

task :extract do
  sh "osmium extract --polygon area/area.geojson --output a.pbf src/planet-latest.osm.pbf"
end
```

# 3. 最新の planet.osm.pbf ファイルをダウンロードする
上記の Rakefile にあるとおり、
```zsh
~/waku $ curl -L -o src/planet-latest.osm.pbf -C - https://planet.openstreetmap.org/pbf/planet-latest.osm.pbf
```
とすることで最新の planet.osm.pbf ファイルをダウンロードできますが、ダウンロード途中で planet-latest.osm.pbf ファイルが変更されてしまうと、ダウンロードに失敗します。-latest ではなく、具体的な日付のファイルをダウンロードしましょう。

https://planet.osm.org/pbf/ を確認して、最新版の URL を確認します。作業時点では、
https://planet.osm.org/pbf/planet-200622.osm.pbf
が最新版でした。これを踏まえて、
```zsh
~/waku $ curl -L -o src/planet-200622.osm.pbf -C - https://planet.openstreetmap.org/pbf/planet-200622.osm.pbf
```
を実行します。ファイルサイズは 50GB 以上あり、ダウンロードには10時間以上かかります。

なお、何らかの事故でダウンロードが止まっても、上記のコマンドを再度投入すれば、ダウンロードが継続されます。継続でダウンロードするようにするためのオプションが `-C -` です。

# 4. md5sum をチェックする
planet.osm.pbf ファイルが壊れていると、変換の途中でエラーが起こります。他方で、planet.osm.pbf は巨大なファイルですので、事故は起こるものです。

ダウンロードの段階でファイルが壊れていないかを確認するために、md5sum をチェックしましょう。

md5sum の MD というのはメッセージダイジェストということを意味しています。md5sum は、与えられたデータに対して計算される一定の長さの文字列で、その計算をするはたらきのことをハッシュ関数といい、計算で得られた文字列をハッシュ値といいます。ハッシュ関数は、データが同じである場合に同じハッシュ値を返します。このことを利用して、ファイル（メッセージ）が同じかどうかを確認するのに、ファイル全体をながめわたす代わりに、ハッシュ値を比較するということがよく行われます。

```zsh
~/waku $ curl https://planet.openstreetmap.org/pbf/planet-200622.osm.pbf.md5
6ea93910ce1757c098132097690e5544  planet-200622.osm.pbf
```

上記が、サーバ上にあるファイルの md5sum でした。手元のディスク上にあるファイルの md5sum を、次のようにして計算します。

```zsh
~/waku $ md5sum src/planet-200622.osm.pbf
6ea93910ce1757c098132097690e5544  src/planet-200622.osm.pbf
```

ハッシュ値が 6ea9 で始まる同一の文字列になりました。これで、planet-200622.osm.pbf が問題なくダウンロードできたことが確認できたことになります。

# 5. 作成範囲をカバーする osm.pbf ファイルを planet.osm.pbf ファイルから切り出す
切り出しの標準形は、[waku の Rakefile](https://github.com/optgeo/waku/blob/master/Rakefile#L6) にあるとおり、
```zsh
  sh "osmium extract --polygon area/area.geojson --output a.pbf src/planet-latest.osm.pbf"
```
です。ここでは、これをアレンジして、次のコマンドを実行することにより、wastern-balkans.pbf を作成します。
```zsh
~/waku $ osmium extract --polygon ../kokoromi-western-balkans/extent/area.geojson --output western-balkans.pbf src/planet-200622.osm.pbf

```
なお、src/planet-200622.osm.pbf のサイズは 52GB であったのに対し、western-balkans.pbf のサイズは 662MB でした。

GitHub レポジトリに収容できるデータの総量は 1GB です。pbf ファイルからファイルシステム上のタイルにすると、データ量が２倍から数倍になります。この倍率には、どうも面積が関係するようで、ニュージーランドのときには思ったより大きくなったという感覚があります。ファイルシステム上のタイルの総量が大きすぎる場合には、大きなズームレベルから切り捨てていきます。作業を続けていきましょう。

# 6. タイルを格納するレポジトリを作る
https://github.com/kokoromi-western-balkans-tiles を作ります。

![ 2020-06-27 6 31 42](https://user-images.githubusercontent.com/18297/85903018-5283ab80-b840-11ea-9b8e-4e03af3046c4.jpg)

docs/.gitkeep を作ります。空ファイルで良いです。

![ 2020-06-27 6 32 39](https://user-images.githubusercontent.com/18297/85903060-68916c00-b840-11ea-9d1d-ee8477f8f80f.jpg)

![ 2020-06-27 6 32 47](https://user-images.githubusercontent.com/18297/85903067-6c24f300-b840-11ea-8a1d-7f1c8506d1c2.jpg)

![ 2020-06-27 6 33 04](https://user-images.githubusercontent.com/18297/85903088-7c3cd280-b840-11ea-9423-7269b5c49103.jpg)

レポジトリの Settings を開きます。

![ 2020-06-27 6 33 35](https://user-images.githubusercontent.com/18297/85903121-8b238500-b840-11ea-95a6-8fb2f3199e61.jpg)

GitHub Pages (gh-pages) の設定に行って、Source を「master branch /docs folder」に変更します。なお、執筆時点では master という表現が使われてますが、近いうちに master という表現が main といった表現に変更されているかもしれません。ここでは、執筆時点での GitHub のデフォルトをそのまま使っています。

![ 2020-06-27 6 33 55](https://user-images.githubusercontent.com/18297/85903237-cf168a00-b840-11ea-84ec-0c8660e03de9.jpg)

![ 2020-06-27 6 34 22](https://user-images.githubusercontent.com/18297/85903242-d63d9800-b840-11ea-9b61-303487d7c134.jpg)

これで、kokoromi-western-balkans-tiles レポジトリの docs に置かれたファイルが https://optgeo.github.io/kokoromi-wastern-balkans-tiles で公開されるように設定されました。

例えば、docs/zxy/2/3/1.pbf というファイルを kokoromi-western-balkans-tiles レポジトリにコミットすれば、GitHub でファイルをアップロードするアクションが動作して、動作後には https://optgeo.github.io/kokoromi-wastern-balkans-tiles/zxy/2/3/1.pbf という URL でアクセスできるようになります。

GitHub Pages は、HTTPS という、実勢上のウェブでの標準的なプロトコルでファイルをホストしてくれるだけではなく、HTTPS 上のさらに高速なプロトコルである HTTP/2 でホストをしてくれ、さらにタイルを外部サイトからアクセスするのを許可する特別な設定（Cross Origin Resource Sharing: CORS）も最初から済ませてくれています。開発者向けにチューンアップした親切な設定を、デフォルトでセットしてくれていることは非常にありがたいことです。このありがたさは、将来あなたが自分のサーバでタイルをホストする際に実感することになるかもしれません。

# 7. タイルを格納するレポジトリのクローンを作る
上記の通り設置したレポジトリのクローンをファイルシステム上に作ります。
```zsh
$ cd
~ $ git clone git@github.com:optgeo/kokoromi-western-balkans-tiles
```

# 8. naru のクローンを作る
今回の作業では、建物の表示を三次元風から三次元に改修しようと思っていますが、[事前の調査](https://github.com/unvt/naru/issues/5) から考えるに、この改修はタイルレベルではなくてスタイルレベルで行けるのではないかと思っています。このため、まずは素直に naru で pbf ファイルをベクトルタイルに変換して kokoromi-ke 相当の表示を実現してしまいましょう。

まずは naru のクローンを作ります。
```zsh
$ cd
~ $ git clone git@github.com:unvt/naru
```

naru は「geofabrik extract を使って、スタンドアローンのベクトルタイルウェブ地図を作る」という設計目標のために作られたキットなので、そのままでは使わず、これから説明するように、少しアレンジして使っていくことになります。

# 9. タイルを生産する
naru のタスクの一覧を見てみます。

```zsh
$ cd naru
~/naru $ rake -T 
rake host           # host the site
rake inet:download  # download source geospatial data to the place
rake inet:fonts     # TODO: clone and build fonts, and copy to docs
rake inet:install   # install extra software for naru
rake inet:mbgljs    # TODO: clone and build mapbox-gl-js, and copy to docs
rake inet:sprite    # TODO: clone and build maki, and copy to docs
rake js             # TODO: build JavaScript code using rollup
rake optimizer      # TODO: run vt-optimizer
rake style          # build style.json from HOCON descriptions
rake tiles          # build tiles from source data
```

タイルの生産をするのは、`tiles` ですね。Rakefile の該当記述内容は、次の通りです。
```ruby
desc 'build tiles from source data'
task :tiles do
  sh "osmium export --config osmium-export-config.json --index-type=sparse_file_array --output-format=geojsonseq --output=- src/#{AREA}-latest.osm.pbf | node filter.js | tippecanoe --no-feature-limit --no-tile-size-limit --force --simplification=2 --maximum-zoom=15 --base-zoom=15 --hilbert --output=tiles.mbtiles"
  sh "tile-join --force --no-tile-compression --output-to-directory=docs/zxy --no-tile-size-limit tiles.mbtiles"
end
```

この第一行をアレンジした、次のコマンドを実行します。具体的には osmium のインプットを、作成しておいた western-balkans.pbf に変更するだけですね。

```zsh
~/naru $ osmium export --config osmium-export-config.json --index-type=sparse_file_array --output-format=geojsonseq --output=- ~/waku/western-balkans.pbf | node filter.js | tippecanoe --no-feature-limit --no-tile-size-limit --force --simplification=2 --maximum-zoom=15 --base-zoom=15 --hilbert --output=tiles.mbtiles
For layer 0, using name "tiles"
7490213 features, 543030712 bytes of geometry, 1989854 bytes of separate metadata, 11164270 bytes of string pool
  99.9%  15/17961/12321
```

tiles.mbtiles のサイズは 1.0 GB でした。

vt-optimizer を使って、構成を確認してみます。

```zsh
~/naru $ node ../vt-optimizer/index.js -m tiles.mbtiles
  ✔ Parsing VT file contents

Vector Tile Info
Zoom levels:  0,
Format:  pbf
Center:  19.517212,42.069684,15
Layers: 

• boundary
• building
• nature
• place
• railway
• road
• route
• structure
• water

Vector Tile Summary
Zoom level  Tiles   Total level size (KB)  Average tile size (KB)  Max tile size (KB)   
----------  ------  ---------------------  ----------------------  ------------------  -
2           1       5.6240234375           5.6240234375            5.6240234375        ✓
3           2       9.6689453125           4.83447265625           5.6416015625        ✓
4           3       11.375                 3.7916666666666665      6.0556640625        ✓
5           3       14.958984375           4.986328125             9.0234375           ✓
6           8       50.69140625            6.33642578125           18.0986328125       ✓
7           16      94.51953125            5.907470703125          14.1923828125       ✓
8           54      420.9755859375         7.795844184027778       21.9052734375       ✓
9           180     963.41015625           5.352278645833334       27.646484375        ✓
10          624     3458.59375             5.542618189102564       31.6875             ✓
11          2677    37363.8994140625       13.957377442683041      167.0625            ✓
12          9545    62084.77734375         6.504429265976952       118.521484375       ✓
13          35338   101244.0712890625      2.865019845182594       66.6337890625       ✓
14          136187  240321.087890625       1.7646404421172726      70.8759765625       ✓
15          510080  470067.720703125       0.9215568552053109      100.5126953125      ✓

? Do you want to get more information about a given level? (y/N) 
```
ズームレベル 15 を捨てれば、470MB ほど削ることができるような構成になっているようです。
いざとなったらズームレベル 15 を捨てるつもりで、継続の作業をしてみましょう。ファイルシステムにタイルを展開します。

```zsh
~/naru $ tile-join --force --no-tile-compression --output-to-directory=docs/zxy --no-tile-size-limit tiles.mbtiles
```

# 10. スタイルをビルドする
動作確認の目的で、スタイルもビルドしておきます。

```zsh
~/naru $ vi hocon/_root.conf
```
として、gh-pages にホストしたタイルが反映されるように site_root を変更します。変更後の hocon/_root.conf の内容は次の通りです。

```hocon
# _root.conf
# project specific parameters
site_root: "https://optgeo.github.io/kokoromi-western-balkans-tiles"
#site_root: "http://localhost:9966"
# center: [lng, lat]
center: [
  174.786987
  -41.314950
]
zoom: 11
sprite: ${site_root}"/sprite/sprite"
glyphs: ${site_root}"/font/{fontstack}/{range}.pbf"
```

この変更をした上で、スタイルをビルドします。
```zsh
~/naru $ rake style
parse-hocon hocon/style.conf > docs/style.json
gl-style-validate docs/style.json
```

これで、naru の docs フォルダに、とりあえず動く西バルカン地域のベクトルタイルができました。これを kokoromi-western-balkans-tiles の docs にコピーしてアップロードします。

# 11. kokoromi-western-balkans-tiles にアップロード
```zsh
~/naru $ cp -r docs ~/kokoromi-western-balkans-tiles
```
これで、naru/docs に作ったファイルをコピーしました。

```zsh
~/naru $ cd ~/kokoromi-western-balkans-tiles
~/kokoromi-western-balkans-tiles $ git add .
~/kokoromi-western-balkans-tiles $ git commit -m update
Auto packing the repository in background for optimum performance.
See "git help gc" for manual housekeeping.
...
 create mode 100644 docs/zxy/9/289/194.pbf
 create mode 100644 docs/zxy/9/289/195.pbf
 create mode 100644 docs/zxy/metadata.json
[master dedc554] update
~/kokoromi-western-balkans-tiles $ git push origin master
Enumerating objects: 687472, done.
Counting objects: 100% (687472/687472), done.
Delta compression using up to 4 threads
Connection to github.com closed by remote host.
fatal: the remote end hung up unexpectedly
Compressing objects: 100% (686100/686100), done.
fatal: the remote end hung up unexpectedly
```

データが大きすぎるようですね。やり直しましょう。コミットをリセットします。

```bash
~/kokoromi-western-balkans-tiles $ git reset --hard HEAD^
Checking out files: 100% (694987/694987), done.
HEAD is now at 4aa1e62dbb Create .gitkeep
```

# 12. 方針変更
zxy ディレクトリの大きさを macOS の Finder 調べたところ、1.18 GB と表示されます。
つまり、ファイル総量を三割程度小さくすれば入ります。1.18 * 0.7 = 0.826 ですから、たぶんいけます。

他方、西バルカン地域では、小さな建物が多いようです。z=15 を捨てることをしてしまうと、小さな建物が取り込まれなくてもったいないと思います。

このため、エリアを精査して 3割ほど削ります。

西バルカン地域といったときに、クロアチアは含んでおいた方が良いのでしょうか。https://www.mofa.go.jp/mofaj/erp/c_see/page25_001753.html によれば、クロアチアを含まない６が取り扱われています。また、https://www.mofa.go.jp/mofaj/gaiko/oda/files/000458062.pdf でも「アルバニア、コソボ、セルビア、ボスニア・ヘルツェゴビナ、マケドニア旧ユーゴスラビア共和国、モンテネグロ」の６です。クロアチアを含まない６でいきましょう。

https://geojson.io をもう一度使って、ポリゴンでエリアを切り直しました。実用上、外側に少しマージンを取ります。

https://github.com/optgeo/kokoromi-western-balkans/blob/master/extent/area2.geojson
![ 2020-06-28 15 22 24](https://user-images.githubusercontent.com/18297/85939793-3c1a4480-b953-11ea-8d9e-3031eb404e10.jpg)

これで、改めて切り直しを行います。

# 13. 切り直し
```bash
$ cd ~/kokoromi-western-balkans
~/kokoromi-western-balkans $ git pull
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (4/4), done.
From github.com:optgeo/kokoromi-western-balkans
   1c5615b..c4a24a0  master     -> origin/master
Updating 1c5615b..c4a24a0
Fast-forward
 extent/area2.geojson | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 236 insertions(+)
 create mode 100644 extent/area2.geojson
$ cd ~/waku
~/waku $ mv western-balkans.pbf western-balkans.old.pbf
~/waku $ osmium extract --polygon ../kokoromi-western-balkans/extent/area2.geojson --output western-balkans.pbf src/planet-200622.osm.pbf
[======================================================================] 100% 
~/waku $ ls -lh western*pbf
-rwxrwxrwx  1 hfu  staff   662M  6 28 07:54 western-balkans.old.pbf
-rwxrwxrwx  1 hfu  staff   317M  6 28 16:07 western-balkans.pbf
```
pbf ファイルのサイズを半分以下に抑えることができたようです。

# 14. タイル生産（２回目）
```zsh
$ cd ~/naru
~/naru $ osmium export --config osmium-export-config.json --index-type=sparse_file_array --output-format=geojsonseq --output=- ~/waku/western-balkans.pbf | node filter.js | tippecanoe --no-feature-limit --no-tile-size-limit --force --simplification=2 --maximum-zoom=15 --base-zoom=15 --hilbert --output=tiles.mbtiles
For layer 0, using name "tiles"
3132118 features, 252451262 bytes of geometry, 911496 bytes of separate metadata, 4866588 bytes of string pool
  99.9%  15/17993/11801
~/naru $ ls -lh tiles.mbtiles
-rw-r--r--  1 hfu  staff   561M  6 28 16:28 tiles.mbtiles
~/naru $ node ../vt-optimizer/index.js -m tiles.mbtiles
  ✔ Parsing VT file contents

Vector Tile Info
Zoom levels:  0,
Format:  pbf
Center:  19.517212,42.069684,15
Layers: 

• boundary
• building
• nature
• place
• railway
• road
• route
• structure
• water

Vector Tile Summary
Zoom level  Tiles   Total level size (KB)  Average tile size (KB)  Max tile size (KB)   
----------  ------  ---------------------  ----------------------  ------------------  -
2           1       1.029296875            1.029296875             1.029296875         ✓
3           2       2.2451171875           1.12255859375           1.4931640625        ✓
4           2       2.2412109375           1.12060546875           1.48046875          ✓
5           2       2.8427734375           1.42138671875           1.9716796875        ✓
6           6       26.58203125            4.430338541666667       15.408203125        ✓
7           12      51.5546875             4.296223958333333       10.107421875        ✓
8           32      220.74609375           6.8983154296875         21.9052734375       ✓
9           98      469.5263671875         4.791085379464286       19.4755859375       ✓
10          354     1800.37890625          5.085816119350283       26.39453125         ✓
11          1630    21976.1181640625       13.482281082246933      112.7919921875      ✓
12          5758    35167.71484375         6.107626752995832       86.12109375         ✓
13          21088   55704.8955078125       2.6415447414554487      47.9228515625       ✓
14          80294   126118.615234375       1.5707103299670586      55.57421875         ✓
15          298315  237684.1416015625      0.7967555825270687      100.5126953125      ✓

? Do you want to get more information about a given level? (y/N) No
~/naru $ tile-join --force --no-tile-compression --output-to-directory=docs/zxy --no-tile-size-limit tiles.mbtiles
```

# 15. フォント入れ替え
naru が持っている sans フォントは、CJK に対応したフォントで、西バルカン地域で使われているディアクリティカルマークに対応したフォントが入っていないので、入れ替えます。

OpenMapTiles プロジェクトで作られたフォントは、基本的に世界のなるべく多くの文字を取り扱ったものですが、漢字については日本人にとり次のような課題がありました：Unicode では Han unification（漢統一）が行われており、例えば日本語と中国語での漢字について同一のコードが割り振られています。他方で、OpenMapTiles の標準フォントでは、漢字について中国語のグリフを優先するという判断が行われています。この結果、日本の地名が中国語のフォントで表示されるという問題がありました。このこともあり、naru などで伝統的に使っていたフォントは、日本語用にセットされたデータを使ったものでした。

他方で、最近の Mapbox GL JS では、ideograph はローカルのもので代用するのがデフォルトです。

このため、この機会に、OpenMapTiles が標準で使っているグローバル向けフォントセットを Mapbox GL JS 用に変換したものを使うように変更します。naru のレベルでこの入れ替えを行いました。

```zsh
$ cd ~
~ $ git clone git@github.com:hfu/openmaptiles-fonts
~ $ cd naru
~/naru $ git rm docs/font/sans/*
~/naru $ cp ~/openmaptiles-fonts/
~/naru $ mkdir -p docs/font/sans
~/naru $ cp ~/openmaptiles-fonts/Noto\ Sans\ Regular/*.pbf docs/font/sans
~/naru $ cp ~/fonts/LICENSE docs/font/sans 
~/naru $ git add docs/font
~/naru $ git commit -m "replaced font"
~/naru $ git push origin master
```

この修正により、セルビアの都市名について、次の通り表示が改善されました。

## 修正前
![ 2020-06-28 19 06 55](https://user-images.githubusercontent.com/18297/85944730-0cc7ff80-b974-11ea-961a-c13bbc0f6145.jpg)

## 修正後
![ 2020-06-28 19 16 12](https://user-images.githubusercontent.com/18297/85944733-18b3c180-b974-11ea-943d-0598dd1353b9.jpg)

# 16. アップロード

```zsh
~/naru $ cp -r docs ~/kokoromi-western-balkans-tiles
~/naru $ cd ~/kokoromi-western-balkans-tiles
~/kokoromi-western-balkans-tiles $ git add .
~/kokoromi-western-balkans-tiles $ git commit -m update
Auto packing the repository in background for optimum performance.
See "git help gc" for manual housekeeping.
[master e89b897380] update
...
 create mode 100644 docs/zxy/9/288/190.pbf
 create mode 100644 docs/zxy/9/288/191.pbf
 create mode 100644 docs/zxy/metadata.json
~/kokoromi-western-balkans-tiles $ git push origin master
Enumerating objects: 404882, done.
Counting objects: 100% (404882/404882), done.
Delta compression using up to 4 threads
Compressing objects: 100% (402187/402187), done.
Writing objects: 100% (404880/404880), 492.89 MiB | 2.98 MiB/s, done.
Total 404880 (delta 2063), reused 404747 (delta 1986)
remote: Resolving deltas: 100% (2063/2063), completed with 1 local object.
To github.com:optgeo/kokoromi-western-balkans-tiles
   4aa1e62dbb..e89b897380  master -> master
```

ちなみに、push すると gh-pages を作るための Action が GitHub の側で動きます。

![ 2020-06-28 20 20 20](https://user-images.githubusercontent.com/18297/85946091-e9558280-b97c-11ea-94bf-69078b70853d.jpg)

成功したタイミングで、表示がこのようになるように refresh できるようになります。

![ 2020-06-28 20 25 22](https://user-images.githubusercontent.com/18297/85946198-97f9c300-b97d-11ea-84fc-4af7147cb78a.jpg)

https://optgeo.github.io/kokoromi-western-balkans-tiles/ にアクセスすると、次のような表示がされることが確認できます。

![ 2020-06-28 20 26 27](https://user-images.githubusercontent.com/18297/85946209-bcee3600-b97d-11ea-9a62-6a8ac3e5143a.jpg)

このあと、元の目的にあったとおり、naru レベルで建物の表示を改善します。

# 変更前の `naru` の pg-buidling*.conf の実装
## [layers.conf](https://github.com/unvt/naru/blob/master/hocon/layers.conf)
```hocon
  { include pg-building-shadow }
  { include pg-building }
  { include pg-building-train-station }
  { include pg-building-outline }
```
二次元グラフィックスで三次元っぽく見せるように黒く縁取りをするということを狙った設計でした。

鉄道駅については、建物の中に鉄道が入っているパターンがあったので、**ある程度拡大すると半透過にする**という工夫をしており、そのために別レイヤとしていました。

Mapbox Style を記述する原則は「下に書くものから順番に書いていく」というものです。それぞれのレイヤの記述内容について、順番にみていきます。

なお、この layers.conf で使っているレイヤ id の命名規則は、概ね次のような感じです。

```
{ジオメトリタイプ}-{OSMタグ}-{レイヤ固有名}
```

ジオメトリタイプは、ベクトルタイルに含まれているジオメトリの型です。次の３種類にしています。

- pt: Point 
- ls: LineString
- pg: Polygon

MultiLineString は ls で、MultiPolygon は pg で扱っている場合が多いです。

OSM タグは、今回の場合は building です。naru で作るベクトルタイルは、**OSM のタグ構造を比較的そのまま素直に取り入れる**ようになっているので、Mapbox Style の filter でうまくひっかけて判別しています。他のベクトルタイルでは、クラス構造（あるいはリレーショナルデータベースのテーブル）を作ってマッピングをしていることがあるかもしれませんが、naru の基本的な考え方は、回避不可能な必要が生じるまでは、凝ったマッピングはしない、というものです。

## [pg-building-shadow](https://github.com/unvt/naru/blob/master/hocon/pg-building-shadow.conf)
```hocon
id: pg-building-shadow
type: fill
source: v
source-layer: building
minzoom: 16
filter: [
  match
  [
    geometry-type
  ]
  [
    Polygon
    MultiPolygon
  ]
  [
    match
    [
      get
      layer
    ]
    [
      "-1"
      "-2"
      "-3"
      "-4"
      "-5"
    ]
    false
    [
      match
      [
        get
        building
      ]
      [
        train_station
        roof
      ]
      false
      true
    ]
  ]
  false
]
paint: {
  fill-color: [
    rgb
    131
    124
    124
  ]
  fill-translate: [
    2
    2
  ]
}
```

このレイヤは、建物の影を描画するためのもので、`paint` にある `fill-translate` で、もとのジオメトリから (2, 2) ピクセル分ずらすことで、この後建物本体が描かれたあとに影としてみられる微量の濃色部をつくります。

## [pg-building](https://github.com/unvt/naru/blob/8436aed5d65d23cd56b90fe25627ec73baba5ae0/hocon/pg-building.conf)
執筆現在最新のものは kokoromi-ke のために fill-extrusion  を使うように改修されているので、この改修前のものをコピーすると次の通りです。

```hocon
id: pg-building
type: fill
source: v
source-layer: building
filter: [
  match
  [
    get
    building
  ]
  [
    train_station
    roof
  ]
  false
  [
    match
    [
      geometry-type
    ]
    [
      Polygon
      MultiPolygon
    ]
    true
    false
  ]
]
paint: {
  fill-color: [
    match
    [
      get
      layer
    ]
    [
      "-1"
      "-2"
      "-3"
      "-4"
      "-5"
    ]
    [
      rgb
      238
      238
      238
    ]
    [
      rgb
      222
      215
      215
    ]
  ]
}
```
train_station と roof を外すという調整はしていますが、基本的には素直なポリゴン描画のスタイルになっています。

## [pg-building-train-station](https://github.com/unvt/naru/blob/master/hocon/pg-building-train-station.conf)
```hocon
id: pg-building-train-station
type: fill
source: v
source-layer: building
filter: [
  match
  [
    geometry-type
  ]
  [
    Polygon
    MultiPolygon
  ]
  [
    match
    [
      get
      building
    ]
    [
      train_station
      roof
    ]
    true
    [
      match
      [
        get
        public_transport
      ]
      station
      true
      false
    ]
  ]
  false
]
paint: {
  fill-color: [
    match
    [
      get
      layer
    ]
    [
      "-1"
      "-2"
      "-3"
      "-4"
      "-5"
    ]
    [
      rgb
      238
      238
      238
    ]
    [
      rgb
      222
      215
      215
    ]
  ]
  fill-opacity: [
    interpolate
    [
      linear
    ]
    [
      zoom
    ]
    16
    1
    17
    0.4
  ]
}
```
fill-opacity のところに、ズームによって透過率を変えるという expression を入れているところが特徴です。つまり、
```lisp
(interpolate (linear) (zoom) 16 1 17 0.4)
```
という表現を入れることで、ズームレベルが17になったら opacity を 0.4 に下げるということをしています。

雑談になりますが、expression は Lisp で書きたくなりますね。[Mapbox Style の仕様](https://docs.mapbox.com/help/tutorials/mapbox-gl-js-expressions/#syntax)にも、次のとおり書いてあります。

> Mapbox GL JS expressions uses a Lisp-like syntax, represented using JSON arrays. 

## [pg-building-outline](https://github.com/unvt/naru/blob/master/hocon/pg-building-outline.conf)
```
id: pg-building-outline
type: line
source: v
source-layer: building
minzoom: 14
include _polygon
paint: {
  line-color: [
    rgb
    183
    174
    174
  ]
  line-width: 1
}
```
フチを強調するためのアウトラインです。

このあと、smellman から次のヒントをもらいました。

> 高さってこれかな？
https://github.com/openmaptiles/openmaptiles/blob/master/layers/building/building.sql#L101
OpenMapTilesではlevels*3.66としています。
これが「データベース」に入ってるというのがポイントです。

これをベースに、https://github.com/unvt/mame を実装しました。

建物の高さ設定の具体的なところは、https://github.com/unvt/mame/blob/master/hocon/pg-building.conf に書いてあり、その主要な部分は次の通りです。

```hocon
  fill-extrusion-height: [
    max
    [
      to-number
      [
        get
        height
      ]
    ]
    [
      "*"
      [
        to-number
        [
          get
          "building:levels"
        ]
      ]
      3.66
    ]
    5
  ]
```
Mapbox Expression の `to-number` は、数が得られないときに null を返すわけではなく、 0 や 1を返すので coalesce ではなくて max で代用しています。

# 感想
私の流儀というのは、昔から、「地理空間情報、データベース、ウェブ」という三層の真ん中をなるべく薄くして地理空間情報とウェブを直結したい、というものだと思います。データベースがボトルネックになり、またパフォーマンスとセキュリティの問題源になることを本能的に恐れる傾向があり、データの設計でデータベースの介在を回避できるというアイディアに賭ける傾向がある。手段として、データベースの代わりにストリームを持ってこようとする傾向がある。計算機科学的には、私は随分と古典的な人間なのだなあと思います。古典的で、プラクティカルであろうとする傾向があると思います。

# 原典

- https://github.com/optgeo/kokoromi-western-balkans/issues/1#issue-646218520
- https://github.com/openmaptiles/openmaptiles/blob/master/layers/building/building.sql#L101 (smellman のヘルプに感謝)