「Cesium」を利用して、任意の座標から高度の取得及び設定方法

<h2>はじめに</h2>
GPS経路情報をCesium上で表示させましたが、Cesiumの地形情報と高度があっていなかっため、うまく表示ができませんでした。

そこで、任意の座標から、Cesiumの地形情報からの高度を取得し、自動車の高度に設定してみました。高度の取得には、clampToHeight()メソッドを利用します。

提供される高度は準拠楕円体からの高さ（Ellipsoid.WGS84）楕円体高になります。
これは、平均海面からの高度である標高ではありません。なので、海でサンプリングした場合、必ずしも0.0になるとは限りません。

あと、自動車の方向が自動で変わらなかったので、自動車位置の差から角度を計算し、設定しました。角度設定には、HeadingPitchRollクラスを使いました。

実は、「Cesium」のデモサイトではほとんどか3Dモデルの角度設定にorientationのunitQuaternionを使って設定しているのですが、設定値が不明でうまくいきませんでした。そのため、あきらめて、HeadingPitchRollクラスを使いました。

このクラスではそれぞれの軸の角度を設定するだけなので、設定値が簡単に変更できました。heading,pitch,rollにそれぞれの軸の角度を設定すると、3Dモデルが回転します。Cesiumのサンプルには、なぜかこの処理がなく、自動で向きが変わっています。

以下、高度の取得及び設定プログラムです。今回は経路と自動車の位置でデータを分けています。
※czmlには、clampToGroundをtrueにしていましたが、高度の設定はされていませんでした。czmlの設定に問題があったのかもしれません。

以下参考ソースです。

```index.html
<!DOCTYPE html>
<html lang="ja">`
  <head>
    <title>Hello World!</title>
    <link rel="stylesheet" href="../Build/CesiumUnminified/Widgets/widgets.css" />
    <script src="../Build/CesiumUnminified/Cesium.js"></script>
    <style>
      html,
      body,
      #cesiumContainer {
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <div id="cesiumContainer"></div>
    <script>
      var viewer = new Cesium.Viewer("cesiumContainer", {
        terrainProvider: Cesium.createWorldTerrain(),
      });
      var scene = viewer.scene;
      var clock = viewer.clock;
        
      var czmlOptions = {
          clampToGround: true
      };

      var entity;
      var positionProperty;
      var prePosition;
      
      viewer.dataSources.add(Cesium.CzmlDataSource.load('../Datas/clampToGround_load5.czml', czmlOptions)).then(function (ds) {
          entity = ds.entities.getById("Vehicle");
          viewer.trackedEntity = entity;
          positionProperty = entity.position;
          start();
      });
      
      viewer.dataSources.add(Cesium.CzmlDataSource.load('../Datas/clampToGround_load6.czml', czmlOptions));
      
       function start() {
          var objectsToExclude = [entity];
          scene.postRender.addEventListener(function () {
            var position = positionProperty.getValue(clock.currentTime);
            if (position === undefined) {
                return;
            };
            entity.position = scene.clampToHeight(position, objectsToExclude);
            if (prePosition !== undefined) {
                entity.orientation = calculateOrientation(prePosition, position);
            }
            prePosition = position;
          });
      } 
      function calculateOrientation(oldPosition, newPosition) {
            var dX = oldPosition.x - newPosition.x;
            var dY = oldPosition.y - newPosition.y;

            var offSet =  Cesium.Math.toRadians(135);
            var heading = Math.atan2(dX, dY) - offSet;
            if (heading > Cesium.Math.TWO_PI) {
                heading -= Cesium.Math.TWO_PI;
            }
            
            var pitch = 0;
            var roll = 0;

            var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
            var orientation = Cesium.Transforms.headingPitchRollQuaternion(oldPosition, hpr);
            return orientation;
      }
    </script>
  </body>
</html>
```

以下、画面イメージです。
![sampleTerrain2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179407/ff27429e-1dbd-cfe8-1476-d9dacf5bb8b3.png)