Sandcastleで始めるCESIUM ion 超入門（連載 第2回）

# はじめに
第1回は Cesium ion（セジウムアイオン）にアカウントを作って、3D可視化環境の Sandcastle（サンドキャッスル）を試しました。

今回はいよいよ実践編です。国土交通省のプロジェクト PLATEAU（プラトー）が提供する、日本全国の高精度な3D都市モデルを使って、リアルな都市空間をブラウザ上に再現してみましょう。

Sandcastle で JavaScript のコードを少しずついじっていきますよ！

**連載第2回のステップ**

1. PLATEAU の地形モデルと3D都市モデルを読み込む
2. 3D地形モデルを見てみる
3. 3D都市モデルを見てみる
4. 熱気球の3Dモデルを街の上空に配置する


# Step 1: PLATEAUの地形モデルと3D都市モデルを読み込む

PLATEAUの地形モデルは、国土地理院が全国をカバーして作っている「基盤地図情報 数値標高モデル（DEM:デム）5mメッシュ」から作られています。メッシュサイズが 5m の DEM はこんな感じでかなり細かいので、詳細な地形が表現できます。なお、5m メッシュ標高が未整備の場所では 10m DEM が使われています。

**5mメッシュはこんな感じ**
![5mmesh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/68938753-5a9e-46ba-a247-51d504795320.png)



そしてなんと、東京都23区では 1mメッシュ！という超細かい DEM が提供されていますので、こんな感じのシャキっと綺麗に角が立った 3D地形を見ることができます。

![千鳥ヶ淵.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/bdc1a260-5200-4729-8c48-ca149bac5b3f.png)


では前説はこれくらいにして、早速やって行きましょう♪

## Cesium ionアカウントにログイン
まずは Cesium ion のアカウントにログインします。

![CesiumIonにログイン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/9e3faabd-88ad-4815-bd2c-8d78fa0c3c9b.png)


アカウントを作れていない人は、第1回を見てくださいね。

:::note info
Sandcastleで始めるCESIUM ion 超入門（連載 第1回）
https://qiita.com/YutakaYasoshima/items/aafd8155f78e8801f6e2
:::

ログインしたら「My Assets」タブを見てみましょう。
前回、Google Photorealistic 3D Tiles を My Assets に追加したのが残っていると思います。

![MyAssets.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/cef4b269-f202-48a6-9b82-e084c0779b26.png)


今回は、My Assets に PLATEAU の地形モデルと建物モデルを追加します。

## Asset Depot から選ぶ

隣の「Asset Depot（アセットデポ）」タブを選択すると、たくさんのアセットが並んでいます。

この中から「Japan」で始まる2行を見つけて「＋」ボタンを押します。これらは、PLATEAU プロジェクトで作成された 3D地形モデルと 3D建物モデルです。


![Japan_Asset_Depot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/b719faec-b2da-4769-bb4d-8cd5e277e453.png)

もう一度「My Assets」タブに戻って見ると、選択した 2つのデータが追加されています。

![MyAssetにPLATEAUデータ追加済み.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/d3958199-4ded-4977-aa21-5f455bfd1a78.png)

<br />

**追加された2個のデータ**
1. PLATEAU Terrain（プラトーテレイン）
　Terrain とは 3D地形モデルのことです。場所によって 1mメッシュ、5mメッシュ、10mメッシュと細かさが違いますが、PLATEAU Terrain は日本全国をカバーしていますので、どこまでもスクロールして、地形を眺めて楽しめるデータです。

2. PLATEAU 3D建物モデル
　2025年5月11日までに、国内 236都市の 3D建物モデルが完成して、オープンデータとして公開されています。

先に、PLATEAU の 3D地形モデルの方から Sandcastle で表示してみましょう。

## Step 2: PLATEAU の 3D地形モデルを見てみる ##

「My Assets」タブで **Japan Regional Terrain**（ジャパン リージョナル テレイン）を見つけましょう。

![Terrainを表示.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/9817a0f9-79f4-4aea-9b23-276f461b9ab1.png)

初期表示は地球全体になりますが、地球儀を回して日本を拡大すると、綺麗な地形を見ることができます。

![最初の地形表示.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/fa9d42f7-f33c-4fad-af8d-cf1221a06e07.png)


しかしその前に、ちょっとまった！

もう第2回ですから、左側のコード部分も観察しておきましょう。

**PLATEAU Terrain（ 3D地形モデル）のコードを見てみる**
とても短いので、恐れるに足らず。2つのブロックしかありません。

![地形表示コード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/76375a67-239f-4b44-b00f-19653b6a9c95.png)

まずは先頭のこの部分から。


```js
// Grant CesiumJS access to your ion assets
Cesium.Ion.defaultAccessToken = "****ここは自分のアクセストークンが書かれています****";
```
これは、AccessToken（アクセストークン）と言って、自分の Cesium ion アカウントにアクセスするための、専用のトークン（鍵として働く秘密の文字列）です。それぞれのアカウントに個別のトークンが割り当てられています。

#### コメントの表記について
この後のコードにも出てくるのですが、
// こんなふうにスラッシュ2個があったら、その右側に書いてあることは、コメントです。プログラムコードに書き込まれた、いわゆる「メモ」です。

コメントはプログラムの動作の説明や、数値の意味など、後で見た時にコレ何だったけ？と思うようなところに細かく入れることになっています。頑張ってコードを書いて徹夜しちゃった翌朝には、内容などすっかり忘却の彼方...なんてことがありますからね😅

コードにコメントをいっぱい入れるのは、とっても良い習慣、良いプログラマです。

:::note info
コードにはクドいくらいにコメントを入れよう！
:::

<br />
さて、次の部分はここ。
```js
try {
  const viewer = new Cesium.Viewer("cesiumContainer", {
    terrainProvider:
      await Cesium.CesiumTerrainProvider.fromIonAssetId(2767062),
  });
} catch (error) {
  console.log(error);
}
```
try で始まるブロックは、
　```{カッコで囲まれた中} ```
の実行をトライする、という意味です。

<br>
ここで大切なのはここだけ。

```js
terrainProvider:
  await Cesium.CesiumTerrainProvider.fromIonAssetId(2767062)
```

これは、Cesium ion のアセットID「2767062」が地形データなのですが、それを
　**CesiumTerrainProvider**（セジウム テレインプロバイダー：地形配信元）
として使おうとしています。



そして、2767062 と言えば...実はさっき見ています。

これ。
ここで登録した地形データ **(2767062)** を呼び出しているのですね。

![TerrainのAssetID.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/21b4f1bd-05ef-4de6-b487-699661381bfa.png)


まとめると、この画面でやっていることは2個でした。


```j: まとめ
① Cesium ion のアクセストークンを渡す

// Grant CesiumJS access to your ion assets
Cesium.Ion.defaultAccessToken = "Cesium.Ion.defaultAccessToken = "****ここは自分のアクセストークンと差し替えて****";";

② 登録しておいた AssetID 2767062（PALTEAU 地形データ）を呼び出す。

try {
  const viewer = new Cesium.Viewer("cesiumContainer", {
    terrainProvider:
      await Cesium.CesiumTerrainProvider.fromIonAssetId(2767062),
  });
} catch (error) {
  console.log(error);
}
```

これで、コードの説明はおしまい。

せっかくなので、地球儀を回して富士山を見ておきます...😆

![日本人ならやっぱり富士山.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/9ed9db75-4197-44f5-a527-0f2e9a9bc381.png)

:::note info
地図を回しても、左側のコード自体は変わらないですね。
カメラの現在位置は、Cesium.jsの内部に保持されているので、
ユーザーが地図を操作してもプログラムコードが書き換わることはありません。
:::

## Step 3: PLATEAU の 建物モデルを見てみる ##

続けて、PLATEAUの 3D建物を見てみましょう。

My Assetsタブを選択して、 **Japan 3D Building Data** を見つけてください。その行を選択したら、左下の **Open complete code example** をクリックします。

![建物を表示.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/458267d5-973c-4faf-b173-e2e2d0f491c6.png)

最初から日本付近が拡大されていますね。

![建物の初期表示.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/a80338e9-6e15-4aee-a727-31e11ce09782.png)


そして、グリグリっと拡大すると、東京タワーだってちゃんと再現されています！
PLATEAU の3D建物モデル、すごいですね。

![東京タワー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/c0f562be-6387-494d-ac9e-f7150b6fb327.png)


<br />

**PLATEAU 3D建物表示のコードを観察**
ここで、左側のコード表示にご注目。
ここの部分です。

![建物に地形がついてくる.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/9eeadef5-c554-4953-8662-ee4f808639ea.png)

これ、さっき地形のコードを見たときに、同じようなことが書いてありましたよね！
ここのところ、そっくりですね。

![地形のコード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/aefb2518-43bf-4e5f-8a74-2d86ed12a5bb.png)

これらは、両方とも PLATEAU Terrain データを呼び出しています。

<br />
あらためて、建物のコードを見ていきます。

```js
// Grant CesiumJS access to your ion assets
Cesium.Ion.defaultAccessToken = "Cesium.Ion.defaultAccessToken = "****ここには自分のアクセストークンが書かれています****";";

const viewer = new Cesium.Viewer("cesiumContainer", {
  terrainProvider: await Cesium.CesiumTerrainProvider.fromIonAssetId(
    2767062,
  ),
});
viewer.scene.globe.depthTestAgainstTerrain = true;

try {
  const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(2602291);
  viewer.scene.primitives.add(tileset);
  await viewer.zoomTo(tileset);

  // Apply the default style if it exists
  const extras = tileset.asset.extras;
  if (
    Cesium.defined(extras) &&
    Cesium.defined(extras.ion) &&
    Cesium.defined(extras.ion.defaultStyle)
  ) {
    tileset.style = new Cesium.Cesium3DTileStyle(extras.ion.defaultStyle);
  }
} catch (error) {
  console.log(error);
}
```

<br />
最初の部分は、地形のコードと同じで、Cesium ion のアクセストークンが書いてあります。

```js
// Grant CesiumJS access to your ion assets
Cesium.Ion.defaultAccessToken = "****自分のアクセストークンが表示される
```
<br />
ご注目いただきたいのは、次のブロック。

```js
const viewer = new Cesium.Viewer("cesiumContainer", {
  terrainProvider: await Cesium.CesiumTerrainProvider.fromIonAssetId(
    2767062,
  ),
});
```

地形のコードとは微妙に書き方が違いますが、ここで、PALTEAU 地形データを呼び出しています。


あれれ？
さっきの操作では、PLATEAU 建物を選んで Sandcatsle を開いただけのに、なぜか、選んでもいない PLATEAU 地形まで、**勝手に**ついてきているぞ！？


:::note warn
**PLATEAU 建物には PLATEAU 地形が勝手についてくる**
:::
<br>
そうなんです。<br>
これは、Cesium 君（Cesium 社の誰かさん）が親切心からやってくれていることで、PLATEAU の建物を Sandcatsle で使う時には、PLATEAU 地形も一緒に組み込んでくれるという🥺 素晴らしい仕組みになっているんですね。

Cesium ion で標準的に使われる地形は、 Cesium World Terrein（セジウム ワールド テレイン）というものです。これは第1回の最初で出てきましたが、世界向けの製品なので、日本の国交省プロジェクト PLATEAU が独自に作っている建物とは相性が悪いのです。

:::note warn
**PLATEAU 建物には PLATEAU 地形を合わせないと具合悪い**
:::

日本のプロジェクトである PLATEAU では、地面の高さの基準を **「東京湾平均海面」** としています。国内で「標高」と呼ばれている高さは全て、この「東京湾平均海面」を 0m とした高さで統一されています。この時点ですでに、世界基準で作られている地形とは、ぴったり合わない予感しかしませんよね。

実際に PLATEAU の3D建物モデルを Cesium World Terrein に配置しても、高さが全く合わずに地面から浮いた感じになってしまいます。



なぜ、高さの一致しない地形が存在するのかの理由を説明するには、お話が長くなりますので、また次回、あるいは番外編で扱おうと思います。とりあえず、Sandcatsle を使っている間は Cesium 君の粋な計らいで、PLATEAU の建物は PLATEAU Terrain とセットで扱われますのでご安心を。

## Step 4: 気球の3Dモデルを街の上空に配置する ##

せっかく、東京タワーまで見られたのですから、上空に何か飛ばしてみようではありませんか。

実際のところ、3Dプロジェクトで PLATEAU の3D都市モデルだけで完結することはほとんどなくて、大概は他の 3Dモデルと組み合わせて、あれこれと楽しんだり、分析したり、という事になると思います。そうしたことも Sandcatsle でサクッとできちゃいますので、まずはやってみましょう。

**気球の 3Dモデルを配置してみる**

熱気球を配置するのは今回作った、PLATEAU 3D建物の上にしましょう。
PLATEATU の建物を読み込んだら、左側のコードに少し呪文を加えます。

現在のコードの下に次を追加します。
![コードをこの下に書き足す.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/86d511da-fec1-4662-9955-3d81363664ee.png)


:::note warn
コード全体を入れ替えるのではなく、コードの最後に追加です。
:::


```js

// 気球を追加する位置を設定する
const balloonPosition = Cesium.Cartesian3.fromDegrees(139.7457, 35.6586, 450.0);   // 概ね東京タワー付近、地上450m

const balloonEntity = viewer.entities.add({
  name: "Hot Air Balloon",
  position: balloonPosition,
  model: {
    uri: "https://sandcastle.cesium.com/SampleData/models/CesiumBalloon/CesiumBalloon.glb",
  },
});

// 気球にカメラをズーム
viewer.zoomTo(balloonEntity, new Cesium.HeadingPitchRange(
  Cesium.Math.toRadians(0),   // heading（方位）
  Cesium.Math.toRadians(-20), // pitch（見下ろし）
  400                         // range（距離m）
));
```

<br>
中身をみて行きましょう。

**(139.7457, 35.6586, 450.0)**　の3つの数値は、それぞれ
　東経 139.7457°
　北緯 35.6586°
　標高 450.0m
を表しています。

そして、const **balloonPosition** = **Cesium.Cartesian3.fromDegrees** によって、カッコ内の緯度、経度、高さを **balloonPosition** に保存しています。

:::note info
ここの数値を変えれば、好きな場所（緯度経度）、好きな高さに気球を配置できます。
:::

<br>
次のブロックには、熱気球のモデルの名前と配置する位置 3Dモデルのファイルを書いてあります。<br>

名前はこれです。<strong>name: "Hot Air Balloon"</strong>
クリックした時に表示される名前を設定しています。ここを日本語で「熱気球」などと変えてもOKです。<br>
<strong>position: balloonPosition</strong><br>
では、上の方で **balloonPosition** に設定した気球の表示位置を読み込ませています。

最後にこの部分で、表示させる気球の 3Dモデルを指定しています。
<strong>
model: {
&nbsp;&nbsp;uri: "https://sandcastle.cesium.com/SampleData/models/CesiumBalloon/CesiumBalloon.glb",
}
</strong>

次の部分で気球にズームしています。
ズームは簡単で、ズームする対象を指定して、カメラ位置（視点）を設定するだけ。

<strong>viewer.zoomTo(balloonEntity, new Cesium.HeadingPitchRange</strong>　はこの一行でカメラをズームする呪文になっています。

　balloonEntity には、何行か上の「balloonEntity = ...」のとこで設定した、3Dモデルの情報が入っています。
  
  そして、その対象物に対して、カメラの方位、見上げあるいは見下ろし角、カメラとの距離を指定します。
　Cesium.Math.toRadians(0),   // heading（方位）
　Cesium.Math.toRadians(-20), // pitch（見下ろし）
　400                         // range（距離m）


heading：北を 0°としたカメラの向きを角度で表したもの
pitch：水平を 0°としたときに下方向のカメラの角度
range：対象物までの距離（m）



完成したコードはこうなります。
もし自分の Sandcatsle で上手く動かない場合は、下記のものと貼り替えてみてください。

```js
// Grant CesiumJS access to your ion assets
// 自分のアカウントのアセットを追加する
Cesium.Ion.defaultAccessToken = "****ここは自分のアクセストークンと差し替えて****";

// Terrain（地形モデル）の指定
// PLATEAUのTerrain（地形データ）の AssetID をCesiumTerrainProvider に指定して、Cesium.Viewerを開く
const viewer = new Cesium.Viewer("cesiumContainer", {
  terrainProvider: await Cesium.CesiumTerrainProvider.fromIonAssetId(2767062),  });

// 地下に埋まっているものを表示しない呪文
viewer.scene.globe.depthTestAgainstTerrain = true;

//ここから
try {
  const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(2602291);    //PLATEAUの3D建物モデル
  viewer.scene.primitives.add(tileset);
  await viewer.zoomTo(tileset);   //建物3Dにズームする。あとで気球にズームするので、この行は削除してOK。

  // Apply the default style if it exists
  const extras = tileset.asset.extras;
  if (
    Cesium.defined(extras) &&
    Cesium.defined(extras.ion) &&
    Cesium.defined(extras.ion.defaultStyle)
  ) {
    tileset.style = new Cesium.Cesium3DTileStyle(extras.ion.defaultStyle);
  }
} catch (error) {
  console.log(error);
}

// 気球を追加する
const balloonPosition = Cesium.Cartesian3.fromDegrees(139.7457, 35.6586, 450.0);   // 概ね東京タワー付近、地上450m

const balloonEntity = viewer.entities.add({
  name: "Hot Air Balloon",
  position: balloonPosition,
  model: {
    uri: "https://sandcastle.cesium.com/SampleData/models/CesiumBalloon/CesiumBalloon.glb",
  },
});

// 気球にカメラをズーム
viewer.zoomTo(balloonEntity, new Cesium.HeadingPitchRange(
  Cesium.Math.toRadians(0),   // heading（方位）
  Cesium.Math.toRadians(-20), // pitch（見下ろし）
  400                         // range（距離m）
));
```
<br>
いかがですか？
下の絵みたいに、東京タワーの横に、ちっちゃい熱気球が出てきたら勝利です！🎉

マウスで地図を拡大縮小しているうちに、気球がどこかに行ってしまったら「Run (F8)」ボタンを押して、コードを再実行してください。最初の表示に戻ります。

そうそう、Cesium は、その時間の太陽高度を再現しようとするので、夜見ると、何だか暗い感じの画面になります。そんなときは、タイムラインの青ボールを動かして、いい感じの日当たりになるように、調整してみてください。

![実行とタイムライン操作.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/c89122fc-911b-4e4f-b0b3-4bd9b57bcff7.png)


# おわりに
Sandcatsle を使うと、ブラウザだけでプログラムコードをいじりながら、3D地図で遊べてしまいましたね。

このままずっと遊び続けても良いのですが、次回はちょっと頑張って、自分のパソコンの中にコードを動かす環境を作ります。そうすると、Sandcastle で動かしたコードを自分のパソコンにコピペして、Sandcastle 無しでもプログラミングを楽しめるようになります。

さらに、自分のパソコン環境で動かしたコードを、そのままホームページに掲載して、他の人たちに見てもらうことも出来るようになります！
<br>
例えば、今回のコードもこんな感じで公開すれば、みんなに見てもらえます。

https://cesium4fun.s3.amazonaws.com/web/part02/play_with_cesium_part02.html

↑このコードのように、Cesium ion の アクセストークンをコード内に書く場合には以下の設定を行いましょう。
①あたらしくトークンを作る
②読み込み専用（ReadOnly）にする
③このアクセストークンで利用できる AssetID を制限する。

こんな感じです。

**新しくトークンを作る**
![CreateToken.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/bedc7333-e032-4f23-855f-2cc9c31357f5.png)

**ReadOnly（読み込み専用）にする
![AccessToken設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/de43ea8e-df4c-4a83-9ed2-20eaca58fa31.png)

さらに、設定欄の下の方に、このアクセストークンで利用できる Assett ID を制限できる場所があります。

**このトークンで使える Assest ID を制限する**
![使えるAssetを制限する.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4076483/cf42c789-341a-4ccb-b18e-bfc29b55794e.png)

これらの設定はいつでも変更可能です。

第2回はこれでおしまい😃 
ちょっと長めでしたか？ご感想をいただけたら嬉しいです。
<br>
#### これまでの連載記事

第1回では Cesium ion のアカウントの作り方をご紹介しています。

:::note info
Sandcastleで始めるCESIUM ion 超入門（連載 第1回）
https://qiita.com/YutakaYasoshima/items/aafd8155f78e8801f6e2
:::