船艦航跡模擬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加入圖台中。