Mapbox Newsletter WEEKLY TIPSの解説 -「3D屋内マップ用にポリゴンを作成」
はじめに
この記事は、先日配信されたMapbox NewsletterのWEEKLY TIPSで紹介されていた「3D屋内マップ用にポリゴンを作成」についての解説です。このサンプルではfill-extrusion
レイヤーを用いた3Dオブジェクトの表示方法を例示しています。また、Newsletterの購読はこちらからお申し込みいただけます。
コードを確認
まずExamplesのコードを見に行きましょう。
日本語サイト
英語サイト
基本的に同じコードですが、英語版はスタイルがMapbox Streets v12にアップグレードされているのでこちらを使用します。Mapbox Streets v11ではデフォルトのプロジェクションがWebメルカトルであるのに対し、Mapbox Streets v12ではGlobe(3D表示された地球)なので、印象がかなり異なります。
HTML/CSS
まずHTMLを見ていきましょう。
以下は地図を表示するエレメントです。
<div id="map"></div>
Mapの作成
次にJavaScriptのコードを見ていきます。以下のコードはいつも通り、Mapオブジェクトを作成しています。container
で地図を表示するHTMLエレメントのidを指定します。ここではさらにantialias
をtrue
に設定します。これは内部的にCanvas#getContext
でantialias
を設定しています。true
とすることで、よりなめらかになります。
const map = new mapboxgl.Map({
container: 'map',
// Choose from Mapbox's core styles, or make your own style with Mapbox Studio
style: 'mapbox://styles/mapbox/streets-v12',
center: [-87.61694, 41.86625],
zoom: 15.99,
pitch: 40,
bearing: 20,
antialias: true
});
次に、ソースとレイヤーをloadイベント(map.on('load', ()=>{})
)の中で追加します。
ソースはWebサーバに配置されているJSONファイルを使用しています。プロパティの中にheight
, base_height
およびcolor
があり、これをレイヤーで使用します。
map.addSource('floorplan', {
'type': 'geojson',
/*
* Each feature in this GeoJSON file contains values for
* `properties.height`, `properties.base_height`,
* and `properties.color`.
* In `addLayer` you will use expressions to set the new
* layer's paint properties based on these values.
*/
'data': 'https://docs.mapbox.com/mapbox-gl-js/assets/indoor-3d-map.geojson'
});
次にレイヤーを作成します。3Dを表示するにはfill-extrusion
レイヤーを指定します。
-
fill-extrusion-height
: 3Dオブジェクトの高さを指定します。底面がソースで指定されたポリゴンである柱体の高さを指定します。単位はメートルです。ここではソースのheight
プロパティを使用しています。 -
fill-extrusion-base
: 柱体の描画を何メートルの高さから開始するかを指定します。ここではソースのbase_height
プロパティを使用しています。 -
fill-extrusion-color
: 柱体の側面の色をしています。ここではソースのcolor
プロパティを使用しています。
map.addLayer({
'id': 'room-extrusion',
'type': 'fill-extrusion',
'source': 'floorplan',
'paint': {
// Get the `fill-extrusion-color` from the source `color` property.
'fill-extrusion-color': ['get', 'color'],
// Get `fill-extrusion-height` from the source `height` property.
'fill-extrusion-height': ['get', 'height'],
// Get `fill-extrusion-base` from the source `base_height` property.
'fill-extrusion-base': ['get', 'base_height'],
// Make extrusions slightly opaque to see through indoor walls.
'fill-extrusion-opacity': 0.5
}
});
各プロパティの詳細はドキュメントをご参照ください。
より単純な形状で確認
さらに簡単な形状で挙動を確認してみます。レインボーブリッジのくるっと一周している部分の真ん中に直方体のオブジェクトを配置します。
GeoJSONは以下のように指定します。
const geojson = {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {},
geometry: {
coordinates: [
[
[139.75576967497403, 35.63764152913795],
[139.75576967497403, 35.636874038618885],
[139.7567477472371, 35.636874038618885],
[139.7567477472371, 35.63764152913795],
[139.75576967497403, 35.63764152913795]
]
],
type: "Polygon"
}
}
]
};
レイヤーは以下のように指定します。fill-extrusion-height
により高さが100mとなりますが、fill-extrusion-base
により20mまでは描画されません。20mの高さから100mの高さの直方体を描画しいているのではなく、地面から100mの高さの直方体のうち20mを描画していないというのが大事な点です。これにより、上部が張り出している建物や、空中のオブジェクトが表現できます。
map.on("load", () => {
map.addSource("source", {
type: "geojson",
data: geojson
});
map.addLayer({
id: "layer",
type: "fill-extrusion",
source: "source",
paint: {
"fill-extrusion-color": '#ff0000',
"fill-extrusion-base": 20,
"fill-extrusion-height": 100,
"fill-extrusion-opacity": 0.5
}
});
});
結果は以下のとおりです。ピッチを変えて浮いている感じをご確認ください。
また、Map#setLight
で照明も調整できるので合わせてご確認ください。
map.setLight({
anchor: "map",
color: "white",
intensity: 1
});
サンプルagain
サンプルで使用していたGeoJSONを再度確認してみます。建物の中に梁のような部分があります。
図中の赤矢印のFeatureの色をわかりやすいように以下のように白色に変更します。
{
"type": "Feature",
"properties": {
"name": "Arch",
"level": 1,
"color": "white",
"base_height": 30,
"height": 40
},
"geometry": {
"coordinates": [
[
[-87.617971, 41.866291],
[-87.617973, 41.866265],
[-87.617805, 41.866267],
[-87.617806, 41.866294],
[-87.617971, 41.866291]
]
],
"type": "Polygon"
},
"id": "4528ad9b9264cbec65bb2e55ac0012c1"
},
改めてGeoJSONを見るとheight
が40m、base_height
が30mとなっているので30m-40mの部分だけが表示されているというのがわかりますね。
まとめ
3Dのオブジェクト(主に建物)を表現するためにはfill-extrusion
レイヤーを使用すると良いことがわかりました。
Discussion