AMCI Develop Note #00 PLATEAUモデルを表示するまで


はじめに

本解説ではPLATEAUで公開しているCityGML(OBJ)データをWebサイト上で使用する方法を、AMCIでの事例を参考に説明していきます。本解説では3DとHTML、JavaScript(Three.js)の基礎的な知識が必要です。予めご注意ください。

AMCI

本解説における推奨の開発環境は以下の通りです。

  • OS: Windows10、MacOS10
  • CPU: Corei7 相当
  • GPU: GTX1060 相当
  • メモリ: 8GB
  • ブラウザ: 最新のGoogle Chrome、Microsoft Edge など

※上記以上のスペックを推奨しています

データのダウンロード

データは G空間情報センター(https://www.geospatial.jp/gp_front/) のサイトからダウンロードを行います。本解説では東京駅周辺のエリアをサンプルとして利用します。
東京都のOBJデータの公開ページは下記リンクを参照ください。

3D都市モデル(Project PLATEAU)東京都23区(OBJ 4次メッシュ 2020年度)
https://www.geospatial.jp/ckan/dataset/plateau-tokyo23ku-obj4-2020

ページ内の「東京都23区構築範囲拡大図2/4」を選択すると、東京23区の北東エリアを区切ったマップが表示されます。この中の東京駅付近の番号を探します。
大丸有と名前がつけられたエリアの「53394611」のデータを利用したいので、このデータのダウンロードページ(533946)へ移動し、ページ右上のダウンロードボタンをクリックします。

データの解凍

ダウンロードしたzipデータを解凍すると、下記のようなデータが含まれているので、その中からLOD1.zipを解凍しそのフォルダをクリックします。この中にはエリア毎に細かく分かれた建物のデータが含まれます。続いてこのデータをブラウザ上で表示してみます。

Zipデータに含まれるもの

  • brid.zip: 橋や陸橋などのデータ
  • dem.zip: 数値標高モデル(地面データ)
  • LOD1.zip: 簡略化した建物データ
  • LOD2.zip: 形状が細かくテクスチャも付いた建物データ

3Dデータの調整

ダウンロードした3Dデータをブラウザで表示する前に、3Dデータの座標を調整する必要があります。PLATEAUの3Dモデルは細かくエリアやカテゴリ(ビル、地面、橋など)に分かれており、それらは組み合わせることで街の再現などが行なえます。
ここで、1点問題があります。
単純に1つの区画だけをブラウザなどで表示する場合は、3Dモデルの左右と奥行きの座標が中央から離れているため、そのまま表示させようと思っても画面外に表示されてしまいます。
なので、一度3Dソフトなどでデータを開き、中央に座標を移動させたデータをJavaScriptなどで読み込む必要があります。
具体的な手順はここでは割愛しますが、Blenderなどの3Dソフトで調整を行った上で利用をおすすめします。

ブラウザでの表示方法

3Dデータをブラウザで表示するためには、Three.jsという3Dの描画ライブラリを利用します。Three.jsについての説明は下記ページを参照ください。

最新版で学ぶThree.js入門 手軽にWebGLを扱える3Dライブラリ(ics.mediaより)
https://ics.media/entry/14771/

続いて、Three.jsの使い方を説明します。まずはThree.jsライブラリを読み込む必要があるのでこちらのファイル( https://threejs.org/build/three.js )をご自分のPCにダウンロードしてください。次にダウンロードしたthree.jsファイルと同じ階層に、下記のHTMLを作成します。

index.html
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>My first three.js app</title>
		<style>
			body { margin: 0; }
		</style>
	</head>
	<body>
		<script src="three.js"></script>
		<script>
			// ここにコードを記述します
		</script>
	</body>
</html>

このHTMLをGoogle ChromeなどのWebブラウザで開き、エラーなどが出ていなければ準備完了です。

OBJデータを表示する

Three.jsでOBJデータを扱うには、OBJ loaderというクラスを読み込む必要があります。こちらはThree.jsの拡張機能のようなもので、他にもFBX loaderやFont loaderなど様々なものが用意されています。
こちらはGitHubのThree.jsのリポジトリ内からダウンロードができますが、最近のバージョンはWebpackなどでの利用を想定して作られているので、今回のサンプルでは過去バージョンのOBJ loaderを使用します。ファイルはサンプルに含めているので、そちらからご利用ください。

使用例: OBJ loader
https://threejs.org/examples/#webgl_loader_obj

index.html
  <script src="three.js"></script>
  <script src="OBJLoader.js"></script>

上記のようにスクリプトの読み込みに1行加えます。必ずThree.js本体の読み込みの後に行うようにしてください。
そして下記がThree.jsでPLATEAUの3Dモデルを描画するソースコードです。行っている処理についてはコメントを参照してください。

また、最近のGoogle Chromeでは「file:///Users/〜」などではじまるアドレスではデータ(3Dデータなど)の読み込みが行えないので、ローカル環境で動作させたい場合はローカルホストを立てる必要があります。
PythonやPHPから、「python -m SimpleHTTPServer 8000」や「php -S localhost:8000」などのコマンドで簡単にローカルホストを立てる事ができます。ご利用の環境によってコマンドは違う可能性もあるので、ご自身の環境で動作するコマンドをお試しください。

index.html
<script>
// 変数の用意
let container;
let camera, scene, renderer;
let mouseX = 0, mouseY = 0;
let windowHalfX = window.innerWidth / 2;
let windowHalfY = window.innerHeight / 2;
let object;

init();
animate();

function init() {
  // HTMLにCanvasを追加
  container = document.createElement('div');
  document.body.appendChild(container);

  // カメラを追加
  camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
  camera.position.z = 250;

  // scene
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0x333333);

  // ライトを追加
  const ambientLight = new THREE.AmbientLight(0xcccccc, 0.4);
  scene.add(ambientLight);
  const pointLight = new THREE.PointLight(0xffffff, 0.8);
  camera.add(pointLight);
  scene.add(camera);

  // 地面を追加
  const geometry = new THREE.PlaneGeometry( 1000, 1000 );
  const material = new THREE.MeshBasicMaterial( {color: 0x666666, side: THREE.DoubleSide} );
  const plane = new THREE.Mesh( geometry, material );
  plane.rotation.x = 90 * Math.PI / 180;
  scene.add( plane );

  // 3Dモデルを読み込み
  const loader = new THREE.OBJLoader();
  loader.load('model/53394610_bldg_6677_center.obj', function(obj) {
    object = obj;
    object.scale.x = 0.3;
    object.scale.y = 0.3;
    object.scale.z = 0.3;
    scene.add(object);
  });

  // レンダラーを追加
  renderer = new THREE.WebGLRenderer();
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  container.appendChild(renderer.domElement);

  // イベントを追加
  document.addEventListener('mousemove', onDocumentMouseMove);
  window.addEventListener('resize', onWindowResize);
}

// ブラウザのリサイズ時に実行
function onWindowResize() {
  windowHalfX = window.innerWidth / 2;
  windowHalfY = window.innerHeight / 2;
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

// マウスムーブ時に実行
function onDocumentMouseMove(event) {
  mouseX = (event.clientX - windowHalfX) / 2;
  mouseY = (event.clientY - windowHalfY) / 2;
}

// 毎フレーム実行
function animate() {
  requestAnimationFrame(animate);
  render();
}

function render() {
  camera.position.x += (mouseX - camera.position.x) * .05;
  camera.position.y += (-mouseY - camera.position.y) * .05;
  camera.lookAt(new THREE.Vector3(0, 10, 0));
  renderer.render(scene, camera);
}
</script>