Mini Tokyo 3D × PLATEAU

この記事は 3D都市モデル Project PLATEAU Advent Calendar 2022 の17日目の記事です(既に終了しておりますが、空いている日付にエントリーさせてもらいます!)。

ここ数年開発を続けている東京の公共交通のリアルタイム3Dマップ Mini Tokyo 3D ですが、2020年末に国土交通省Project PLATEAU を発表して以来、何か面白いデータの活用ができないかと考えを巡らせておりました。今回 PLATEAU の精巧な3D都市モデルを Mini Tokyo 3D のマップ上に組み込んで表示する Mini Tokyo 3D プラグイン PLATEAU plugin for Mini Tokyo 3D の公開に至ったので、概要をご紹介したいと思います。

使い方

まずは https://minitokyo3d.com にアクセスし、画面右側の下から3つ目の「レイヤー設定」ボタンを押して、「PLATEAU」レイヤーを選択して有効にしてください。PLATEAU レイヤーは非常に負荷が高く多くのリソースを消費するため、おそらくモバイルデバイス等ではうまく動かないと思います(デフォルトで機能を無効にしているのはこのため)。快適に利用するには、ハイエンドの GPU を搭載した PC をご利用ください。あと、建築物のデータが表示されるまでにだいぶ時間がかかるので、気長にお待ちくださいね。

あとは通常の Mini Tokyo 3D の操作同様、マップ上を自由に移動したり、列車をクリックして追跡しながら周囲の風景をお楽しみください!

PLATEAU では東京都23区全域に加え、首都圏のいくつかの都市のモデルも含まれているのですが、全部表示しようとすると非常に重たくなってしまうため、現時点では千代田区中央区、港区、新宿区、品川区、渋谷区、豊島区のデータのみに絞っています。

しくみ

Mini Tokyo 3D は Mapbox GL JS をベースマップとして使用し、deck.glGeoJsonLayer を Mapbox にオーバーレイする形で地下の構造や駅のハイライトの表示に使っています。幸いなことに、Project PLATEAU では PLATEAU配信サービス(試験運用)-チュートリアルにて建築物モデル等の 3DTiles データ、地形モデルの Terraindb データ、航空写真オルソ画像タイルデータが Mapbox および deck.gl(もしくは Cesium 等の他のマップライブラリ)ですぐに使える形で配信されているため、これらをデータソースとして利用しています。いや、これは素晴らしいオープンデータの公開の取り組みですよ。その筋のプロの犯行ですね。

プラグインでは、Mapbox のラスターレイヤーで航空写真オルソ画像を表示し、deck.gl の Tile3DLayer を新たに組み込んでテクスチャ付きの建築物を表示しています。

課題と対応

少し実装上の課題と、それらへの対応についても書いておきたいと思います。

まず、PLATEAU で公開されている3D都市モデルの空間参照系は「日本測地系2011における経緯度座標系」であり、高さは「東京湾平均海面を基準とする標高」です。Mapbox や deck.gl が前提とする WGS84日本測地系2011はほぼ同一であるため、緯度経度は問題ありません。しかし、Mini Tokyo 3D では地形(Terrain)データを使わず、建築物は全て同じ平面上に配置することを前提としているため、それぞれの建築物に高さ情報を含む3D都市モデルを表示しようとすると問題が起こります。

具体的には、次の記事でも指摘されているように「モデルが浮く問題」として顕在化します。そこで、モデルの高さをマップ平面に合うように調整する必要があります。記事中では、タイルロード時に呼び出される Tile3DLayeronTileLoad コールバックの中で cartographicOrigin.z から一律40mを引くことで対処しています。しかし、これはあくまで都心沿岸部での見た目をましにするための対策なので、内陸の丘陵部では依然モデルが浮いたままです。

一つの対処法としては、標高データを使ってロードされたタイルの位置の標高分の高さを下げる、という方法が考えられます。しかし、実際やってみたところまだビルが浮いていたり、逆に沈み込んでいるのが目立ったりしていて、満足できる感じではありませんでした。タイルの中心座標の標高を使って調整するのですが、これだとタイルが表現する領域内の起伏をうまく反映できるとは限らず、たまたま中心座標の標高が周囲より突出している場合にモデル全体が浮いてしまうという状況が起こりやすくなります。

そこで、やや手間がかかるのですが、onTileLoad の中でアクセスできるバイナリデータ content.batchTableBinary.buffer の中から、建築物毎の最小の高さが入っている部分の数列を読み出し、ソートした上で中央値を取り出します。これにより、タイルに含まれる建築物の基礎の統計的な標高の目安がわかります。さらに、タイルロード時にセットされている cartographicOrigin.z は標高ではなく楕円体高のようなので、建築物の基礎の標高に加えて東京駅付近のジオイド高 36.6641m を引いてやります。

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.6641 + zMins[Math.floor(len / 2)];
}

するとどうでしょう、赤レンガの東京駅が地面にぴったり。少し内陸の渋谷のハチ公前もばっちりです。もちろんジオイド高も場所によって変わりますが、首都圏平野部ならあまり目立たない誤差の範囲に収まるかと思います。

次に課題になったのは、Tile3DLayer で単純に建築物を表示しただけだと、全体的に暗い色調になってしまうことです。これは deck.gl のグローバルなライティングの設定によるものだろう、ということで deck.gl の初期値を調べてみると

  • AmbientLight(環境光): 強さ 1.0
  • DirectionalLight 1(平行ライト): 強さ 1.0
  • DirectionalLight 2(平行ライト): 強さ 0.9

ということだったので、これはもっと強力なライトを当てないといかん、そして Mini Tokyo 3D では時間経過とともに色合いが変わる地図にしているため、ライトの色もそれに合わせないといかん、ということでそれを実装してみました。調整の結果、最終的には

  • AmbientLight: 強さ 3.0
  • DirectionalLight: 強さ 9.0

になり、これに加えて航空写真オルソのラスターレイヤーも、時間に応じて raster-brightness-max プロパティを変化させて明るさを調整しています。

さらに、Mini Tokyo 3D では Mapbox の Sky レイヤーを使って空に太陽を表示しているため、上の DirectionalLight の光の方向が太陽の位置と同期していたら良さげになるはずです。ということで、SunCalc ライブラリを使って Unix Time、緯度経度から天球上の太陽の位置を取得し、DirectionalLight の方向を都度設定するようにしました。

const now = Date.now();
const {lat, lng} = map.getCenter();
const sunPos = SunCalc.getPosition(now, lat, lng);
const azimuth = Math.PI + sunPos.azimuth;
const altitude = -sunPos.altitude;

directionalLight.direction = [
    Math.sin(azimuth) * Math.cos(altitude),
    Math.cos(azimuth) * Math.cos(altitude),
    -Math.sin(altitude)
];

都心から望む夕日の雰囲気もパーフェクトです。ちゃんとビル群に夕日の光の反射が見えます。

Mini Tokyo 3D、PLATEAU Plugin for Mini Tokyo 3D は GitHub で公開されているので、チェックしてね!