1. 概要
ベクトルタイルAdvent Calendar 2021の@frogcatさんの記事『PLATEAUのCityGML Building LOD2をMVTにしてみた』は、PLATEAUのLOD2モデルをmapbox-gl.js/maplibre-gl-jsの「fill-extrusion」で描画するというものです。このLOD2-MVTは3DTilesのような表現力はないですが、相対的に軽く扱いやすいので、非常に興味を持ちました。
また、東京都のLOD2-MVTタイルセットがindigo-lab/plateau-lod2-mvtで公開されています。
上記の記事ではCityGMLから地物のGeojsonを生成し、tippecanoeでMVTを生成していますが、ちょっと手間がかかるので、勉強がてら、citygml4jとmapbox-vector-tile-javaを使って、手っ取り早くLOD2-MVTを生成するツールを作ってみました。
2. 作成物
MVTの仕様とか、色々と理解不足がありますが、とりあえず、CityGMLを読み込み、LOD2-MVTを出力するツールを作ってみました。
実行環境はJRE1.8以降です。
実行形式のJARファイルは『ココ』からダウンロードできます。
※変換するPlateau-CityGMLは、iurのXMLSchemaとロケーション属性が1.5版である必要があります。
3. 使い方
使い方は以下のとおりです。
- 入力先にGityGMLが入ったディレクトリを指定します。※D&D可能
- 出力先にMVTの出力先のディレクトリを設定します。※D&D可能
- 出力する最小・最大のズームレベルを設定します。
- モデルの属性(GenericAttributeのみ)をMVTに設定するか選択します。
- 「処理開始」ボタンを押すと処理を開始します。
4. 作成したクラス/ソースコード
作成したクラスは以下のとおりです。
今回はメモリを節約するため、1ファイルごとにMVTを生成し、同じタイル座標のMVTが存在する場合は地物又はレイヤーを追加してMVTを更新しています。
メモリに余裕があれば、すべてのファイルを読み込み、一括でMVTを生成した方が効率的だと思います。
- Lod2MvtApp.java ---------- GUIを構築するクラス。GUI TookkitはSwingを使用しています。
- TileMeshUtil.java --------- 緯度経度からタイル座標を算出するユーティリティクラス。
- GMLToJsonUtil.java -------- CityGMLからGeojsonデータを生成するユーティリティクラス。
- MVTBuilder.java ---------- GeojsonデータからMVTを生成するクラス。
- JtsAdapterReverse.java --- JtsAdapterクラスをPLATEAU向けに修正したクラス。
5. LOD2-MVTの使用例
mapbpx-gl.jsでの使用例を以下に示します。
なお、VS-CodeのLiveServerでHTML/JS及びタイルの動作を確認しました。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>テスト</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<script src="https://api.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.css" rel="stylesheet">
<style type="text/css">
html{height:100%; margin: 0px;}
body { margin: 0; padding: 0; height:100%;}
#main {
position:relative;
transition: margin-left .5s;
padding: 16px;
height:96%;
}
#map {
position: absolute;
top: 0; right: 0;
bottom: 0; left: 0;
width:100%;
height:100%;
}
</style>
</head>
<body>
<div id="main">
<div id='map'></div>
</div>
<script>
const data={
"version": 8,
"sources": {
"t_pale": {
"type": "raster",
"tiles": ["https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg"],
"tileSize": 256,
}
},
"layers": [{
"id": "t_pale",
"type": "raster",
"source": "t_pale",
"minzoom": 9,
"maxzoom": 18
}]
};
mapboxgl.accessToken = 'アクセストークン';
const map = new mapboxgl.Map({
container: 'map',
zoom: 13.1,
maxZoom: 18,
minZoom: 9,
center: [139.7673068,35.6809591],
pitch: 70,
bearing: 0,
antialias: true,
style: data
});
map.on('load', () => {
map.addLayer({
'id': 'sky',
'type': 'sky',
'paint': {
'sky-type': 'atmosphere',
'sky-atmosphere-sun': [0.0, 0.0],
'sky-atmosphere-sun-intensity': 15
}
});
map.addSource("mvt", {
"type": "vector",
"tiles": ["http://127.0.0.1:5500/mvt/{z}/{x}/{y}.pbf"],
"minzoom":12,
"maxzoom": 18
});
map.addLayer({
"id": "bldg",
"type": "fill-extrusion",
"source": "mvt",
"source-layer": "BUILDING",
"minzoom": 12,
"maxzoom": 18,
"paint": {
"fill-extrusion-color": "#6666ff",
"fill-extrusion-height": ["get", "height"]
}
});
map.addLayer({
"id": "brid",
"type": "fill-extrusion",
"source": "mvt",
"source-layer": "BRIDGE",
"minzoom": 12,
"maxzoom": 18,
"paint": {
"fill-extrusion-color": "#66ff66",
"fill-extrusion-height": ["get", "height"]
}
});
map.addLayer({
"id": "water",
"type": "fill",
"source": "mvt",
"source-layer": "WATER_BODY",
"minzoom": 12,
"maxzoom": 18,
"paint": {
'fill-color': {
property: 'height',
stops: [
[0, '#ffffff'],
[0.5, '#ff0000'],
]
},
'fill-opacity': 0.5,
}
});
map.addSource("jgd", {
"type": "vector",
"tiles": ["https://cyberjapandata.gsi.go.jp/xyz/experimental_bvmap/{z}/{x}/{y}.pbf"],
"minzoom":9,
"maxzoom": 16
});
map.addLayer({
"id": "jgd",
"type": "line",
"source": "jgd",
"source-layer": "road",
"minzoom": 9,
"maxzoom": 18,
"paint": {
'line-opacity': 1.0,
'line-color': 'rgb(80, 80, 80)',
'line-width': 2
}
});
map.addControl(new mapboxgl.FullscreenControl());
map.addControl(new mapboxgl.NavigationControl());
map.addSource('mapbox-dem', {
'type': 'raster-dem',
'url': 'mapbox://mapbox.mapbox-terrain-dem-v1',
'tileSize': 512,
'maxzoom': 14
});
map.setTerrain({ 'source': 'mapbox-dem', 'exaggeration': 1.0 });
});
</script>
</body>
</html>
■LOD2-MVT変換例(東京都港区付近)
■LOD2-MVT変換例(東京タワー)※PLATEAUのbldgデータ
■LOD2-MVT変換例(レインボーブリッジ) ※PLATEAUのbridデータ
■LOD2-MVT変換例(荒川の浸水予測情報) ※PLATEAUのfldデータ
6. その他
最近、3DTilesやオルソ画像のPLATEAU配信サービス(試験運用)が始まっており、色々と楽しみですが、データ量が大きいので現在はちょっと使い難い感じします。
LOD1ではちょっと味気ない、でも処理を軽くしたい時にLOD2-MVTを使用する場合があるかもと思っています。
【出典など】
本エントリで使用した画像は、3D都市モデル(Project PLATEAU)東京都23区(CityGML 2020年度)を加工して作成したものです。
本ツールでのデータセットの使用・加工にあたっては、PLATEAU Policyを確認し、権利者の権利を侵害しないように留意してください。
-
【免責事項】
- 明示、暗黙を問わず内容に関してはいかなる保証も適用しません。
- ツール等の利用により、何らかのトラブルや損失・損害等が生じた場合、著者は損害、損失に対していかなる場合も一切の責任を負いません。
- 全ての情報について、内容の合法性・正確性・安全性等、あらゆる点において保証しません。