cesium在进行动画展示这一块的功能比较完善。最近有一个需求,需要进行模拟卫星的飞行轨迹,如果可以实现,针对扫描卫星需要添加模拟扫描光波。
当我首先针对需求进行卫星,将卫星运行与扫描进行拆分,当利用cesium的api实现卫星轨道后,能否针对卫星模型绑定一个固定的扫描的模型上去。后期这个想法被我抛弃了,因为我之前二维做的多,三维这一块有部分经验,但是都没有深入了解过,所以我将二维的思想带入了cesium的体系中,也走了不少绕路,cesium针对模拟动画其实有更完美的解决方案,下面我简单介绍一下自己的思维历程。
模型法 首先,我认为既然要实现卫星扫描轨迹,那么肯定需要两个模型:卫星模型和扫描模型。卫星模型网上有,glb或者gltf的都行,而扫描模型我直接使用Cesium的几何模型去实现圆锥即可。
1.初始化Vierer 1 2 3 4 5 6 7 8 9 10 11 12 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 , });
2. 时间定义 动画的执行都是与时间有关的,从什么时间开始,到什么时间结束。而Cesium使用了不同于我们js常用的时间规范,需要用到Cesium.JulianDate
。Cesium.JulianDate
是指示在 Cesium 中添加或描述对象动画时使用的 Julian Date 格式的时间标签。Julian Date 是以天为单位计数的相对格林尼治标准时间的一种计时法。
所以以下代码意思我们以当前时间为基准,6分钟内执行动画。但是速率是10.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 start = Cesium .JulianDate .addHours (new Cesium .JulianDate .fromDate (new Date ()), 8 , new Cesium .JulianDate ()); 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.轨迹处理方法 上面定义好了播放时间以及时间间隔等等,那么准备工作做完了,可以开始构建模型了。
先定义一些存储路径变量的方法:
1 2 3 4 5 6 7 function mySatePosition (hen ) { this .lon = 0 ; this .lat = 0 ; this .satelliteHeight = hen; this .orbitHeight = hen / 2 ; this .time = 0 ; }
定义轨迹方法:
可以看出这个方法利用传进去的是否纬度标识,如果是就构造了一个固定纬度degree,经度360度分段的循环数组,反之一样。所以这些变量其实可以根据自己需要定义初始化或者调整。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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); if (ifLat == 'lon' ) { sateP.lon = degree; sateP.lat = i; } else { sateP.lon = i sateP.lat = degree; } sateP.time = i - lat; arr.push (sateP); } return arr }
经纬度转换为Cesium模型使用的position
1 2 3 4 5 6 7 8 9 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含义上面方法也需要使用,含义分别就是是否纬度,角度以及卫星高度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 let path = getRandState (ifLat, degree, hen); let entityPath = computePosition (path, 2 ); let entity = viewer.entities .add ({ 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 : 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. 调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 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 , }); 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 ; 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文件中直接添加圆锥模型,省略了其它没变的地方:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [ { ... } , { ..., "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定义和代码定义时有一些变量是无法使用的,只能用基本数据类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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 , }); var property; var satellite = null ; viewer.dataSources .add (Cesium .CzmlDataSource .load ("xxx/gf.czml" ))
代码清晰简单多了,将所有的数据定义全部放在了czml文件中。当然我们可以多找几份czml文件,然后添加渲染。
Cesium Demo02
4. 存在的问题 因为卫星轨道不再如方法一一样是固定高度,那么存在了一个问题就是扫描高度没法动态跟着联动,后边我需要再研究研究czml文档规范去优化。
参考资料 cesium 卫星环绕扫描_cesium 卫星扫描-CSDN博客