一场大的台风,路径通常很长,可能从靠近赤道的太平洋一直往北吹到东三省。跨度这么大,在三维GIS中,往往不容易看全。如果能够实现相机随动效果,即相机跟随台风步进、移动,就生动许多了。

cesium 相机跟随_flyTo


cesium 相机跟随_flyTo_02

感觉在cesium中,坐标和视角、相机、view是一个难点,我到现在都搞不清楚。方向(orientation)可以总结一下:

XYZ轴,由负到正,分别对应西东、南北、下上。(cesium是右手)

cesium 相机跟随_数据_03

heading:视场角,观察者(相机?)与地球围绕Y轴转,可以想象为相机向左,地球就向右;反之。

pitch:俯仰角,观察者与地球围绕X轴转;观察者低头,看见地球南极,抬头,看见地球北极。

roll:旋转,观察者与地球围绕Z轴转;可以想象为地球就是一个平面的饼,在围绕圆心在转。

cesium 相机跟随_数据_04

cesium 相机跟随_合适坐标点_05

言归正传。一场台风在绘制过程中,每个步骤都有对应的一个坐标值。只要观察者能够跟随这个脚步,从空中俯视,就能观察到每一个步骤的细节。

问题是,如何才能跟随?

相机(camera)的话,有lookAt啦,setView啦,flyTo啦。前面2个在本场景中都用不上,相机不动,光调转角度不行。只有flyTo才会跟随。

但是,flyTo方法,既有viewer.flyTo,又有camera.flyTo。viewer.flyTo,飞向指定坐标点(也就是台风的某个步骤坐标点),该坐标点就会正正好出现在屏幕中央,最符合要求;而camera.flyTo,是相机飞到指定点,如果按照默认值,相机角度是正俯视地球,那么没有问题,指定点也会在屏幕中央;但问题是,相机往往设置了各种倾斜的角度,那么待观察的点就不会在屏幕中央,而是出现在一边,甚至根本看不见!

那我们还犹豫什么,就用viewer.flyTo就好了。不行啊,这个viewer.flyTo,如果当前有数据输出,画面在更新,它不会执行,只有等待一切平静后它才客客气气地执行。这就出现一场台风全部步骤绘制完毕以后,才执行这个viewer.flyTo,黄花菜都凉了。

所以,解决办法还是应该用camera.flyTo。飞到一个合适的坐标点,可以让被观察点(即台风步骤坐标点)出现在屏幕中央。现在的问题是,如何得到这个合适的坐标点,让相机飞过去?

可不可以根据相机的角度,和被观察点的坐标,倒推出这个坐标点,以至于相机来到这里,角度啥的不变,刚好让被观察点出现在屏幕中央?

这个不是方程式,我没有找到计算方法。但是也许可以间接计算。我是这么想的:

台风刚开始的时候,我们先viewer.flyTo台风起点,这时台风起点出现在屏幕中央;

台风前进一步,来到了一个新位置,这时,可以计算它与上一步的位移;

然后我用相机当前位置,加上这个位移,得到一个新的坐标点,这就是那个合适的坐标点

相机飞到这个该死的合适坐标点

以此类推。

cesium 相机跟随_cesium_06

代码如下:

//台风开始时,我们飞向起点p
let flyEntity = new Cesium.Entity({
id : 'flyTmp',
position : Cesium.Cartesian3.fromDegrees(p.x,p.y,0),
point : {
pixelSize : 1,
color : Cesium.Color.WHITE.withAlpha(0.0),
}
});
viewer.entities.add(flyEntity);

return viewer.flyTo(flyEntity, {
offset : {//角度啥都不变
heading : viewer.camera.heading,
pitch : viewer.camera.pitch,
range : HEIGHT
}
});
//一个台风步骤
function step(){
let p1 = ... //上一步骤
let p2 = ... //当前步骤

let cp = viewer.camera.positionCartographic;//当前相机世界坐标

let offset = Cesium.Cartesian3.subtract(//两个台风步骤之间的位移
Cesium.Cartesian3.fromDegrees(p1.x, p2.y,cp.height), //这个高度必不可少
Cesium.Cartesian3.fromDegrees(p1.x, p2.y,cp.height),
new Cesium.Cartesian3());

let newCameraLoc = Cesium.Cartesian3.add(//新的相机坐标点
viewer.camera.position,
offset,
new Cesium.Cartesian3());

viewer.camera.flyTo({
destination : newCameraLoc,
duration:2,//飞行时间2秒
orientation : {
heading : viewer.camera.heading,
pitch : viewer.camera.pitch,
roll : 0.0
},
complete:function(){//完成后进入下一步;职责链模式
step();
}
});
}

当然啦,实际应用过程中,不会步步跟随,而是20步跟随一次,否则系统估计会卡死。

​cesium中定位方法使用​