根據cesium官網示例(https://sandcastle.cesium.com/?src=Interpolation.html)改造爲颱風移動軌跡,颱風數據從颱風路徑實時發佈系統獲取。模擬颱風移動路徑從颱風發生地開始,動態移動至颱風消亡地,之後從起點又開始循環移動。採用實體ellipse模擬颱風的旋轉,給橢圓背景material貼圖,使用CallbackProperty設置rotation和stRotation逆時針轉動。
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Use correct character set. -->
<meta charset="utf-8">
<!-- Tell IE to use the latest, best version. -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- Make the application on mobile take up the full browser screen and disable user scaling. -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title>typhoon path</title>
<script src="../js/jquery-3.3.1.min.js"></script>
<script src="../js/require.min.js" data-main="../js/main"></script>
<style>
@import url(../../build/Cesium/Widgets/widgets.css);
@import url(../css/main.css);
</style>
</head>
<body>
<div id="cesiumContainer"></div>
<script>
var viewer;
function onload(Cesium) {
var url = 'http://{s}.tianditu.com/img_w/wmts?service=WMTS&version=1.0.0&request=GetTile&tilematrix={TileMatrix}&layer=img&style={style}&tilerow={TileRow}&tilecol={TileCol}&tilematrixset={TileMatrixSet}&format=tiles&tk=fb0dd2b4158b2f9d4bec0aaaf9722801';
viewer = new Cesium.Viewer('cesiumContainer', {
imageryProvider: new Cesium.WebMapTileServiceImageryProvider({
url: url,
layer: 'img',
style: 'default',
format: 'tiles',
tileMatrixSetID: 'w',
credit: new Cesium.Credit('天地圖全球影像服務'),
subdomains: ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7'],
maximumLevel: 18
}),
baseLayerPicker: false,
selectionIndicator: false,
infoBox: false,
animation: false, // 動畫
timeline: false, // 時間線
navigationHelpButton: false, // 關閉幫助控件
fullscreenButton: false, // 關閉全屏顯示
geocoder : false // 關閉搜索控件
});
viewer._cesiumWidget._creditContainer.style.display = "none"; // 去除水印
viewer.scene.globe.enableLighting = false; // 關閉日照
viewer.scene.globe.depthTestAgainstTerrain = true; // 開啓地形探測(地形之下的不可見)
viewer.scene.globe.showGroundAtmosphere = false; // 關閉大氣層
var imageryLayers = viewer.imageryLayers;
var url2 = 'http://{s}.tianditu.com/cia_w/wmts?service=WMTS&version=1.0.0&request=GetTile&tilematrix={TileMatrix}&layer=cia&style={style}&tilerow={TileRow}&tilecol={TileCol}&tilematrixset={TileMatrixSet}&format=tiles&tk=fb0dd2b4158b2f9d4bec0aaaf9722801';
var labelImagery = new Cesium.WebMapTileServiceImageryProvider({
url: url2,
layer: 'cia',
style: 'default',
format: 'tiles',
tileMatrixSetID: 'w',
credit: new Cesium.Credit('天地圖全球影像中文註記服務'),
subdomains: ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7']
});
imageryLayers.addImageryProvider(labelImagery);
var scene = viewer.scene;
var camera = viewer.scene.camera;
// 初始位置
var initViewLocation = {
destination : Cesium.Cartesian3.fromDegrees(112.951085, 28.148327, 1000),
duration : 0, // 旋轉速度 數值越大越慢
orientation : { // 朝北向下俯視
heading : 5.857107801269642,
pitch : -0.02842342271796139, // 相機間距
roll : 0.0 // 相機滾動
}
}
$.get("../data/json/typhoonData.json", null, function(data){
console.log(data)
var typhoonName = "利奇馬";
var result = [];
if (data.length > 0) {
for(let i = 0; i < data.length; i++){
if(typhoonName===data[i].name){
const points = data[i].points
for(let p=0; p<points.length; p++){
const d = {
'FID' : points[p].time,
'serial': p+1,
'fLongitude': points[p].lng,
'fLatitude': points[p].lat
}
result.push(d);
}
}
}
typhoonFlytoPath(viewer, result, typhoonName)
}
})
}
function typhoonFlytoPath(viewer, positions, typhoonName){
// 允許飛行動畫
viewer.clock.shouldAnimate = true;
// 設定模擬時間的界限
const start = Cesium.JulianDate.fromDate(new Date(2015, 2, 25, 16));
const stop = Cesium.JulianDate.addSeconds(start, positions.length, new Cesium.JulianDate());
viewer.clock.startTime = start.clone();
viewer.clock.stopTime = stop.clone();
viewer.clock.currentTime = start.clone();
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; //末尾循環
// 飛行速度,值越大速度越快,multiplier爲0停止移動
viewer.clock.multiplier = 3;
// viewer.timeline.zoomTo(start, stop);
// 計算飛行時間和位置
const property = computeFlight(start, positions)
var rotation = Cesium.Math.toRadians(30);
function getRotationValue() {
rotation += -0.03;
return rotation;
}
const typhoonEntity = viewer.entities.add({
name : '颱風路徑',
availability : new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({
start : start,
stop : stop
})]),
position : property,
orientation : new Cesium.VelocityOrientationProperty(property), // 根據位置移動自動計算方向
ellipse : {
semiMinorAxis : 35000.0,
semiMajorAxis : 35000.0,
height: 0.0,
fill: true,
outlineColor: Cesium.Color.RED,
outlineWidth: 5,
outline : false,
rotation: new Cesium.CallbackProperty(getRotationValue, false),
stRotation: new Cesium.CallbackProperty(getRotationValue, false),
material: new Cesium.ImageMaterialProperty({
image: "../images/typhoon.gif",
transparent: true
})
},
point : {
pixelSize : 10,
color : Cesium.Color.TRANSPARENT,
outlineColor : Cesium.Color.YELLOW,
outlineWidth : 4
},
label : {
text: typhoonName,
font : '18px sans-serif',
pixelOffset : new Cesium.Cartesian2(0.0, 50)
},
path : {
resolution : 1,
material : new Cesium.PolylineGlowMaterialProperty({
glowPower : 0.9,
color : Cesium.Color.YELLOW
}),
width : 3
}
})
// 設置飛行視角
viewer.trackedEntity = undefined;
viewer.flyTo(typhoonEntity,{
duration: 2,
offset : {
heading : Cesium.Math.toRadians(0.0),
pitch : Cesium.Math.toRadians(-90),
range : 1500000
}
})
// 飛行視角追蹤
var preUpdateHandler = function(){
if(typhoonEntity){
const center = typhoonEntity.position.getValue(viewer.clock.currentTime);
const hpr = new Cesium.HeadingPitchRange(Cesium.Math.toRadians(0.0), Cesium.Math.toRadians(-90), 1500000)
if(center){
viewer.camera.lookAt(center, hpr)
}
}
}
viewer.scene.preUpdate.addEventListener(preUpdateHandler)
// viewer.zoomTo(typhoonEntity, new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-90), 15000));
// 設置插值函數爲拉格朗日算法
typhoonEntity.position.setInterpolationOptions({
interpolationDegree : 3,
interpolationAlgorithm : Cesium.LagrangePolynomialApproximation
});
}
function computeFlight(start, positions){
const property = new Cesium.SampledPositionProperty();
for(let i=0; i<positions.length; i++){
const time = Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate());
const position = Cesium.Cartesian3.fromDegrees(parseFloat(positions[i].fLongitude), parseFloat(positions[i].fLatitude), Cesium.Math.nextRandomNumber() * 500 + 1750);
property.addSample(time, position);
}
return property;
}
</script>
</body>
效果如下