はじめに
先の年末年始に地理院地図Vector/最適化ベクトルタイルに標高値を与えて、deck.gl+Mapbox GL JS/MapLibre GL JS で表示させるという取り組みをしていました。
こちらの記事で紹介しているサイトは、その場で 3D 用データを作成して表示することにフォーカスしていましたが、今回は、その 3D の表示を予め タイル分割 された 独自形式 のデータ(JSON)として作っておいて、それを読み込むためのローダーを作ったのでメモしておきます。
なお、以前(4年前)も同じような試行をしていましたが、今回は、ChatGPT を活用し、自分が考えるよりも性能のいいやつをすぐに作ってもらえました。
デモサイトはこちら
※神戸市新開地駅~谷上駅付近のサンプルデータを表示可能(ZL13 以上で表示)
機能等の説明
前提とする情報
今回のサイトでは、ZL 14 のタイル単位に分割した独自形式の JSON を、地図の動きに合わせて読み込み、deck.gl の PathLayer 等へ設定の上、MapLibre 上で表示させています。
利用しているライブラリは、MapLibre v5+deck.gl v9 の組み合わせです。
参考ですが、独自形式のデータは、GeoJSON を拡張したようなものとなっています。以下の通り、3次元座標を持つ GeoJSON を、データ種別ごとにメンバーとしてまとめています。(※ Polygon のみ各頂点は2次元で、高さは属性値で代用しています。)
{
"road": [
{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[135.08746, 34.75884, 290],
...
[135.08721, 34.75875, 320]
],
},
"properties": {...}
},
...
], // 道路の Line 型の GoeJSON (Feature の配列)
"railway": [...], // 鉄道の Line 型の GoeJSON (Feature の配列)
"symbol": [...], // 記号・注記の Point 型の GoeJSON (Feature の配列)
...
"building": {...} // 建物の Polygon 型の GoeJSON (FeatureCollection)
}
タイルローダー
今回のメイン機能である、地図の動きに合わせてタイルを取得するためのローダーの機能については、地図の動きが止まった時(moveend
イベント発生時)に使われることを想定し、以下のような流れになっています。
- 地図の範囲から必要なタイル番号を割り出し、
- JSON データを必要な分だけ取得してい、
- 一式データをマージして次の工程へ渡す
これは、MapLibre v5+deck.gl v9 の組み合わせでは、deck.gl の MapboxOverlay と Mapbox GL JS/MapLibre GL JS の addControl()
を用いる必要があるのですが、MapboxOverlay を更新するには、いったん削除する必要があるので、ローダーにおいても、データを都度追加するのではなく、一式取得してマージしなおす形に割り切っています。
なお、タイルデータ一式を取得しなおすのは無駄であるため、キャッシュ機能も有しています(ChatGPT にオーダーした結果をそのまま実装しています……)。
高縮尺用差分データ
また、デモサイトにおいては、3D 表示におけるちょっとした工夫として、ズームを上げると詳細なデータの差分がロードされるという工夫もしています。これは、3D Tiles の実装なども参考にしています。
具体的には、今回のデモサイトでは、基本的に「多くの建物が省略されているデータ」(最適化ベクトルタイル ZL14 由来)を表示していますが、一部地域(すずらん台駅周辺)については、ZL 16 以上にズームした際に、「詳細な建物データ」(最適化ベクトルタイル ZL 16 由来)を 差分として追加 で読み込み、表示するようにしています。既に ZL 16 未満から表示されている等高線や道路等のデータは差分データには含まれておらず、ZL 16 未満用のものをそのまま流用することで全体のデータ量を抑えています。
- ZL 16 未満では、建物データは省略している
- ZL 16 以上では、詳細な建物データを差分として追加で読み込み、表示している(建物データ以外は、ZL 16 未満用で読み込んだものをそのまま用いている)
課題
いったん解消しました(2025/03/25)。
以下のような課題を認識しています。
注記の文字が、建物(MapLibre のネイティブレイヤ(fill-extrusion)使用)よりも下に表示されてしまう動くたびにレイヤを一式作成しなおしているため、動かすたびにデータが一瞬消えてしまってうるさい
レポジトリ
メインのローダー機能は TileLoader
クラスに集約されています。
感想
ChatGPT の力を借りることで、タイルローダーをかなり簡単に実装できました。デモサイトのパフォーマンスとしては、データ形式がかなり雑で肥大化したものでありながら、(データの重さを考慮すれば)意外とさくさく動いているような気がします。