この記事は MapLibre Advent Calendar 2025 14日目の記事です。
はじめに
- 本記事では、新宿駅周辺屋内地図オープンデータ(令和2年度更新版) をもとに、屋内フロア別の GeoJSON(3次元座標) を生成し、歩行空間ネットワーク(リンク)と合わせて 3D化するまでの一連のデータ処理パイプラインを紹介します。
- さらに、作成した GeoJSON(3次元座標) を MapLibre GL JS と deck.gl を用いて、屋内3Dマップとして表示します。
使用データ
1. 新宿駅周辺屋内地図オープンデータ(令和2年度更新版)
- 新宿駅周辺屋内地図オープンデータは、国土交通省が実施している「高精度測位社会プロジェクト」の実証実験において作成された屋内地図の 令和2年度更新版です。
- 実証実験に用いた屋内地図のうち、通路・部屋の範囲などの基盤となる地図、歩行空間ネットワークデータ、トイレ・エレベータ等の公共設備POIデータが公開されています。
- なお、本データは、国土地理院の「階層別屋内地理空間情報データ仕様書(案)」(平成30年3月)を基に作成されています。
- 屋内地図データのディレクトリ構成は、大きく ShinjukuTerminal(屋内地図) と nw(歩行空間ネットワークデータ) の2つです。
新宿駅周辺屋内地図オープンデータ_統合版(Shapefile)
│ meta.xml
│
├─nw
│ Shinjuku_link.shp
│ Shinjuku_node.shp
│
└─ShinjukuTerminal
ShinjukuTerminal_<Floor>_<Layer>.shp
屋内地図(ShinjukuTerminal ディレクトリ)
- 屋内地図は フロア × レイヤー種別 の組み合わせで Shapefile が分割されています。
- ファイル名のパターンは以下の通りです。
ShinjukuTerminal_<Floor>_<Layer>.(shp / dbf / prj / shx)
<Floor>:B3, B2, B1, 0, 1, 2, 2out, 3, 3out, 4, 4out
<Layer>:Drawing / Facility / Fixture / Floor / Space / Opening / TWSILine / TWSIPoint
使用データ
本記事で 3D 化の対象としたデータは、ShinjukuTerminal ディレクトリ内の以下の Shapefile です。
- Floor(階層面)
- Space(通路・部屋など空間)
- Fixture(柱・設備)
- Opening(出入口)
これらをフロアごとに読み込み、2D → 3D(Z座標付与) に変換します。
(Drawing / Facility / TWSI は今回の 3D 表示では使用しません)
歩行空間ネットワークデータ(nw ディレクトリ)
- 歩行者ネットワークは ノード(点) と リンク(線) で構成されます。
- ファイル名のパターンは次の通りです。
Shinjuku_<type>.(shp / dbf / prj / shx)
<type>:
- node:ノード(交点・階段の開始点・EV乗り場などの重要地点)
- link:リンク(ノード間の通行可能な経路)
使用データ
本記事で 3D 化に使用するネットワークデータは次の 2 つです。
-
Shinjuku_node.shp(ノード)
- 位置(2D座標)と
ordinal(階層番号)を保持 -
ordinal → フロア → Zの計算に利用 - ノードの 3D Point を生成
- 位置(2D座標)と
-
Shinjuku_link.shp(リンク)
- ノード間の通行可能経路
- 起点・終点ノードの Z を参照し、線形補間で 3D LineString を生成
2. 3D都市モデル(Project PLATEAU)豊島区(2023年度) | 土地利用モデル
- 屋内3Dマップの「平面(地表)」を表現するために、土地利用モデルを使用しています。
- 土地利用モデルは、都市計画基礎調査の土地利用現況が出典です。
- PLATEAU GIS Converter を用いて CityGML から GeoJSON に変換しています。
- 変換後 GeoJSON の座標参照系は WGS 84(EPSG:4979) です。
使用するスクリプト等
使用するスクリプトやHTML、作成データ等は、以下の GitHub レポジトリに格納しています。
https://github.com/shiwaku/mlit-shinjuku-indoor-3dmap-on-maplibre
1.make_shinjuku_3d_geojson.py
2.merge_shinjuku_floor_geojson.py
4.shinjuku_network_3d_export.py
index.html
屋内地図および歩行空間ネットワークデータの3次元化
本記事で用いる Z 値は、実際の建築物の床高を再現したものではありません。
フロア構造と歩行ネットワークの立体関係を分かりやすく可視化するための 仮想的な高さ(Z値) です。
フロア別 Z 値の設定方法
まず、新宿駅周辺の地盤高を代表する値として 標高 37.5m を基準値(BASE_Z)に設定します。
BASE_Z = 37.5 # 新宿駅の基準標高(可視化用)
次に、各フロアに対して BASE_Z からの 相対オフセットを定義し、地下階は負、地上階は正の値にします。
FLOOR_OFFSETS = {
"B3": -35.0,
"B2": -25.0,
"B1": -15.0,
"0F": 0.0,
"1F": +15.0,
"2F": +25.0, "2out": +25.0,
"3F": +35.0, "3out": +35.0,
"4F": +45.0, "4out": +45.0,
}
各フロアの Z 値は次の式で決定します。
Z = BASE_Z + FLOOR_OFFSETS[floor_label]
ここで設定しているオフセット値は、実際の階高(数 m)を正確に表現するものではなく、次を優先して 意図的に誇張しています。
- フロア同士が 3D 空間上で重ならないこと
- MapLibre / deck.gl 等の Web地図 表示で視認しやすいこと
また、2out や 3out などの屋外通路は、対応する屋内フロアと同じ高さに設定しています。
この Z 値定義は屋内地図データと歩行空間ネットワークデータで共通に使用し、フロア形状とネットワークが 3D 空間上で正しく対応することを保証します。
1. Shapefile からフロア別 GeoJSON(3次元座標)を生成
使用するスクリプト: 1.make_shinjuku_3d_geojson.py
処理フロー
屋内地図 Shapefile 群を読み込み、以下を行います。
-
./shape配下のすべての Shapefile を走査 -
ファイル名からフロアラベルを推定(例:
_b2,_1,_2out→ B2, 1F, 2out) - フロアラベルに応じて Z を決定(
Z = BASE_Z + FLOOR_OFFSETS[floor]) - ジオメトリを 3D 化(
[x, y] → [x, y, z]) - メタ情報を付与(
__floor,__z_abs) -
*.3d.geojsonとして出力
フロア推定の例
filename = "shinjuku_b2_polygon.shp"
floor = "B2" # 正規表現で抽出
Z = BASE_Z + FLOOR_OFFSETS[floor]
ジオメトリ3D化のイメージ
new_coords = [(x, y, z) for (x, y) in original_coords]
- Point / LineString / Polygon / MultiPolygon / MultiLineString に対応
- shapely の
make_valid()で壊れたジオメトリを救済 - 必要に応じて WGS84(EPSG:4326)へ変換可能
ポイント
- ファイル名でフロアを推定することで、Shapefile 側にフロア属性が無くても扱える
- 3D化時にメタデータを付与し、可視化・解析で使いやすくする
2. フロア別 × ジオメトリタイプ別に GeoJSON(3次元座標)をマージ
使用するスクリプト: 2.merge_shinjuku_floor_geojson.py
目的
クライアント側で扱いやすいよう、次の粒度で GeoJSON をマージします。
- フロア別(B3, B2, B1, 0F, 1F, 2F, 2out…)
- ジオメトリ種別別(points / lines / polygons)
処理フロー
-
./geojson配下の 3D GeoJSON を読み込み -
ファイル名から
- フロアラベルの推定
- 空間種別(Space / Floor / Fixture…)の推定
-
MERGE_MODE = "per_floor_by_geom"を利用 -
Feature のジオメトリタイプで分類
- Point → points
- Line → lines
- Polygon → polygons
-
次のような出力を得ます。
Shinjuku_B1.polygons.geojson
Shinjuku_B1.lines.geojson
Shinjuku_B1.points.geojson
...
メタ情報の保持
Feature に __source_file を付与し、「どの Shapefile → どの Feature か」 を追跡できるようにしています。
3. 歩行者ネットワーク(node / link)の3D化
使用するスクリプト: 4.shinjuku_network_3d_export.py
3.1 node の3D化
-
Shinjuku_node.shpを読み込み -
ordinal(階層番号)→floor_label(B3, B2, B1, 0F, 1F…)へ変換 -
Z = BASE_Z + FLOOR_OFFSETS[floor_label]で高さ決定 - 3D Point として GeoJSON 化
ordinal → floor の一例
| ordinal | floor_label |
|---|---|
| -3 | B3 |
| -2 | B2 |
| -1 | B1 |
| 0 | 0F |
| 1 | 1F |
3.2 link の3D化(線形補間)
- link の
start_id/end_idから node の高さを join - LineString の各頂点に対して Z を線形補間して付与
- MultiLineString にも対応
-
shinjuku_link_3d.geojsonを出力
線形補間の式
Z(t) = z_start + (z_end - z_start) * (dist(t) / dist_total)
階段やスロープ等のリンクも、自然な 3D ラインとして可視化できます。
4. Web地図で可視化(MapLibre GL JS / deck.gl)
使用する HTML: index.html
前章までで生成したフロア別 GeoJSON(3次元座標)と 3D 歩行者ネットワークを、MapLibre GL JS + deck.gl で重ね合わせて表示します。実装は index.html にまとめています。
4.1 使用ライブラリと構成
- MapLibre GL JS:ベース地図
- deck.gl(MapboxOverlay):GeoJSON(3次元座標)(屋内フロア / 歩行者ネットワーク)を 3次元表示
4.2 deck.gl を MapLibre に重畳(MapboxOverlay)
map.on("load", ...) で MapboxOverlay を追加し、deck.gl のレイヤー群を MapLibre 上に重ねます。
const overlay = new deck.MapboxOverlay({
interleaved: true,
layers: [ /* deck.GeoJsonLayer */ ],
});
map.addControl(overlay);
4.3 フロア別 GeoJSON(polygons/lines/points)を一括レイヤー化
フロア一覧と色定義
const FLOORS = ["B3","B2","B1","0F","1F","2F","2out","3F","3out","4F","4out"];
const FLOOR_COLORS = {
B3: [255, 0, 255, 230],
B2: [127, 0, 255, 230],
// ...
"4out": [255, 0, 0, 170],
};
4.4 GeoJSON ファイル命名則に合わせた URL 生成
前処理で出力した命名則(Shinjuku_<floor>.<type>.geojson)に合わせて、URL を組み立てています。
function urlFor(floor, type) {
return `./geojson_merged/Shinjuku_${floor}.${type}.geojson`;
}
4.5 レイヤー生成関数(GeoJsonLayer)
buildLayer(floor, type) で、フロア×ジオメトリ種別の deck.gl レイヤー(GeoJsonLayer)を作ります。
GeoJsonLayer
GeoJSONの座標には、高度または標高を、オプションの3番目の値として含めることができる(GeoJSON 仕様 RFC 7946 ではオプショナル)。 deck.gl の GeoJsonLayer は 、GeoJSON の高さパラメータをきちんと解釈して 3D 空間に GeoJSON の要素を配置してくれるため、データを用意するだけで3D表示できる。
function buildLayer(floor, type) {
const color = FLOOR_COLORS[floor] || [200,200,200,160];
const id = `Shinjuku_${floor}.${type}`;
if (type === "polygons") {
return new deck.GeoJsonLayer({
id,
data: urlFor(floor, type),
extruded: true,
wireframe: true,
filled: true,
stroked: true,
lineWidthUnits: "meters",
getLineWidth: 0.8,
getLineColor: color,
getFillColor: [color[0], color[1], color[2], 30], // 塗りは薄め
getElevation: 5,
pickable: true,
});
}
// lines / points も同様に type に応じて GeoJsonLayer を返す(実装は省略)
}
4.6 3D 歩行者ネットワーク(リンク)を重ねる
shinjuku_link_3d.geojson を 1 本の GeoJsonLayer として描画します。
特徴:
-
parameters: { depthTest: false }で常に前面表示(ポリゴンに埋もれにくい) -
blending: "additive"で発光っぽい見た目に寄せる
new deck.GeoJsonLayer({
id: "shinjuku_link_3d",
data: "./shinjuku_link_3d.geojson",
stroked: true,
filled: false,
lineWidthUnits: "meters",
getLineWidth: 1,
getLineColor: [0, 255, 255, 80],
parameters: { depthTest: false },
blending: "additive",
});
4.7 デモサイト: 新宿駅周辺屋内3Dマップ
https://shiwaku.github.io/mlit-shinjuku-indoor-3dmap-on-maplibre/
5. 全体の処理パイプラインまとめ
Shapefile群
↓
1. フロア推定 + 3D化(make_shinjuku_3d_geojson.py)
↓
2. フロア別 × ジオメトリ別に整理(merge_shinjuku_floor_geojson.py)
↓
3. 歩行者ネットワークの3D化(shinjuku_network_3d_export.py)
↓
4. Web地図で可視化(MapLibre GL JS / deck.gl)
6. 屋内3Dマップの利用が想定されるシーン
- 駅構内や地下通路等ナビゲーションの 3D 可視化
- 駅構内や地下通路等シミュレーション(歩行者流動、導線評価)
- フロア別レイヤ切替 UI
- 3D 経路探索
- パーティクル可視化(deck.gl の ParticleLayer 等)との組み合わせ
7. まとめ
本記事では、屋内地図データをフロア別に 3D 化し、歩行者ネットワークも含めて立体的に扱えるデータ処理パイプラインを解説しました。
- フロア推定 → 固定Z付与で 3D 化
- ジオメトリ別に整理して Web 表示に最適化
- node/link の線形補間により滑らかな 3D ネットワークを生成
一連の処理をスクリプト化しておくと、実務でも応用しやすくなります。ぜひ似た案件で活用してみてください。
