船艦航跡模擬Widget


本章將說明船艦航跡模擬Widget的使用方式。

[info] 小提示:

SandBox連結:https://codesandbox.io/s/widget-orbit-sample-kyfkwj?file=/Script/main.js


[info] 小提示:

構成本Widget使用之Function:
ov.TerrainView.getModule
ov.Widget.Timeline
ov.Widget.TimelinePlayer
ov.Widget.Timeline.addLink ov.TrackModule.addTrackEntity ov.TerrainView.addMoveEvent ov.TerrainView.setMoveMode


初始化Widget

先撰寫一個最基本的圖台,本功能有使用第三方套件Turf.js用於計算船艦轉向方位角。

index.html

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="utf-8" />
        <title>BaseMapWidget</title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/Turf.js/6.5.0/turf.min.js"></script>
        <script src="Script/PGWeb3D.min.js"></script>
    </head>
    <body>
        <div id="MyMap" style="width:100%;height:100%;position:absolute;top:0;left:0"></div>
        <div id="divOrbitCtrl" class="GSMis-wrapper" style="display: none;">
          <main class="GSMis-main-wrap">
            <div class="map-panel">
              <div class="map-panel-body scroll" style="border-radius: 10px;">
                <!-- Header -->
                <div class="map-panel-header">
                  <div class="title">播放控制</div>
                </div>
                <div id="divSimPlyCtrl">
                  <!-- 播放控制 -->
                  <div class="p-3 pt-0 bg-white shadow-down">
                    <div class="mb-2">
                      <label for="" class="form-label">視角</label>
                      <select
                        id="ddlSimView"
                        class="form-select"
                        onchange="switchSimView();"
                      >
                        <option value="90">俯視(90度)</option>
                        <option value="45">俯視(45度)</option>
                        <option value="third">第三人稱</option>
                        <option value="first">第一人稱</option>
                      </select>
                    </div>
                    <div class="mb-2">
                      <label class="form-label">物件跟隨</label>
                      <input
                        type="checkbox"
                        id="chkObjFollow"
                        class="form-check-input"
                        onchange="switchSimView();"
                      />
                      <label id="lbObjFollow" for="chkObjFollow">Toggle</label>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </main>
        </div>
        <script src="main.js"></script>
    </body>
</html>

將Widget初始化加進我們撰寫好的main.js中。

main.js

var terrainview = new window.ov.TerrainView("MyMap");
var widgetTimeline = null;
var widgetTimelinePlayer = null;
var track = null;
var moveEvent = null;
var SimCourse = 0;

//在使用其他圖層前必須要先讀入地形 其他圖層的新增都會在callback中
var param = {
  url: "http://127.0.0.1:8080",
  identifier: "範例地形圖",
  callback: openCallback,
  urlTemplate: "https://sample.pilotgaea.com.tw/Oview.aspx?{URL}"
};
terrainview.openTerrain(param);

function openCallback(result) {
  terrainview.setBaseLayer({
    url: "BING_MAP",
    identifier: "IMAGE"
  });

  //設定初始位置
  let initialPos = new window.GeoPoint(
    121.2347878442796,
    23.727553934089445,
    465850.0013822634
  );
  let initialV = new window.Geo3DPoint(0, 0, -1);
  let initialUp = new window.Geo3DPoint(0, 1, 0);
  let initialCamera = new window.ov.Camera(initialPos, initialV, initialUp);
  terrainview.gotoCamera(initialCamera, false);

  widgetTimeline = new ov.Widget.Timeline({
    view: terrainview,
    style: { left: "8%" }
  });

  //選擇要綁定的terrainview並初始化時間軸撥放器Widget。
  widgetTimelinePlayer = new ov.Widget.TimelinePlayer({
    view: terrainview,
    timeline: widgetTimeline,
    playSpeed: 5000.0
  });
  //預設開啟船艦航跡模擬Widget
  OrbitWidget();
}

function OrbitWidget() {
  //船艦軌跡點位
  var OrbitPos = [
    [119.572, 23.564, 1699609200000],
    [119.571, 23.56323, 1699609440000],
    [119.57082, 23.56077, 1699610460000],
    [119.56984, 23.55868, 1699613760000]
  ];
  //軌跡各點為抵達時間
  var TimeSpan = [1699609200000, 1699609440000, 1699610460000, 1699613760000];
  var CourseArray = [];    //個點位轉向
  var PathArray = [];
  track = terrainview.getModule("track");
  track.depthTest = false;

  for (i = 0; i < OrbitPos.length; i++) {
    var p1 = OrbitPos[i];
    var p2 = i != OrbitPos.length - 1 ? OrbitPos[i + 1] : OrbitPos[i];
    //計算方位角
    var bearing = turf.bearing(p1, p2, { final: true });
    var course = bearing / 360;    //計算船艦方位角
    CourseArray.push(course);
    var Hight = terrainview.getHeight(new window.GeoPoint(p1[0], p1[1]));
    var GeoPt = new window.GeoPoint(p1[0], p1[1], Hight + 1);
    PathArray.push(GeoPt);
  }

  SimCourse = CourseArray[0];
  var steps = [];
  for (i = 0; i < PathArray.length; i++) {
    if (i != PathArray.length - 1) {
      var startPosition = PathArray[i];
      var endPosition = PathArray[i + 1];
      var stepObj = {
        startPosition: startPosition,
        endPosition: endPosition,
        acceleration: 0
      };
      steps.push(stepObj);
    }
  }

  track.addTrackEntity({
    onGround: true,
    target: {
      src: "ShipModel/fishing_boat.glb",
      scale: 0.025,
      minRange: 0,
      rotate: { x: 0, y: 1, z: 0, w: 180 - parseFloat(CourseArray[0]) * 360 }
    },
    path: {
      geo: new window.GeoPolyline(PathArray),
      date: TimeSpan
    },
    callback: function (ent) {
      widgetTimeline.addLink(ent);
      ent.show = true;
      widgetTimeline.updateTime(ent.playingInfo.TimeSpan[0]);
      var stops = ent._entity.TrackManager.Stops;
      for (i = 0; i < stops.length; i++) {
        stops[i].Heading = CourseArray[i];
      }

      moveEvent = terrainview.addMoveEvent({
        steps: steps,
        entity: ent,
        defaultFunction: function (entity, position) {
          //更新圖素位置
          entity.update({
            position: position
          });
          //設定船艦轉向
          var Heading = null;
          var currentTime = widgetTimeline.getNowTime().getTime();
          for (i = entity._entity.TrackManager.Stops.length - 2; i >= 1; i--) {
            var StopTime = entity._entity.TrackManager.Stops[i].Date.getTime();
            if (currentTime > StopTime) {
              Heading = entity._entity.TrackManager.Stops[i].Heading;
              break;
            }
          }

          if (Heading != null) {
            entity.targetEntity.update({
              rotate: { x: 0, y: 1, z: 0, w: 180 - parseFloat(Heading) * 360 }
            });
          }
        },
        angularVelocity: Math.PI
      });
    }
  });

  let initialPos = new window.GeoPoint(OrbitPos[0][0], OrbitPos[0][1], 300);
  let initialV = new window.Geo3DPoint(0, 0, -1);
  let initialUp = new window.Geo3DPoint(0, 1, 0);
  let initialCamera = new window.ov.Camera(initialPos, initialV, initialUp);
  terrainview.gotoCamera(initialCamera, false);
  widgetTimeline.show();
  widgetTimelinePlayer.show();
  $("#divOrbitCtrl").show();
  //設定視角
  switchSimView();
}

//切換視角
function switchSimView() {
  if (track.entities.length > 0) {
    var Heading = 180 - parseFloat(SimCourse) * 360;
    var ViewVal = $("#ddlSimView").val();
    document.getElementById("chkObjFollow").removeAttribute("disabled");
    terrainview.setMoveMode(ov.MOVE_TYPE.PANEL);
    var EntityPos = track.entities[0].position;
    var ParamPos = terrainview.camera.pos;
    var ParamV = new window.Geo3DPoint(0, 0, -1);
    var ParamUp = new window.Geo3DPoint(0, 1, 0);
    var IsFollow = $("#chkObjFollow")[0].checked;

    switch (ViewVal) {
      case "90":    //俯視90度
        ParamPos = new window.Geo3DPoint(EntityPos.x, EntityPos.y, 250);
        break;
      case "45":    //俯視45度
        ParamPos = new window.Geo3DPoint(
          EntityPos.x,
          EntityPos.y / 1.00007,
          160
        );
        ParamV = new Geo3DPoint(-0.02978, 0.734546, -0.6779);
        ParamUp = new Geo3DPoint(-0.02746, 0.677348, 0.735);
        break;
      case "third":    //第三人稱
        ParamPos = new window.Geo3DPoint(
          EntityPos.x * 1.000003,
          EntityPos.y / 1.000001,
          12
        );
        ParamV = new window.Geo3DPoint(0, 0, 0);
        ParamUp = new window.Geo3DPoint(SimCourse, SimCourse, 0);
        if (Heading < 0) {
          ParamUp = new window.Geo3DPoint(-SimCourse, -SimCourse, 0);
        }
        let Camera = new window.ov.Camera(ParamPos, ParamV, ParamUp);
        terrainview.gotoCamera(Camera, false);

        terrainview.setMoveMode(ov.MOVE_TYPE.FOLLOW, {
          entity: track.entities[0],
          moveEvent: moveEvent,
          view: ov.FOLLOW_VIEW.THIRD_PERSON_VIEW,
          thirdPersonViewMovingFunction: function (
            entity,
            moveEvent,
            pos,
            angle
          ) {
            let position = entity.position;
            pos.x = position.x;
            pos.y = position.y;
            pos.z = position.z;
            //設定天頂角及方位角
            angle.azimuthAngle = moveEvent.getAzimuthAngle();
            angle.polarAngle = moveEvent.getPolarAngle();
          }
        });

        $("#chkObjFollow").prop("checked", true);
        document
          .getElementById("chkObjFollow")
          .setAttribute("disabled", "disabled");
        break;
      case "first":     //第一人稱
        terrainview.setMoveMode(ov.MOVE_TYPE.FOLLOW, {
          entity: track.entities[0],
          moveEvent: moveEvent,
          view: ov.FOLLOW_VIEW.FIRST_PERSON_VIEW,
          firstPersonViewMovingFunction: function (
            entity,
            moveEvent,
            pos,
            v,
            up,
            offset
          ) {
            let position = entity.position;
            pos.x = position.x;
            pos.y = position.y;
            pos.z = 7;
            //設定面相
            let vDir = moveEvent.getV();
            v.x = vDir.x;
            v.y = vDir.y;
            v.z = vDir.z;
            //設定頭頂方向
            let upDir = moveEvent.getUp();
            up.x = upDir.x;
            up.y = upDir.y;
            up.z = upDir.z;
            //設定偏移量
            offset.x = 0.0;
            offset.y = 0.0;
            offset.z = 0.0;
          }
        });
        $("#chkObjFollow").prop("checked", true);
        document
          .getElementById("chkObjFollow")
          .setAttribute("disabled", "disabled");
        break;
    }

    if (ViewVal == "90" || ViewVal == "45") {
      let Camera = new window.ov.Camera(ParamPos, ParamV, ParamUp);
      terrainview.gotoCamera(Camera, false);

      if (IsFollow) {
        terrainview.setMoveMode(ov.MOVE_TYPE.FOLLOW, {
          entity: track.entities[0],
          moveEvent: moveEvent,
          view: ov.FOLLOW_VIEW.THIRD_PERSON_VIEW,
          thirdPersonViewMovingFunction: function (
            entity,
            moveEvent,
            pos,
            angle
          ) {
            let position = entity.position;
            pos.x = position.x;
            pos.y = position.y;
            pos.z = position.z;
          }
        });
      } else {
        terrainview.setMoveMode(ov.MOVE_TYPE.PANEL);
      }
    }
  }
}

到目前為止,我們已成功將船艦航跡模擬Widget加入圖台中。

Copyright © NCHC 2022 Version:13.0 all right reserved,powered by Gitbook修訂時間: 2024-11-20 13:52:09