提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
在Cesium的学习中,学会读文档十分重要!!!在这里附上中文文档Cesium中文文档1.95
在Cesium中进行沿轨迹运动比较简单,大致分5步,先加载小车模型,然后设置当前的时间及开始时间,接下来加入轨迹点,并对其插值,然后为轨迹动画加入时间,最后为小车模型加入轨迹和朝向即可。
一、加载小车模型
我们利用viewer.entitis.add来加载模型,在这里先查看官方文档了解一下
从viewer中可以找到其属性entities,中文文档描述为获取未绑定到特定数据源的实体集合,可能在这里还难以理解(可能中文文档是社区直译过来的),我们点击右侧的EntityCollection进去看看
结合这里可以看出,这里是展示在地球中的entity的集合,也就是说可以通过将模型加入这个集合来展示到地球中,这正是我们需要的。查看其方法可以看见一个add方法,其可以将entity加入此集合,返回该entity对象。
所以,要展示小车模型,需先将小车模型转为entity,再加入EntityCollection。那么该如何将小车模型转为entity呢。我们点进entity看看。
简单了解到entity其实就是可以展示在地球上的实体对象,在其中我们注意到一个属性model,用来设置或获取模型。
浏览文档可以发现,这正是我们要找的gltf/glb模型,其中可以通过uri属性加入路径中的模型文件,因此加入小车模型就变的简单了。这里加入minimumPixelSize和 maximumScale属性方便浏览小车模型,还可以为其添加position属性,可以看见小车的初始位置
const entityMotion = viewer.entities.add(
new Cesium.Entity({
model: {
uri: url,// 模型路径
minimumPixelSize: 128,// 最小像素大小
maximumScale: 20000,// 最大比例尺
},
})
);
二、设置当前及开始时间
Cesium的动画与时间息息相关,因此我们要为运动轨迹添加起始时间及当前时间,并开启场景动画。时间可以在viewer的clock设置(这里相当于将获取与viewer绑定的clock对象),这里把起始时间和当前时间都设置成了现在的时间,Cesium.JulianDate是表示天文儒略日期,即自 -4712 年 1 月 1 日(公元前 4713 年)中午以来的天数,now获取当前的时间,clock对象的几个属性(startTime,currentTime等)的类型都是Cesium.JulianDate。
//开启动画
viewer.clock.shouldAnimate = true;
//设置当前时间
viewer.clock.currentTime = Cesium.JulianDate.now();
//设置起始时间
const startTime = Cesium.JulianDate.now();
这里我们把起始时间先存起来,方便后续设置轨迹动画的时间
三、加入轨迹点,并对其插值
加入轨迹点的部分比较关键,首先,咱们先简单了解一下Cesium里的坐标系统里的两种,一个是三维笛卡尔坐标,一个是地理坐标系,三维笛卡尔坐标是椭球中心为原点的空间直角坐标系,在Cesium是通过 new Cesium.Cartesian3(x,y,z)创建,地理坐标系则是默认为wgs84为参考,通过new Cesium.Cartographic (lon,lat,height)创建。但是,在进行计算时,笛卡尔坐标更方便,因此,在Cesium中,entity的position属性的类型是PostionProperty,
再看看PositionProperty,它是用来定义实体在场景中位置的属性,它可以表示一个静态位置、一个随时间变化的位置,或者其他位置属性,也就是Cartesian3笛卡尔三维坐标系下的坐标与时间的集合。因此如果是坐标经纬度我们则需要将其转换为笛卡尔坐标(坐标系在我后面的专栏中会介绍)。
在这里我们先假设一些轨迹点的笛卡尔三维坐标。假设小车的轨迹正是沿这些点运动。
const pos = [
{ "x": 211806.85957262348, "y": -5014329.870144445, "z": 3922801.0928523946 },
{ "x": 577385.9583266769, "y": -4938316.887198847, "z": 3981533.981599306 },
{ "x": 750598.459437521, "y": -4990762.968138716, "z": 3886760.285356019 },
{ "x": 592151.816210726, "y": -5114279.084725079, "z": 3752163.222673417 },
{ "x": 288850.23764454946, "y": -5133829.536730374, "z": 3761092.1832171273 }
];
但是如果直接为这些点附上时间会存在一些问题,因为小车直接沿两点间进行直线运动,因此小车可能会进入地球内部。所以我们在这里可以为点进行插值,并将其放入一个新数组中。在Cesium中有一个内置的线性插值静态函数。
我们可以根据距离插值,也可以按特定数量插值,这里为了方便,我们为每个两点间插入98个点
const postions = [];
for (let i = 0; i < pos.length - 1; i++) {
for (let j = 0; j <= 1; j += 0.01) {
postions.push(Cesium.Cartesian3.lerp(pos[i], pos[i + 1], j, new Cesium.Cartesian3()));
}
}
四、为轨迹动画加入时间
接下来是为上面的插值点加入时间,加入时间,小车才能从当前时间所在点运动到下一时间所在点。从上一节可以知道,小车的轨迹Position的类型是PositionProperty,但PositionProperty只是一个接口,SampledPositionProperty是这个接口的具体实现对象。
上一节已经实现了坐标插值,现在要对每个坐标加入时间。我们通过JulianDate的静态方法addSeconds来加入秒数,并将对应的坐标通过addSample方法来添加样本对象。
const positionProperty = new Cesium.SampledPositionProperty();
postions.forEach((poss, index) => {
const time = Cesium.JulianDate.addSeconds(startTime, index, new Cesium.JulianDate());
positionProperty.addSample(time, poss);
})
五、为小车模型加入轨迹和朝向
最后,我们将轨迹加入小车,并根据轨迹计算小车的朝向。
entityMotion.position = positionProperty;
const orientationProperty = new Cesium.VelocityOrientationProperty(positionProperty);
entityMotion.orientation = orientationProperty;
为了方便显示,我们还可以显示线轨迹
viewer.entities.add({
polyline: {
positions: pos,
clampToGround: true, //贴地
width: 3,
material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.YELLOW,
}),
depthFailMaterial: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.YELLOW,
}),
},
})
最终结果如下
六、代码封装
上面已经完成了小车运动的基本功能,但是,上面的代码都是写死的,为了方便代码复用,我们可以对以上代码进行封装。
首先,设计一个构造函数
export default class Motion {
/**
*
* @param {*} viewer Cesium.Viewer
*/
constructor(viewer) {
this.viewer = viewer;
this._entityMotion = null// 运动的模型
this._postions = [];//运动轨迹
}
}
然后就是添加运动模型
/**
* 添加运动模型
* @param {*} entity 运动的模型
*/
addModle(entity) {
this._entityMotion = this.viewer.entities.add(entity);
}
```
开始运动
/**
* 开始运动
* @param {*} positions 运动轨迹(点)
*/
startMotion(positions) {
if (!this.viewer.clock.shouldAnimate) {
this.viewer.clock.shouldAnimate = true;
}
this.viewer.clock.currentTime = Cesium.JulianDate.now();// 设置当前时间
for (let i = 0; i < positions.length - 1; i++) {
for (let j = 0; j <= 1; j += 0.01) {
this._postions.push(Cesium.Cartesian3.lerp(positions[i], positions[i + 1], j, new Cesium.Cartesian3()));
}
}
const positionProperty = new Cesium.SampledPositionProperty();
const startTime = Cesium.JulianDate.now();
this._postions.forEach((pos, index) => {
const time = Cesium.JulianDate.addSeconds(startTime, index, new Cesium.JulianDate());
positionProperty.addSample(time, pos);
})
this._entityMotion.position = positionProperty;
const orientationProperty = new Cesium.VelocityOrientationProperty(positionProperty);
this._entityMotion.orientation = orientationProperty;
//this.viewer.trackedEntity = this._entityMotion;//是否追踪小车
}
在Cesium中,不用的资源要及时释放,不然会很卡。
// 清除所有
clearAll() {
// 清空位置数组
this._postions = [];
// 移除实体运动
this.viewer.entities.remove(this._entityMotion);
this._entityMotion = null;
}
这里,为了方便控制小车,我们还可以通过改变时间来改变小车的运动状态
/**
* 修改运动状态
* @param {*} state 运动状态true 或 false
*/
modifyMotionState(state) {
this.viewer.clock.shouldAnimate = state;
}
/**
* 修改运动速度
* @param {*} speed 速度
*/
modifySpeed(speed) {
this.viewer.clock.multiplier = speed;
}
后续,我们就可以通过新建一个Motion对象来直接控制小车了,并且通过视图上的按钮来控制小车的运动,线的绘制(见我的其他文章,可能暂时还没写),把绘制的线的点集合当参数调用startMotion,就可以实现绘制小车轨迹然后小车沿轨迹运动了。
大家对内容满意的话记得点赞关注!