重点思路:在于如何实现从圆锥上面到下面的动态扫描。
以下实现的Cesium版本为1.106。最终要实现的效果如下:
Cesium快速入门到精通系列教程九:实现飞机动态圆锥形光柱
目的是将地球定位到自己需要的区域。
// 场景定位
viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(121.195,21.813,738947.02),
orientation :{
heading : Cesium.Math.toRadians(355.1),
pitch : Cesium.Math.toRadians(-75.3),
roll :0.0
}
});
此处分为两个步骤,第一步进行动态数据的整理与拟合,第二步进行加载模型。
此处主要运用了Cesium中的SampledPositionProperty类,该类的目的是给定不同时间的位置信息,返回线性插值后平滑的位置数据,优点在于位置轨迹线性变化,不突兀,丝滑具有物理世界的真实感。
// 定义一个数组,存储物体运动的时间和位置点
let positionData = [
{ time: 0, longitude: 120.49155063, latitude: 25.47218132,height:80000 },
{ time: 25, longitude: 119.7230824, latitude:23.027839,height:80000 },
];
let positionSampler = new Cesium.SampledPositionProperty();
positionSampler.setInterpolationOptions({
interpolationDegree: 2,
interpolationAlgorithm: Cesium.HermitePolynomialApproximation
});
let startTime = new Cesium.JulianDate.now()
for (let i = 0; i < positionData.length; i++) {
let data = positionData[i];
let time = new Cesium.JulianDate.addSeconds(startTime, data.time, new Cesium.JulianDate());
let position = new Cesium.Cartesian3.fromDegrees(data.longitude, data.latitude,data.height);
positionSampler.addSample(time, position);
}
此处注意调整scale(模型比例)参数的设置,避免模型看不到的现象,最后记得跳转到模型本身。
let model_entity = viewer.entities.add({
name: "model_entity",
position: positionSampler,
orientation : new Cesium.VelocityOrientationProperty(positionSampler),
// 设置朝向
model: {
show: true,
uri: "/Assets/Cesium_Air.glb",
scale: 4000, // 缩放比例
minimumPixelSize: 128, // 最小像素大小
maximumScale: 20000, // 模型的最大比例尺大小。minimumPixelSize的上限
incrementallyLoadTextures: true, // 加载模型后纹理是否可以继续流入
runAnimations: true, // 是否应启动模型中指定的glTF动画
clampAnimations: true, // 指定glTF动画是否应在没有关键帧的持续时间内保持最后一个姿势
shadows: Cesium.ShadowMode.ENABLED,
heightReference: Cesium.HeightReference.NONE,
color:Cesium.Color.WHITE.withAlpha(1)
},
});
viewer.zoomTo(model_entity); //定位到模型
首先圆锥体的类型是“cylinder”,参数的含义分别为length(圆锥体的高度)、topRadius(圆锥体顶部半径)、bottomRadius(圆锥体底部半径)、material(圆锥体颜色)。设置圆锥体position的时候,需要注意两点:1、利用Cesium的CallbackProerty属性去动态设置position,2、设置圆锥体height的时候,要设置为飞机模型高度的一半,因为圆锥体的position在圆锥体的中心位置。
// 飞机扫描轴体
let cylinder_model = viewer.entities.add({
name:'cylinder_entity',
cylinder: {
length: 80000, //圆锥高度
topRadius: 0.0, //顶部半径
bottomRadius: 20000.0, //底部半径
material: Cesium.Color.fromCssColorString(`rgba(255,0,0,0.5)`), //圆的颜色,
},
position: new Cesium.CallbackProperty((time)=>{
let pos = model_entity.position.getValue(time)
if(!pos) return
let cartographic = Cesium.Ellipsoid.WGS84.cartesianToCartographic(pos);
let lon = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
return Cesium.Cartesian3.fromDegrees(lon,lat,40000)
},false)
})
此处分为两个步骤讲解,第一步:绘制圆形平面,第二步是让圆半径和高度动态改变。
注意cesium中无直接绘制圆形,是通过椭圆进行绘制的,entity的类型为ellipse,部分参数解释:semiMinorAxis(短半轴)、semiMajorAxis(长半轴)、height为高度。
let circle_height = 0
let circle_radius = 0
let circle_model = viewer.entities.add({
name:'circle_entity',
position: new Cesium.CallbackProperty((time)=>{
let pos = model_entity.position.getValue(time)
if(!pos) return
let cartographic = Cesium.Ellipsoid.WGS84.cartesianToCartographic(pos);
let lon = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
return Cesium.Cartesian3.fromDegrees(lon,lat,80000)
},false),
ellipse: {
semiMinorAxis: new Cesium.CallbackProperty(()=>{
return circle_radius
},false), // 短轴
semiMajorAxis:new Cesium.CallbackProperty(()=>{
return circle_radius
},false), // 长轴
material: Cesium.Color.RED.withAlpha(1),
outline: true,
outlineColor: Cesium.Color.RED,
outlineWidth: 10,
height: new Cesium.CallbackProperty(()=>{
return circle_height
},false),
}
})
此处利用定时去动态改变圆的半径和高度,需要注意的是,此处采用cesium中math.lerp函数对中间值进行插值,该函数的作用是:通过输入参数(a,b,比例)去获取a,b之间对应比例的值。因此圆的半径应该由小到大(0-20000),圆的高度应该由高到低(80000-0)。
// 每100ms更新圆的大小与高度,3000ms为一循环。
let i = 0
let timeInterval = setInterval(()=>{
if(i>3000){
i = 0
}else{
i +=100
}
circle_height = Cesium.Math.lerp(80000,0,i/3000)
circle_radius = Cesium.Math.lerp(0,20000,i/3000)
},100)
此处主要设置cesium时间轴的起始时间、终止时间等相关参数,需要注意的是记得最后销毁定时器。
let stopTime = Cesium.JulianDate.addSeconds(startTime, 30, new Cesium.JulianDate());
viewer.clock.startTime = startTime.clone();
viewer.clock.stopTime = stopTime.clone();
viewer.clock.currentTime = startTime.clone();
viewer.clock.shouldAnimate = true;
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
viewer.clock.multiplier = 1;
viewer.clock.onStop(()=>{clearInterval(timeInterval)})
全部代码:
全部代码