Mapbox Newsletter WEEKLY TIPSの解説 -「3D屋内マップ用にポリゴンを作成」

2023/07/07に公開

はじめに

この記事は、先日配信された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を指定します。ここではさらにantialiastrueに設定します。これは内部的にCanvas#getContextantialiasを設定しています。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を再度確認してみます。建物の中に梁のような部分があります。

original

図中の赤矢印の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"
},

white

改めてGeoJSONを見るとheightが40m、base_heightが30mとなっているので30m-40mの部分だけが表示されているというのがわかりますね。

まとめ

3Dのオブジェクト(主に建物)を表現するためにはfill-extrusionレイヤーを使用すると良いことがわかりました。

GitHubで編集を提案
マップボックス・ジャパン合同会社

Discussion