3D都市モデルPLATEAU 3DTiles+GeoJSONで3D主題図を作る

# はじめに
:::note info
この記事は [3D都市モデル Project PLATEAU Advent Calendar 2023](https://qiita.com/advent-calendar/2023/plateau) 2日目の記事です。
:::
[3D都市モデル PLATEAU LT 05](https://asciistartup.connpass.com/event/294152/)で[@_shimizu](https://twitter.com/_shimizu)さんが発表されていた、[deck.glのTerrainExtension](https://deck.gl/docs/api-reference/extensions/terrain-extension)を用いた[事例](https://docs.google.com/presentation/d/1bvh8Ddd7JYN4QibBQE7127psRAqDJUlXgLnq7RDnVag/edit#slide=id.g294ab4db98d_2_10)を参考にさせていただきながら、3D都市モデルPLATEAU 3DTiles+GeoJSONで3D主題図を作成してみました。具体には、[3D都市モデルPLATEAU 3DTiles](https://github.com/Project-PLATEAU/plateau-streaming-tutorial)と[国土数値情報の用途地域データ（2019年）](https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-A29-v2_1.html)を組み合わせて、用途地域名で色分けした3D主題図を作成してみました。

# アウトプットイメージ
https://x.com/shi__works/status/1720689201572905017?s=20

# [deck.gl](https://deck.gl/)とは
- オープンソースの地図ライブラリーです。
- WebGLによる可視化フレームワークで、大規模なデータセットをサポートしており、2Dおよび3Dデータの可視化を行えます。
- もともとはUber社が開発していましたが、現在はUrban Computing Foundationに移管されています。
- 可視化のための様々な[レイヤー](https://deck.gl/examples)が用意されています。

# [Tile3DLayer](https://deck.gl/docs/api-reference/geo-layers/tile-3d-layer)
- 3Dタイルデータ（3DTilesなど）を読み込み、地図上に表示するレイヤです。
- 3D都市モデルPLATEAU 3DTilesを表示するために使用します。

https://deck.gl/docs/api-reference/geo-layers/tile-3d-layer

# [GeoJsonLayer](https://deck.gl/docs/api-reference/layers/geojson-layer)
- GeoJSONデータを読み込み、地図上に表示するレイヤです。
- 国土数値情報の用途地域データを表示するために使用します。

https://deck.gl/docs/api-reference/layers/geojson-layer

# 2D主題図と3D主題図の比較
- 通常、3D都市モデルPLATEAU 3DTilesと国土数値情報の用途地域データ（2019年）の2つのレイヤをdeck.glで重ねて表示すると下図のようになり、用途地域の2Dデータの一部が建物の3Dモデルに隠れてしまいます。

**2D主題図**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205045/5c450413-6f4c-1da7-5172-74b3b6cea7c5.png)

- 一方で、[deck.glのTerrainExtension](https://deck.gl/docs/api-reference/extensions/terrain-extension)を用いると、用途地域の2Dのデータが建物の3Dモデルのサーフェイスに沿ってレンダリングされ、3Dモデルのサーフェイスに2Dのデータがオーバレイされます。

**3D主題図**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205045/859bf22d-0709-04d8-e4ec-f8a3915abe17.png)

:::note warn
ただし、TerrainExtensionは、現時点では実験的な位置付けの機能です。また、負荷が高く、巨大なGeoJSONをオーバレイすると固まります。
:::

# デモサイト

https://shi-works.github.io/PLATEAU-3DTiles-by-deck-gl-TerrainExtension/

# ソースコードと解説
- 全体のソースコードは、下記のGitHubから入手してください。

https://github.com/shi-works/PLATEAU-3DTiles-by-deck-gl-TerrainExtension

## Tile3DLayer
- `onTileLoad`以下は、3Dモデルの高さをマップ平面に合うようにするための調整です。
- 詳細については、下記のブログ記事が詳しいです。

https://nagix.hatenablog.com/entry/2022/12/27/203825

- `operation: 'terrain+draw'`とすることで、deck.glのTerrainExtensionが機能します。

```javascript:javascript
                // PLATEAU-3DTiles
                new deck.Tile3DLayer({
                    id: "tile3dlayer",
                    data: 'https://plateau.geospatial.jp/main/data/3d-tiles/bldg/13100_tokyo/13104_shinjuku-ku/low_resolution/tileset.json', // 新宿区
                    loader: Tiles3DLoader,
                    opacity: 1,
                    pickable: false,
                    onTileLoad: d => {
                        const { content } = d;
                        const buffer = content.batchTableBinary.buffer;
                        const key = content.batchTableJson;
                        const len = key._gml_id.length;
                        const zMinView = new DataView(buffer, key._zmin.byteOffset, len * 8);
                        const zMins = [];

                        for (let i = 0; i < len; i++) {
                            zMins.push(zMinView.getFloat64(i * 8, true));
                        }
                        zMins.sort((a, b) => a - b);
                        content.cartographicOrigin.z -= 36.9702 + zMins[Math.floor(len / 2)];
                    },
                    operation: 'terrain+draw'
                }),
```

# GeoJsonLayer
- `getFillColor` [国土数値情報の用途地域データ](https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-A29-v2_1.html)の属性「用途地域コード（A29_004）」をもとに、色分けを行っています。
- 用途地域コード（A29_004）のコード表は下記を参照してください。

https://nlftp.mlit.go.jp/ksj/gml/codelist/UseDistrictCd.html

- `extensions: [new deck._TerrainExtension()]`とすることで、deck.glのTerrainExtensionが機能します。
```javascript:javascript
                // 用途地域（TerrainExtension）
                new deck.GeoJsonLayer({
                    id: 'youtochiiki-1',
                    data: data,
                    stroked: false,
                    filled: true,
                    getFillColor: ({ properties }) => {
                        const { A29_004 } = properties;
                        if (A29_004 == 1)
                            return [0, 178, 133]
                        else if (A29_004 == 2)
                            return [145, 207, 185]
                        else if (A29_004 == 3)
                            return [120, 206, 63]
                        else if (A29_004 == 4)
                            return [173, 223, 33]
                        else if (A29_004 == 5)
                            return [235, 238, 94]
                        else if (A29_004 == 6)
                            return [255, 210, 182]
                        else if (A29_004 == 7)
                            return [255, 166, 56]
                        else if (A29_004 == 8)
                            return [255, 176, 195]
                        else if (A29_004 == 9)
                            return [255, 89, 61]
                        else if (A29_004 == 10)
                            return [167, 148, 197]
                        else if (A29_004 == 11)
                            return [185, 234, 255]
                        else if (A29_004 == 12)
                            return [95, 197, 251]
                        else if (A29_004 == 21)
                            return [196, 215, 155]
                    },
                    opacity: 0.5,
                    extensions: [new deck._TerrainExtension()]
                }),

```

# おわりに
deck.glのTerrainExtensionを用いて、3D都市モデルPLATEAU 3DTiles+GeoJSONで3D主題図を作成してみました。deck.glのTerrainExtensionを使用すると、主題図と3Dモデルを組み合わせて、様々な表現が可能です。例えば、国土数値情報の用途地域データには、用途地域名のほかに容積率などの属性を持っていますので、3D都市モデルPLATEAU 3DTilesと組み合わせて、容積率マップを作ってみるなど、面白そうな試みができそうです。容積率マップは、にゃんこそば（[@ShinagawaJP](https://twitter.com/ShinagawaJP)）さんの書籍で紹介されていました。

https://x.com/ShinagawaJP/status/1725468837267579077?s=20