Cesium实现船舶的轨迹回放

1,022 阅读2分钟

开局一张图,如图实现船舶的运动轨迹回放。

图片

原理

基于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) {}

}