core/Roaming.js

/*
 * 路径漫游主类
 * @Author: jianlei wang
 * @Date: 2024-02-26 10:59:50
 * @Last Modified by: jianlei wang
 * @Last Modified time: 2024-03-28 14:06:41
 */
import { Cesium } from '../../namespace'
import { shouldAnimate } from '../utils/Clock'

/**
 * 路径漫游主类
 * @class
 */
class Roaming {
  /**
   * 构造函数
   * @param {Object} viewer - 地图场景对象
   */
  constructor(viewer) {
    this._viewer = viewer
    this._id = 'first-person-navigation'
  }

  /**
   * 按照路径节点,自动执行第一人称路径漫游动画
   * @param {Array<DegreePosZ>} points - 路径相机位置点集合
   * @param {Array<Number>} [times=null] - 时间集合,时间戳:毫秒),注意:如果要定义,请确保时间集合与路径点一一对应;若设置为null,则默认间隔为10s的时间集合
   * @param {Boolean} path - 是否显示路径线
   */
  async play(points, times, path) {
    this.removeAll()
    const viewer = this._viewer
    shouldAnimate(this._viewer, true)
    const option = this._getOption(points, times)
    const czml = this._initCZML(option, path)
    // 定义变量,模型和视角跟随事件
    // 加载数据
    this._trackItem = await viewer.dataSources.add(
      Cesium.CzmlDataSource.load(czml)
    )
    // 获取模型对象
    const entity = this._trackItem.entities.getById(this._id)
    if (!entity) return
    viewer.trackedEntity = entity
    // 获取当前模型方向和位置
    const { orientation, position } = entity

    // 实时调整位置
    function adjust() {
      if (viewer.clock.shouldAnimate === true) {
        let ori = orientation.getValue(viewer.clock.currentTime) // 获取偏向角
        let center = position.getValue(viewer.clock.currentTime) // 获取位置
        // 1、由四元数计算三维旋转矩阵
        var mtx3 = Cesium.Matrix3.fromQuaternion(ori)
        // 2、计算四维转换矩阵:
        var mtx4 = Cesium.Matrix4.fromRotationTranslation(mtx3, center)
        // 3、计算角度:
        var hpr = Cesium.Transforms.fixedFrameToHeadingPitchRoll(mtx4)
        // 获取角度(弧度)
        const headingTemp = hpr.heading
        const pitchTemp = hpr.pitch
        // 调整角度为第一人称
        const heading = Cesium.Math.toRadians(
          Cesium.Math.toDegrees(headingTemp) + 90
        )
        const pitch = Cesium.Math.toRadians(
          Cesium.Math.toDegrees(pitchTemp) - 12
        )
        // 视角高度,根据模型大小调整
        const range = 5.0
        // 动态改变模型视角
        viewer.camera.lookAt(
          center,
          new Cesium.HeadingPitchRange(heading, pitch, range)
        )
      }
    }
    this._onTickEvent = viewer.clock.onTick.addEventListener(adjust)
  }

  /**
   * 暂停漫游
   */
  pause() {
    shouldAnimate(this._viewer, false)
  }

  /**
   * 继续漫游
   */
  continue() {
    shouldAnimate(this._viewer, true)
  }

  /**
   * 清除所有
   */
  removeAll() {
    this._onTickEvent && this._onTickEvent()
    this._viewer.trackedEntity = undefined
    this._trackItem && this._viewer.dataSources.remove(this._trackItem)
  }

  _getOption(points, times) {
    const length = points.length
    if (!times || times.length != length) {
      const time = new Date().getTime()
      times = Array.from({ length }, (_, index) => time + index * 10000)
    }
    let cartographicDegrees = []
    for (let index = 0; index < length; index++) {
      const point = points[index]
      cartographicDegrees.push((times[index] - times[0]) / 1000)
      cartographicDegrees = cartographicDegrees.concat(point)
    }
    const startTime = new Date(times[0]).toISOString()
    const endTime = new Date(times[times.length - 1]).toISOString()
    const option = {
      interval: `${startTime}/${endTime}`,
      currentTime: startTime,
      cartographicDegrees,
    }
    return option
  }
  _initCZML(option, show) {
    const czml_team = [
      {
        id: 'document',
        name: this._id,
        version: '1.0',
        clock: {
          interval: option.interval,
          currentTime: option.currentTime,
        },
      },
      {
        id: this._id,
        name: 'path with GPS flight data',
        description: '第一人称漫游。',
        availability: option.interval,
        path: {
          material: {
            polylineGlow: {
              color: {
                rgba: [0, 0, 255, 50],
              },
              glowPower: 0.1,
              taperPower: 0.1,
            },
          },
          width: 20,
          show: show,
        },
        orientation: {
          // 自动计算方向
          velocityReference: '#position',
        },
        position: {
          // 插值算法
          interpolationAlgorithm: 'LAGRANGE',
          interpolationDegree: 5,
          epoch: option.currentTime,
          cartographicDegrees: option.cartographicDegrees,
        },
      },
    ]
    return czml_team
  }
}

export default Roaming