p5.jsでお手軽3次元データビジュアライズ（PLATEAU 3D都市モデル編）

本記事は、[Processing Advent Calendar 2022](https://adventar.org/calendars/7370) 兼 [PLATEAU Advent Calendar 2022](https://qiita.com/advent-calendar/2022/plateau)の記事です。

毎年恒例の、お手軽3次元データビジュアライズするシリーズ。今回は、p5.jsでPLATEAU3D都市モデルを表示します。

# はじめに
[PLATEAU（プラトー）](https://www.mlit.go.jp/plateau/)は、国土交通省が整備を進めている、日本全国の3D都市モデルのオープンデータです。
![p.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80141/04b6ee38-02e3-05eb-8d34-e43f70bb09a4.png)

[PLATEAUのオープンデータ](https://www.mlit.go.jp/plateau/open-data/)にはさまざまなものが含まれますが、今回の記事では、建物や構造物の3D都市モデルデータをp5.jsに取り込んで3次元表示する方法を紹介します。

この記事の執筆時点では、3D都市モデルは全国をカバーしていませんが、着々と進んでいる印象です。今回は東京23区の3D都市モデルデータをダウンロードして、新国立競技場周辺（新宿区の一部）の建物を可視化してみます。


__完成例__

https://openprocessing.org/sketch/1781244






# 前提条件
マウスで視点操作を行うので、ライブラリはPeasyCamを使います。openprocessingであれば、エディター右側のペインで「p5.easycam.min.js」にチェックを入れてください。

openprocessing以外でライブラリ本体を使う場合はこちら
* [p5.easycam.min.js](https://github.com/freshfork/p5.EasyCam)

# 使用するデータの準備

[PLATEAUのオープンデータ](https://www.mlit.go.jp/plateau/open-data/)で「東京23区」を選び、[OBJファイル](https://www.geospatial.jp/ckan/dataset/plateau-tokyo23ku)を選択しダウンロードします。23区全体の建物の3Dデータなのでかなりファイルサイズが大きいです。

解凍したフォルダの「13100_indexmap_op.pdf」を参照して区画のIDを調べます。今回は新国立競技場周辺のデータを使用するのでIDは「53394517」です。

![index.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80141/e2c62ed9-82d9-532e-2b66-24f307d94185.png)


解凍したフォルダの「bldg」フォルダに建築物の3Dデータが入っています。「lod1」「lod2」がありますが、今回は「lod2」の高精細なモデルデータを使用します。

「lod2」の中にある「53394517」で始まるフォルダを探します。「53394517_bldg_6677_obj」フォルダの中に今回使用するモデルデータ「53394517_bldg_6677.obj」が入っています。

このobjファイルはp5.jsから読み込める場所にアップロードしてください。openprocessing.orgでスケッチを作る場合は、右ペインの「files」にアップロードしておきます。

![op.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80141/27518f49-25eb-35fc-f357-2e097d3aa57e.png)

# p5.jsで読み込んで表示

## 1. objファイルの中身を調べる

まずは、読み込みたいobjファイルをテキストエディタで開いて、先頭の頂点の座標を調べます。具体的には、「53394517_bldg_6677.obj」をテキストエディタで開くと、次のようになっています。行頭に「v」があるデータが頂点の座標です。vの後にスペース区切りで3つの数値がありますが、それぞれ頂点のx座標、y座標、z座標です。


```
# Created with FME Version: FME(R) 2021.1.0.0 20210630 - Build 21607 - WIN64
mtllib materials.mtl
# COORDINATE_SYSTEM:  OGC_DEF PROJCS["JGD2011 / Japan Plane Rectangular CS IX",GEOGCS["JGD2011",DATUM["Japanese_Geodetic_Datum_2011",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","1128"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","6668"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",36],PARAMETER["central_meridian",139.833333333333],PARAMETER["scale_factor",0.9999],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","6677"]]
# Number of Geometry Coordinates  : 11977
# Number of Texture  Coordinates  : 27870
# Number of Normal   Coordinates  : 0
v -10857.893105 -35428.917336 26.677082
（以下略）

```
## 2. p5.jsのコードを書く

コードは下記を参照してください。大まかな流れとしては下記の通りです。
1. preloadブロックでobjファイルを読み込む
1. setupブロックで peasyCamを初期化
1. drawブロックでobjをモデルとして表示

objをモデルとして表示する際、translate()で原点を移動しますが、その際、前の手順で調べた頂点の座標の正負を逆にした値を入れてください。ただしz座標は0、または、0から任意に少しずらした値にしておきます。


```javascript:sketch.js
//the 3D model data is distributed by PLATEAU
// objファイルの入手先
// https://www.geospatial.jp/ckan/dataset/plateau-tokyo23ku
let obj1;
var easycam;

function preload(){
  obj1 = loadModel('53394517_bldg_6677.obj');
}

function setup() {
  createCanvas(windowWidth, windowHeight, WEBGL);
  
  easycam = createEasyCam();
  
  fill(255);
  noStroke();
}
	
function draw() {
	
  background(0);
  ambientLight(128);
  directionalLight(200, 200, 190, 0.01, 1, 0.1);
	rotateX( radians(90) );
	
	//objファイルをテキストエディタで開き、先頭の頂点vのx座標とy座標の正負を逆転した値をtranslateのx座標とy座標に入れる
	//例：　v -10857.893105 -35428.917336 26.677082　の場合
	//translate(10857.893105, 35428.917336, -100); とする（ただしzは任意なのでここでは-100とする）
  translate(10857.893105, 35428.917336, -100);
	
	//ノーマル表示
	fill(200);
	noStroke();
	
	//ワイアーフレームで表示
	//stroke(0,164,255,128);
	//strokeWeight(.1);
	//noFill();
	
  model(obj1);
}

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  easycam.setViewport([0,0,windowWidth, windowHeight]);
}
```

# 結果

このように、新国立競技場周辺の建物が3次元空間上に表示できています。

![normal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80141/d13aef62-f558-e5a9-69ef-63bffdf39046.png)

ワイアーフレームで表示しても面白いかと思います。

![wire.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80141/74f94d20-7142-0ea2-0463-a07651d45ac7.png)

同じような方法で4区画分を並べて表示するとそれなりに街並みの再現も可能です。こちらの例では「lod1」のobjを4区画分使って新宿駅周辺を再現しています。

https://editor.p5js.org/masakiyamabe@gmail.com/sketches/1CAHUulbEs

![shinjyuku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80141/1da6b9a8-0b00-9f67-e045-0894618502d6.png)




# 終わりに

オープンデータを使って、データビジュアライズとクリエイティブコーディングを融合させる表現はまだまだ探究の余地がありそうです。ぜひ面白い作品にトライしてください。

# 追記（2023/01/02）

より発展させた表現をひとつご紹介します。

本記事では、plateauのobjファイルをp5.jsのloadModel()メソッドで読み込み、3次元モデルとして表示しましたが、objファイルは、荒っぽい言い方をすれば頂点と放線を集めたテキストデータなので、loadString()メソッドでテキストデータとして頂点座標をパースすることもできます。

objから頂点座標を抜き出してp5に点群を配置し、夜景のようなイメージを作ってみました。

https://twitter.com/masakick/status/1609703075702509569

最終形のコードは以下で公開しています。

https://openprocessing.org/sketch/1782887

自由に改変して楽しんでみてください。