Cesium场景之卫星轨道展示 | DLLCNX的博客
cesium在进行动画展示这一块的功能比较完善。最近有一个需求,需要进行模拟卫星的飞行轨迹,如果可以实现,针对扫描卫星需要添加模拟扫描光波。
当我首先针对需求进行卫星,将卫星运行与扫描进行拆分,当利用cesium的api实现卫星轨道后,能否针对卫星模型绑定一个固定的扫描的模型上去。后期这个想法被我抛弃了,因为我之前二维做的多,三维这一块有部分经验,但是都没有深入了解过,所以我将二维的思想带入了cesium的体系中,也走了不少绕路,cesium针对模拟动画其实有更完美的解决方案,下面我简单介绍一下自己的思维历程。
模型法
首先,我认为既然要实现卫星扫描轨迹,那么肯定需要两个模型:卫星模型和扫描模型。卫星模型网上有,glb或者gltf的都行,而扫描模型我直接使用Cesium的几何模型去实现圆锥即可。
1.初始化Vierer
viewer = new Cesium.Viewer('cesiumContainer', {shouldAnimate: true, // 这个值比较特殊,如果为true页面渲染后自动执行动画,反之需要自己手动去点击开始geocoder: false, // 隐藏查找位置homeButton: false, // 隐藏返回视角到初始位置sceneModePicker: false, // 隐藏视角模式的选择baseLayerPicker: false, // 隐藏图层选择器navigationHelpButton: false, // 隐藏帮助timeline: true, // 隐藏时间轴fullscreenButton: false, // 隐藏全屏按钮animation: true, // 隐藏动画速度控制器infoBox: false, // 点击的详情弹窗entity的description可以描述html显示在弹窗中,也可以通过viewer.infoBox.frame来接入访问});
2. 时间定义
动画的执行都是与时间有关的,从什么时间开始,到什么时间结束。而Cesium使用了不同于我们js常用的时间规范,需要用到Cesium.JulianDate
。Cesium.JulianDate
是指示在 Cesium 中添加或描述对象动画时使用的 Julian Date 格式的时间标签。Julian Date 是以天为单位计数的相对格林尼治标准时间的一种计时法。
所以以下代码意思我们以当前时间为基准,6分钟内执行动画。但是速率是10.
// 定义开始时间
start = Cesium.JulianDate.addHours(new Cesium.JulianDate.fromDate(new Date()), 8, new Cesium.JulianDate());把js中的时间转换为JulianDate时间,东八区时间// 定义结束时间,360秒后
stop = Cesium.JulianDate.addSeconds(start, 360, 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; //循环结束时//时间变化来控制速度 // 时间速率,数字越大时间过的越快
viewer.clock.multiplier = 10;//给时间线设置边界
viewer.timeline.zoomTo(start, stop);
Cesium.JulianDate.addHours()
:是一种可以在 Julian 时间戳上增加指定小时数的方法。Cesium.JulianDate.addSeconds()
同理增加秒。
-
julianDate - Julian 日期时刻,可以是 JulianDate 对象或 secondsSinceEpoch 值。
-
hours - 要添加的小时数,可以为负值表示减小。
-
result - 可选,用于存储结果的 JulianDate 对象。
Cesium.JulianDate.clone()
:用于克隆一个 JulianDate 对象。
viewer.timeline.zoomTo(start, stop)
方法可以用来在时间轴控件中设置一个时间窗口并自动居中和缩放。
- start - 时间窗口起始时间,可以是Date对象或JulianDate对象
- stop - 时间窗口结束时间
3.轨迹处理方法
上面定义好了播放时间以及时间间隔等等,那么准备工作做完了,可以开始构建模型了。
先定义一些存储路径变量的方法:
function mySatePosition(hen) {this.lon = 0; // 经度初始化this.lat = 0; // 纬度初始化this.satelliteHeight = hen; // 卫星高度this.orbitHeight = hen / 2; // 轨道高度this.time = 0; //
}
定义轨迹方法:
可以看出这个方法利用传进去的是否纬度标识,如果是就构造了一个固定纬度degree,经度360度分段的循环数组,反之一样。所以这些变量其实可以根据自己需要定义初始化或者调整。
function getRandState(ifLat, degree, hen) {let arr = [];let lat = Math.floor(Math.random() * 360);for (let i = lat; i <= 360 + lat; i += 30) {let sateP = new mySatePosition(hen); // new一个路径变量类if (ifLat == 'lon') {sateP.lon = degree;sateP.lat = i;} else {sateP.lon = isateP.lat = degree;}sateP.time = i - lat;arr.push(sateP);}return arr
}
经纬度转换为Cesium模型使用的position
function computePosition(source, panduan) {let property = new Cesium.SampledPositionProperty();for (let i = 0; i < source.length; i++) {let time = Cesium.JulianDate.addSeconds(start, source[i].time, new Cesium.JulianDate());let position = Cesium.Cartesian3.fromDegrees(source[i].lon, source[i].lat, panduan === 1 ? source[i].satelliteHeight : source[i].orbitHeight);property.addSample(time, position);}return property;
}
4. 模型构建
定义好上一步方法,定义模型即可。这边三个参数ifLat, degree, hen含义上面方法也需要使用,含义分别就是是否纬度,角度以及卫星高度。
//获取路径let path = getRandState(ifLat, degree, hen);/*** 扫描圆锥*/let entityPath = computePosition(path, 2);let entity = viewer.entities.add({//关联时间轴 TimeIntervalCollection管理时间间隔数据的集合 把时间轴的起止时间同步为实体的availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({start: start,stop: stop})]),position: entityPath,orientation: new Cesium.VelocityOrientationProperty(entityPath),cylinder: {HeightReference: Cesium.HeightReference.CLAMP_TO_GROUND,length: hen,topRadius: 0,bottomRadius: hen / 2,material: Cesium.Color.RED.withAlpha(0.4),outline: true,numberOfVerticalLines: 0,outlineColor: Cesium.Color.RED.withAlpha(0.8)},});//插值器,两个点位之间的模拟插值entity.position.setInterpolationOptions({interpolationDegree: 5,interpolationAlgorithm: Cesium.LagrangePolynomialApproximation});/*** 卫星*/let satellitePath = computePosition(path, 1);let satelliteEntity = viewer.entities.add({// 将实体availability设置为与模拟时间相同的时间间隔。availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({start: start,stop: stop})]),//计算实体位置属性position: satellitePath,//基于位置移动自动计算方向.orientation: new Cesium.VelocityOrientationProperty(satellitePath),//加载飞机模型model: {uri: './sources/kml.glb',minimumPixelSize: 68,scale: 2000.0,},//路径path: {resolution: 1,material: new Cesium.PolylineGlowMaterialProperty({glowPower: 0.1,color: Cesium.Color.GREEN}),width: 5}});//插值器,两个点位之间的模拟插值satelliteEntity.position.setInterpolationOptions({interpolationDegree: 5,interpolationAlgorithm: Cesium.LagrangePolynomialApproximation});
5. 开始执行
我们给第四步添加好ifLat, degree, hen三个参数就可以看到一颗卫星运行起来。我们可以把上一步继续封装来实现多个卫星运行。
Cesium Demo01
CZML法
当我参考网上案例完成上一步卫星轨道的过程中,就发现了其实Cesium对于动画类的模型,其实推荐使用CZML数据驱动的模式,并且上一步实现的卫星轨道有一个问题,轨道高度固定,而往往真实的卫星轨道可能会随时改变。如果大家想要了解CZML是什么,可以访问Cesium官网或者看一下我对于CZML的简单理解Cesium之CZML。简单来说,CZML将动画运行的轨道,模型,渲染样式等等全部以json数据格式的方式写在了czml文档里面,用数据驱动场景渲染。
1.czml定义
我们按照规范可以自己完整的构建一个CZML文档,但是卫星一般网上有能下载到,然后也有转换工具,可以直接生成CZML。下面是一个示例:
注意:因为czml里面包含大量坐标导致过长,填入了下载地址。
test.czml
2. 调用
viewer = new Cesium.Viewer('cesiumContainer', {shouldAnimate: true,geocoder: false, // 隐藏查找位置homeButton: false, // 隐藏返回视角到初始位置sceneModePicker: false, // 隐藏视角模式的选择baseLayerPicker: false, // 隐藏图层选择器navigationHelpButton: false, // 隐藏帮助timeline: true, // 隐藏时间轴fullscreenButton: false, // 隐藏全屏按钮animation: true, // 隐藏动画速度控制器infoBox: false, // 点击的详情弹窗entity的description可以描述html显示在弹窗中,也可以通过viewer.infoBox.frame来接入访问});var clock = viewer.clock;var cylinderEntity = viewer.entities.add({cylinder: {heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,length: 600000,topRadius: 0,bottomRadius: 600000/2,material: Cesium.Color.RED.withAlpha(.4),outline: !0,numberOfVerticalLines: 0,outlineColor: Cesium.Color.RED.withAlpha(.4)}});// 卫星带扫描var property;var satellite = null;viewer.dataSources.add(Cesium.CzmlDataSource.load("xxx/test.czml")).then(function (dataSource) {// 添加扫描satellite = dataSource.entities.getById("Satellite/GAOFEN 1");property = new Cesium.SampledPositionProperty();var date = Cesium.JulianDate.toDate(clock.startTime);for (var ind = 0; ind < 292; ind++) {var time = Cesium.JulianDate.addSeconds(clock.startTime, 300 * ind, new Cesium.JulianDate());var position = satellite.position.getValue(time);var cartographic = viewer.scene.globe.ellipsoid.cartesianToCartographic(position);var lat = Cesium.Math.toDegrees(cartographic.latitude),lng = Cesium.Math.toDegrees(cartographic.longitude),hei = cartographic.height;// console.log(position)property.addSample(time, Cesium.Cartesian3.fromDegrees(lng, lat, hei));}cylinderEntity.position = property;cylinderEntity.position.setInterpolationOptions({ //设定位置的插值算法interpolationDegree: 5,interpolationAlgorithm: Cesium.LagrangePolynomialApproximation});});
代码部分有点难以理解,其实我们viewer.dataSources.add(Cesium.CzmlDataSource.load("xxx/test.czml"))
就可以加载卫星轨道以及模拟,但是,因为czml驱动的动画,我们如果想要获取模型entity,那么可能就得通过xxx.then
函数链来获取具体内容了。里面的操作逻辑其实就是分段获取每个时刻卫星的状态,然后又通过转换对应赋值给圆锥实体,这样可以实现效果,但是比较麻烦。能不能进一步优化?
3. 优化
既然czml可以定义模型,是否可以同时定义圆锥和卫星模型,然后两个使用同一份动画数据呢?答案是可以。
我们在第一步czml文件中直接添加圆锥模型,省略了其它没变的地方:
[{...},{...,"model": {"gltf": "../sources/kml.glb","scale": 1,"minimumPixelSize": 128},"cylinder": {"length": 650000,"topRadius": 0,"bottomRadius": 300000,"heightReference": "CLAMP_TO_GROUND","outline": true,"numberOfVerticalLines": 0,"material": [214,88,148, 0.4],"outlineColor": [214,88,148, 0.4]},...}
]
注意:czml定义和代码定义时有一些变量是无法使用的,只能用基本数据类型。
viewer = new Cesium.Viewer('cesiumContainer', {shouldAnimate: true,geocoder: false, // 隐藏查找位置homeButton: false, // 隐藏返回视角到初始位置sceneModePicker: false, // 隐藏视角模式的选择baseLayerPicker: false, // 隐藏图层选择器navigationHelpButton: false, // 隐藏帮助timeline: true, // 隐藏时间轴fullscreenButton: false, // 隐藏全屏按钮animation: true, // 隐藏动画速度控制器infoBox: false, // 点击的详情弹窗entity的description可以描述html显示在弹窗中,也可以通过viewer.infoBox.frame来接入访问});// 高分一号,带扫描var property;var satellite = null;viewer.dataSources.add(Cesium.CzmlDataSource.load("xxx/gf.czml"))
代码清晰简单多了,将所有的数据定义全部放在了czml文件中。当然我们可以多找几份czml文件,然后添加渲染。
Cesium Demo02
4. 存在的问题
因为卫星轨道不再如方法一一样是固定高度,那么存在了一个问题就是扫描高度没法动态跟着联动,后边我需要再研究研究czml文档规范去优化。
参考资料
cesium 卫星环绕扫描_cesium 卫星扫描-CSDN博客