开局一张图,如图实现船舶的运动轨迹回放。
原理
基于Entity的availability属性和postion属性。
其中availability可以设置时间轴,position中定义了Entity的位置和时间信息。
时间轴类似于一个触发器,当在某个时间,模型就移动到某个地方,因为是一个序列,模型不会跳跃,而是会计算一个均速移动过去。
实现
想要实现轨迹回放,我们需要一组轨迹坐标组、一个定义好时间点状态的Property,以及船舶图片(也可以使用model代替)。
拿到轨迹坐标组后,通过计算获取节点的距离,时间,生成完整可用的Property作为我们Entity的position属性。一般拿到的坐标点数据是经纬度坐标,需要转换为笛卡尔坐标。
构建Property和时间轴。定义开始时间结束时间并给到viewer的clock实例上代表时间段。
const startTime = new Date(dayjs(trackList[0].dynamicTime).valueOf())
const endTime = new Date(dayjs(maxTime).valueOf())
const start = Cesium.JulianDate.fromDate(startTime)
const stop = Cesium.JulianDate.fromDate(endTime)
const positionProperty = new Cesium.SampledPositionProperty()
positionProperty.setInterpolationOptions({
interpolationDegree: 1,
interpolationAlgorithm: Cesium.LagrangePolynomialApproximation
})
for (let i = 0; i < trackList.length; i++) {
const { longitude, latitude } = trackList[i]
const position = Cesium.Cartesian3.fromDegrees(longitude, latitude, 0)
const time = new Date(dayjs(trackList[i].dynamicTime).valueOf())
const csTime = Cesium.JulianDate.fromDate(time)
positionProperty.addSample(csTime, position)
}
最后,添加一个Entity实体,做轨迹回放的物体,添加到场景中后利用viewer.trackedEntity动态追踪实体。
this.entity = viewer.entities.add({
name: '目标船舶',
availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({ start, stop })]),
position: positionProperty,
orientation: new Cesium.VelocityOrientationProperty(positionProperty),
billboard: {
show: true,
image: shipIcon,
width: 36,
height: 36,
rotation: new Cesium.CallbackProperty((time, result) => {
return 2 * Math.PI - degree2Radian(this.getCurTimeDir(trackList, time))
}, false)
},
label: {
text: String(data.mmsi),
font: '500 28px Helvetica',
scale: 0.6,
style: Cesium.LabelStyle.FILL,
fillColor: Cesium.Color.WHITE,
pixelOffset: new Cesium.Cartesian2(0, -50),
showBackground: false
}
})
viewer.trackedEntity = this.entity
封装成Class
封装成一个类,构建轨迹回放图层。大致结构设计如下。
/**
* 船舶轨迹图层
* 提供多船舶,船舶时间点,轨迹线的绘制
*/
import { degree2Radian, validateCoordLnglat } from '@/utils'
import { Entity, Viewer } from 'cesium'
import shipIcon from '@/assets/ships/ico_ship_highlight.png'
import dayjs from 'dayjs'
import * as Cesium from 'cesium'
/**
* 轨迹参数
*/
export interface ITrackOptions {
/**
* 航向角
*/
courseAngle: number
/**
*
*/
dataStage: string
/**
* 时间
*/
dynamicTime: string
/**
* 纬度
*/
latitude: number
/**
* 经度
*/
longitude: number
/**
* 航速
*/
speed: number
/**
* 目标mmsi
*/
targetId: string
/**
* tb名
*/
tbname: string
/**
* 真实航向
*/
trueHeading: number
/**
* 经度
*/
x: number
/**
* 纬度
*/
y: number
}
/**
* 船舶轨迹图层可配置参数
*/
export interface ITrackLayerOptions {
/**
* 图层名
*/
name?: string
/**
* viewer
*/
viewer: Viewer
/**
* 轨迹数据
*/
data: {
/**
* MMSI
*/
mmsi: number
/**
* 轨迹数据列表
*/
trackList: ITrackOptions[]
/**
* 轨迹数据开始时间
*/
minTime: number
/**
* 轨迹数据结束时间
*/
maxTime: number
}
/**
* 是否启用自动动画
*/
isRun?: boolean
/**
* 初始化播放速度
*/
multiplier?: number
}
export class TrackLayer {
protected options: ITrackLayerOptions
// 存放船舶实体
protected entity: Entity | undefined
// 存放路径
protected path: Entity | undefined
protected polyLine: Entity | undefined
protected points: any[] = []
public constructor(options: ITrackLayerOptions) {
this.options = {
name: 'TrackLayer',
...options
}
}
/**
* 暴露渲染轨迹方法
*/
public renderTrack() {
const { isRun, multiplier } = this.options
this.renderShipAnimation()
if (isRun) this.play()
if (multiplier) this.speed(multiplier)
}
/**
* 开启动画
*/
public play() {
const { viewer } = this.options
viewer.clock.shouldAnimate = true
}
/**
* 暂停动画
*/
public stop() {
const { viewer } = this.options
viewer.clock.shouldAnimate = false
}
/**
* 播放速度
* @param multiplier 速度
*/
public speed(multiplier: number) {
const { viewer } = this.options
viewer.clock.multiplier = multiplier
}
/**
* 移除轨迹
*/
public removeTrack() {}
/**
* 绘制船舶轨迹动画
*/
private renderShipAnimation() {}
/**
* 目标运动路径动画
* @returns
*/
private renderShipAndPath() {}
/**
* 获取当前时间目标对应航向
* @param {Array} data 轨迹数据
* @param {timestamp} time 当前播放器时刻
*/
private getCurTimeDir(data: ITrackOptions[], time: any) {}
}