フリーダムの日記

GIS(地理情報システム)を中心に技術的なことを書いています。

Cesium と ArcGIS の連携について

はじめに

2023 年 5 月に Cesium と EsriArcGIS が正式に連携をサポートするニュースがありました。CesiumJS で ArcGIS のロケーションサービスを使用する機能を Esri と Cesium が正式にサポートされるようになりました。Cesium と Esri では、以下のアナウンスがありました。

Cesium - ArcGIS

cesium.com

Cesium - ArcGIS

www.esri.com

今回は、ArcGIS と連携した CesiumJS で具体的にどのような機能が使用できるようになったのか、 主に連携した内容にフォーカスして説明していきたいと思います。

Cesium とは

Cesium は、オープンソースの 3D 地理空間可視化プラットフォームで、3D の地物や地形のデータを読み込んで、ブラウザだけで GIS を実現することができます。2012 年に初期リリースされており、3D 情報を表示する仕組みとして 3DTiles を導入しています。3D 関連のサービスや製品としては、以下の 3D 関連の製品群があります。

CesiumJS(ArcGIS との連携)

ArcGIS との連携を可能にしているのは、Web アプリ開発API である CesiumJS になります。CesiumJS には、連携用のクラスを提供しています。

連携用のクラス 内容
Cesium.I3SDataProvider OGC I3S (Esri の 3D データ仕様) を表示
ArcGisMapServerImageryProvider 2D イメージ タイルを表示
ArcGISTiledElevationTerrainProvider Esri の標高レイヤーを標高ソースに使用

Esri 側には、CesiumJS と連携するための ArcGIS REST JS を使用した連携のコードを提供しています。これにより、ArcGIS のロケーションサービスを使用することができるようになります。

CesiumJS

developers.arcgis.com

具体的には、以下のレイヤーや機能が使用できます。

  • レイヤー
    • フィーチャ レイヤー (GeoJSON)
    • 3D オブジェクト シーン レイヤー
    • 3D メッシュ シーン レイヤー
    • 2D イメージ タイル レイヤー
    • 標高レイヤー
  • 機能
    • フィーチャのクエリ
    • データの可視化
    • 住所検索
    • ルート検索等...

代表的なサービスや機能の詳細を以下に説明します。

CesiumJS

サービス 機能 内容
シーン 背景地図 衛星画像や陰影を含むイメージ タイル サービス、標高サービスを表示します。
データサービス フィーチャ サービス フィーチャは、ジオメトリと属性情報を含んだ地理データです。フィーチャ サービスからクエリして GeoJSON 形式でフィーチャを返して表示します。
3D オブジェクト 3D オブジェクトを表示します。3D オブジェクトは、建物などのエンティティのモデリングに使用される I3S データの一種です。I3S は、OGC の Community Standard として承認されています。
3D メッシュ 3D メッシュを表示します。3D メッシュは、エリア全体をモデル化するために使用されるI3Sデータの一種です。通常、航空画像によって作成される統合メッシュは、ある範囲内のすべてを含む単一の連続サーフェスで構成されます。I3S は、OGC の Community Standard として承認されています。
イメージ タイルサービス イメージ タイルは、イメージ タイルサービスに保存されるタイルデータの一種です。通常、航空写真などの画像を表示しますが、他のデータを表示するために使用することもできます。イメージ タイル サービスは、ArcGIS でホストされているフィーチャ サービスをパブリッシュすることで作成できます。
ジオコーディングサービス 住所検索 検索ボックスから場所や住所を検索します。
リバースジオコーディング 緯度/経度から場所や住所を検索します。
場所の検索 コーヒーショップ、ガソリンスタンド、レストランなどの場所を検索します。
ルート検索 ルート検索とルート案内 出発地から目的地までのルートとルート案内を検索します。
到達圏(サービスエリア)の検索 ある場所から特定の運転時間で到達できるエリアを検索します。
POI (Point of Interest)の検索 近くの場所と詳細の検索 キーワード検索で場所周辺の場所を見つけ、詳細な場所情報を取得します。
矩形範囲の場所を検索 テキストベースの検索を実行して、バウンディングボックス内の場所を検索します。

CesiumJS での使用方法については、サンプルコードも合わせてチュートリアルにございますのでご確認ください。

developers.arcgis.com

CesiumJS で 3Dオブジェクトを表示

日本のデータを使用して表示してみたいと思います。今回は、データサービスとして 3D オブジェクトを表示しました。3D オブジェクトのデータとして、 Living Atlas に公開されている「Project PLATEAU」の東京都23区・八王子市南大沢 3D都市モデル(Project PLATEAU)を使用しました。このデータは無料で利用することができます。ただし、ご利用にあたっては、国土交通省ホームページ PLATEAU Policy に定められた利用条件を必ず遵守する必要があります。

3D オブジェクトの表示は、以下のチュートリアルを参考としました。ほぼ、同じコードで実装できます。

developers.arcgis.com

developers.arcgis.com

実装したサンプルのソースです。

<html>
  <head>
    <meta charset="utf-8" />
    <title>CesiumJS: Display a 3D object layer popup</title>
  <!-- Include the CesiumJS JavaScript and CSS files -->
  <script src="https://cesium.com/downloads/cesiumjs/releases/1.110/Build/Cesium/Cesium.js"></script>
  <link href="https://cesium.com/downloads/cesiumjs/releases/1.110/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
  <style>
      html,
      body {

          margin: 0px;
          padding: 0px;
          height: 100%;
      }
      #cesiumContainer {
          height: 100%;
      }
  </style>
</head>
<body>
  <div id="cesiumContainer"></div>
    <script type="module">

      const apiKey = "";

      Cesium.ArcGisMapService.defaultAccessToken = apiKey;

      const cesiumAccessToken = '';

      Cesium.Ion.defaultAccessToken = cesiumAccessToken;

      const arcGisImagery = Cesium.ArcGisMapServerImageryProvider.fromBasemapType(Cesium.ArcGisBaseMapType.SATELLITE, {
        enablePickFeatures:false
      });

      const viewer = new Cesium.Viewer("cesiumContainer", {

          baseLayer: Cesium.ImageryLayer.fromProviderAsync(arcGisImagery),

          terrain: Cesium.Terrain.fromWorldTerrain(),
          timeline: false,
          animation: false,
          geocoder:false

      });

      viewer.camera.setView({
          destination: Cesium.Cartesian3.fromDegrees(139.767125,35.671236,400),
          orientation: {
            heading: Cesium.Math.toRadians(60),
            pitch: Cesium.Math.toRadians(-15.0),
          }
      });

      const geoidService = await Cesium.ArcGISTiledElevationTerrainProvider.fromUrl("https://tiles.arcgis.com/tiles/z2tnIkrLQ2BRzr6P/arcgis/rest/services/EGM2008/ImageServer");

      const i3sLayer = "https://tiles.arcgis.com/tiles/wlVTGRSYTzAbjjiC/arcgis/rest/services/13100_13201_Tokyo-23ku_Minamiosawa_Building/SceneServer";

      const i3sProvider = await Cesium.I3SDataProvider.fromUrl(i3sLayer, {
          geoidTiledTerrainProvider: geoidService,
          token: apiKey
      })

      viewer.scene.primitives.add(i3sProvider);

      // An entity object which will hold info about the currently selected feature for infobox display
      const selectedEntity = new Cesium.Entity();
      // Show metadata in the InfoBox.
      viewer.screenSpaceEventHandler.setInputAction(function onLeftClick(movement) {
        // Pick a new feature
        const pickedFeature = viewer.scene.pick(movement.position);
        if (!Cesium.defined(pickedFeature)) {
          return;
        }
        const pickedPosition = viewer.scene.pickPosition(movement.position);

        if (
          Cesium.defined(pickedFeature.content) &&
          Cesium.defined(pickedFeature.content.tile.i3sNode)
        ) {
          const i3sNode = pickedFeature.content.tile.i3sNode;

          i3sNode.loadFields().then(function () {
            const geometry = i3sNode.geometryData[0];
            if (pickedPosition) {
              const location = geometry.getClosestPointIndexOnTriangle(
                pickedPosition.x,
                pickedPosition.y,
                pickedPosition.z
              );
              let description = "No attributes";
              let name;
              if (location.index !== -1 && geometry.customAttributes.featureIndex) {
                const featureIndex =
                  geometry.customAttributes.featureIndex[location.index];
                if (Object.keys(i3sNode.fields).length > 0) {
                  description =
                    '<table class="cesium-infoBox-defaultTable"><tbody>';
                  for (const fieldName in i3sNode.fields) {
                    if (i3sNode.fields.hasOwnProperty(fieldName)) {
                      const field = i3sNode.fields[fieldName];
                      description += `<tr><th>${field.name}</th><td>`;
                      description += `${field.values[featureIndex]}</td></tr>`;
                      console.log(
                        `${field.name}: ${field.values[featureIndex]}`
                      );
                      if (
                        !Cesium.defined(name) &&
                        isNameProperty(field.name)
                      ) {
                        name = field.values[featureIndex];
                      }
                    }
                  }
                  description += `</tbody></table>`;
                }
              }
              if (!Cesium.defined(name)) {
                name = "unknown";
              }
              selectedEntity.name = name;
              selectedEntity.description = description;
              viewer.selectedEntity = selectedEntity;
            }
          });
        }

      },
      Cesium.ScreenSpaceEventType.LEFT_CLICK);

      function isNameProperty(propertyName) {
        const name = propertyName.toLowerCase();
        if (
          name.localeCompare("name") === 0 ||
          name.localeCompare("objname") === 0
        ) {
          return true;
        }
        return false;
      }
    </script>
  </body>
</html>

実装した3Dオブジェクトを表示した Cesium アプリソースコードは、GitHub でも公開していますので、興味のある方は触ってみてください。

Cesium - ArcGIS

最後に

Cesium が ArcGIS のサービスをサポートしたことで、Cesium で実現できる幅がかなり広がったと思われます。まだ、I3S のサービスを完全にサポートはされていませんが、今後に期待したいところです。また、新しい機能がサポートされたら本ブログでも紹介していきたいと思います。