前言 我们在前面的文章 中介绍过Cesium的Property机制,了解了它的作用以及它的用法,这篇文章我们通过一个实际场景来深入学习和复习一下Property机制。
在智慧城市系统中,必不可少的就是轨迹回放或者让模型沿着指定路径行走,而Cesium提供的Property机制能够在很好的实现这样的场景的同时保证性能。下面简单来讲一下Property,更多的知识可以阅读Cesium的Property机制究竟有多香
property 简单来说,Property就是Cesium提供的一种机制,可以让指定的属性随时间自动变化并赋值,操作便捷的同时能够节省性能。在Cesium中Property可以与时间轴进行关联,并根据时间返回对应的属性,我们再通过返回的值去改变我i们想要修改的状态值。
实现 一样的,首先我们整理思路,想要实现轨迹回放,我们需要一组轨迹坐标串、一个定义好时间点状态的Property,以及一个用来在轨迹上行走的model(本文用billboard代替)。
我们拿到了轨迹坐标串后,需要通过计算获得几个关键节点的距离、时间,生成完整的property作为我们实体的position属性。
基本思路如上,接下来我们开始动手实现。思路只提供大致方向,在实现中可能会对某些方面进行部分修改。一般场景下我们得到的都是经纬度坐标,而操作时需要使用笛卡尔坐标。
1 2 3 4 5 6 7 8 9 10 11 12 13 let lnglatArr = [ [121.527589 , 38.957547 ], [121.527825 , 38.960166 ], [121.536472 , 38.959098 ], [121.540442 , 38.958464 ], [121.543489 , 38.958131 ], [121.542888 , 38.955861 ], [121.542266 , 38.953325 ], ]let positions = lnglatArr.map ((item ) => { return Cesium .Cartesian3 .fromDegrees (item[0 ] , item[1 ] , 0.5 ); })
接下来我们需要获取回放中每段的时间以及总时间,这里我们需要通过速度和坐标点之间的距离算出所需时间(这是小学知识吧,t = l/s)。
我们需要记录每个点位的时间,用来为property添加节点,这里我们将它写成一个方法方便调用。入参就是我们的坐标串与速度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 getSiteTimes (positions, speed ) { let timeSum = 0 ; let times = []; for (let i = 0 ; i < positions.length ; i++) { if (i === 0 ) { times.push (0 ); continue ; } timeSum += this .spaceDistance ([positions[i - 1 ], positions[i]]) / speed; times.push (timeSum); } return { timeSum, siteTimes : times, }; },
可以看到我们在循环内又定义了一个方法spaceDisatance
,这个方法用来计算传入的两个点之间的距离。Cesium为我们提供了相关方法计算两点之间的距离。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 spaceDistance (positions ) { let distance = 0 ; for (let i = 0 ; i < positions.length - 1 ; i++) { let point1cartographic = Cesium .Cartographic .fromCartesian ( positions[i] ); let point2cartographic = Cesium .Cartographic .fromCartesian ( positions[i + 1 ] ); let geodesic = new Cesium .EllipsoidGeodesic (); geodesic.setEndPoints (point1cartographic, point2cartographic); let s = geodesic.surfaceDistance ; s = Math .sqrt ( Math .pow (s, 2 ) + Math .pow (point2cartographic.height - point1cartographic.height , 2 ) ); distance = distance + s; } return distance.toFixed (2 ); },
现在我们拿到了总时间和每两个点之间的时间,现在可以开始构建property和时间轴了。首先我们需要定义开始时间结束时间并给到viewer
的clock
实例上代表时间段,接着构筑Property实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 let timeObj = this .getSiteTimes (positions, speed);let startTime = Cesium .JulianDate .fromDate (new Date ());let stopTime = Cesium .JulianDate .addSeconds ( startTime, timeObj.timeSum , new Cesium .JulianDate () ); viewer.clock .startTime = startTime.clone (); viewer.clock .stopTime = stopTime.clone (); viewer.clock .currentTime = startTime.clone ();var property = new Cesium .SampledPositionProperty ();for (var i = 0 ; i < positions.length ; i++) { const time = Cesium .JulianDate .addSeconds ( startTime, timeObj.siteTimes [i], new Cesium .JulianDate () ); property.addSample (time, positions[i]); }
最后一步,我们添加一个Entity实体,做轨迹回放的物体,这里可以使用任何实体。添加到场景中后需要利用viewer.trackedEntity
动态追踪实体。我们的轨迹回放就大功告成了。
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 this .animateEntity = viewer.entities .add ({ availability : new Cesium .TimeIntervalCollection ([ new Cesium .TimeInterval ({ start : startTime, stop : stopTime, }), ]), position : property, orientation : new Cesium .VelocityOrientationProperty (property), billboard : { image : "./static/blueCamera.png" , verticalOrigin : Cesium .VerticalOrigin .BOTTOM , }, path : { resolution : 1 , width : 10 , material : Cesium .Color .RED , }, }); viewer.trackedEntity = this .animateEntity ;
效果如下:
封装 这里简单提一下利用Class进行封装吧,没有什么大的难点,就是把实现步骤拆分,选定参数进行暴露。这里简单书写一下代码结构。
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 export default class TrackedAnimate { constructor (viewer, cb ) { this .viewer = viewer; this .cb = cb; } startRoam (positions, speed ) { let newPositions = positions.map (item => { return Cesium .Cartesian3 .fromDegrees (item[0 ], item[1 ], 0.5 ); }); this .addRoamLine (newPositions, speed); } addRoamLine (positions, speed ) { this .endRoam (); let timeObj = this .getSiteTimes (positions, speed); let startTime = Cesium .JulianDate .fromDate (new Date ()); let stopTime = Cesium .JulianDate .addSeconds ( startTime, timeObj.timeSum , new Cesium .JulianDate () ); this .viewer .clock .startTime = startTime.clone (); this .viewer .clock .stopTime = stopTime.clone (); this .viewer .clock .currentTime = startTime.clone (); let property = this .getSampledPositionProperty ( positions, startTime, timeObj.siteTimes ); this .addModel (startTime, stopTime, property); this .viewer .trackedEntity = this .animateEntity ; this .timoutId = setTimeout (e => { this .endRoam (); }, timeObj.timeSum * 1000 ); } addModel (startTime, stopTime, property ) { } endRoam ( ) { } getSampledPositionProperty (positions, startTime, siteTimes ) { } getSiteTimes (positions, speed ) { } spaceDistance (positions ) { } }
调用:
1 2 3 4 5 6 this .trackEntity = new TrackedAnimate (viewer , () => { console .log ('moe_' ); });this .trackEntity .startRoam (this .movePositions , 25 );this .trackEntity .endRoam ();
最后 上述我们简单实现了轨迹回放的场景,可以看到在封装中我们还设置了回调函数的参数,后续可以进行很多扩展比如多视角回放,特定位置做特定功能等等,都可以进行扩展。Property的使用场景有很多,这只是其中一部分,感兴趣的可以深入研究共同探讨。