Cesium根据socket接口实时数据,进行平滑位移

4 阅读2分钟

通过entity加载的实体,根据后端socket请求数据,经纬度发生变化,平滑移动

class Device {
  /**
   * 设备基类
   * @param {*} mapView 地图操作类
   * @param {*} id 设备id
   * @param {*} modelUri 模型
   */
  constructor(mapView, id, modelUri, options) {
    this.mapView = mapView
    this.mapView.map.clock.shouldAnimate = true
    this.id = id
    this.entity = null
    this.imageEntity = null
    this.label = null
    this.speed = 0
    this.lon = 0
    this.lat = 0
    this.heading = 0
    this.modelUri = modelUri
    this.modelScale = 1
    this.isDrill = false
    this.groundHeight = 0
    this.sampledPositionProperty = new Cesium.SampledPositionProperty()
    if (options) {
      this.modelScale = options.scale
      ;(this.isDrill = options.isDrill), (this.is3d = options.is3d)
    }
    this.cacheTime = null
    this.cachePosition = null
    this.lastSampledTime = null
    this.lastOrientation = null
    this.empty = null
    this.equalCount = 0
    this.init()
  }

  /**
   * 初始化实体对象
   */
  init() {
    var eyeSet = new Cesium.Cartesian3(0, 6, 0)
    this.entity = this.mapView.map.entities.add({
      id: this.id,
      model: {
        uri: this.modelUri,
        scale: this.modelScale,
        minimumPixelSize: 28,
        runAnimations: false,
        shadows: Cesium.ShadowMode.DISABLED
      },
      label: {
        text: '',
        font: '12px sans-serif',
        showBackground: true,
        backgroundPadding: new Cesium.Cartesian2(3, 3),
        eyeOffset: eyeSet,
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0.0, 8000.0)
      }
    })
    Object.assign(this.entity, { isFirstEnter: true })
  }

  addData(data) {
    if (!data.online) {
      return
    }
    data.heading = data.heading || 0
    const equal = floatsEqual(this.lon, data.longitude) && floatsEqual(this.lat, data.latitude) && floatsEqual(this.heading, data.heading)
    if (equal) this.equalCount++
    else this.equalCount = 0
    if (!equal || this.equalCount < 3) {
      if (data.deviceId.includes('.mml') || data.deviceId.includes('.truck') || data.deviceId.includes('.cmd')) {
        this.entity.label.text = (data.speed ? data.speed : 0) + 'km/h' + '\n' + data.name
      } else {
        this.entity.label.text = data.name
      }
      if (this.empty != null) {
        this.entity.model.uri = this.empty ? './Cesium/models/kuangka.glb' : './Cesium/models/kuangka_full_earth.glb'
      }
      this.empty = formatLoadState(data.produceState)
      if (this.entity.isFirstEnter) {
        // 首次
        const height = this.mapView.map.scene.sampleHeight(Cesium.Cartographic.fromDegrees(data.longitude, data.latitude))
        const time = this.mapView.map.clock.currentTime
        const position = Cesium.Cartesian3.fromDegrees(data.longitude, data.latitude, height)

        this.entity.position = position
        this.entity.orientation = Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(-data.heading, 0, 0))

        this.lon = data.longitude
        this.lat = data.latitude
        this.heading = data.heading
        this.cacheTime = time
        this.cachePosition = position

        this.entity.isFirstEnter = false
      } else {
        if (!floatsEqual(this.lon, data.longitude) || !floatsEqual(this.lat, data.latitude)) {
          // 位置变化
          const height = this.mapView.map.scene.sampleHeight(Cesium.Cartographic.fromDegrees(data.longitude, data.latitude))
          const time = this.mapView.map.clock.currentTime
          const position = Cesium.Cartesian3.fromDegrees(data.longitude, data.latitude, height)

          if (!(this.entity.position instanceof Cesium.SampledPositionProperty)) {
            this.lastSampledTime = null
            this.entity.position = new Cesium.SampledPositionProperty()
            this.entity.position.forwardExtrapolationType = Cesium.ExtrapolationType.HOLD
            // this.entity.orientation = new Cesium.VelocityOrientationProperty(this.entity.position)
            this.entity.orientation = new Cesium.CallbackProperty((time) => {
              const currentPos = this.entity.position.getValue(time)
              const pastPos = this.entity.position.getValue(Cesium.JulianDate.addSeconds(time, -0.1, new Cesium.JulianDate()))
              const velocity = currentPos && pastPos && Cesium.Cartesian3.subtract(currentPos, pastPos, new Cesium.Cartesian3())
              if (!velocity || Cesium.Cartesian3.magnitude(velocity) < 0.15 || Cesium.Cartesian3.magnitude(velocity) > 1.5) {
                return this.lastOrientation || Cesium.Transforms.headingPitchRollQuaternion(currentPos, new Cesium.HeadingPitchRoll(-data.heading, 0, 0))
              } else {
                let current = new Cesium.VelocityOrientationProperty(this.entity.position).getValue(time)
                this.lastOrientation = current || this.lastOrientation || Cesium.Transforms.headingPitchRollQuaternion(currentPos, new Cesium.HeadingPitchRoll(-data.heading, 0, 0))
                return this.lastOrientation
              }
            }, false)
          }

          let duration, startTime
          if (!this.lastSampledTime) {
            duration = 7.5
            startTime = time
          } else {
            const diff = Cesium.JulianDate.secondsDifference(time, this.lastSampledTime)
            if (diff >= 0) {
              // 当前时间比采样时间慢了
              duration = 7.5
              startTime = time
            } else if (diff <= -5) {
              // 当前时间比采样时间快了超过5秒
              duration = 2.5
              startTime = this.lastSampledTime
            } else {
              // 当前时间比采样时间快了不超过5秒
              duration = Cesium.JulianDate.secondsDifference(time, this.cacheTime)
              startTime = this.lastSampledTime
            }
          }
          const samples = 20
          for (let i = 0; i <= samples; ++i) {
            const factor = i / samples
            const sampleTime = Cesium.JulianDate.addSeconds(startTime, duration * factor, new Cesium.JulianDate())
            const samplePosition = Cesium.Cartesian3.lerp(this.cachePosition, position, factor, new Cesium.Cartesian3())
            this.entity.position.addSample(sampleTime, samplePosition)
            this.lastSampledTime = sampleTime
          }
          // this.entity.orientation = Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(-data.heading, 0, 0))

          this.lon = data.longitude
          this.lat = data.latitude
          this.heading = data.heading
          this.cacheTime = time
          this.cachePosition = position
        } else {
          // 朝向变化
          // const height = this.mapView.map.scene.sampleHeight(Cesium.Cartographic.fromDegrees(data.longitude, data.latitude))
          const time = this.mapView.map.clock.currentTime
          const position = /* Cesium.Cartesian3.fromDegrees(data.longitude, data.latitude, height) */ this.cachePosition

          this.entity.position = position
          this.entity.orientation = Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(-data.heading, 0, 0))

          this.lon = data.longitude
          this.lat = data.latitude
          this.heading = data.heading
          this.cacheTime = time
          this.cachePosition = position
        }
      }
    }
  }

  /**
   * 添加
   * @param data 设备数据
   */
  addDataOld(data) {
    const equal = floatsEqual(this.speed, data.speed) && floatsEqual(this.lon, data.longitude) && floatsEqual(this.lat, data.latitude) && floatsEqual(this.heading, data.heading)
    if (data.online && !equal) {
      this.speed = data.speed
      this.lon = data.longitude
      this.lat = data.latitude
      this.heading = data.heading
      let heading = null
      // let height = null
      if (data?.heading) {
        heading = data.heading
      } else {
        heading = 0
      }

      if (this.entity.billboard) {
        this.entity.billboard.rotation = heading + Math.PI / 2
      }

      if (data?.deviceId?.indexOf('.drill') == -1 && data?.deviceId?.indexOf('.shovel') == -1 && data?.deviceId?.indexOf('.worker') == -1) {
        this.entity.label.text = (data.speed ? data.speed : 0) + 'km/h' + '\n' + data.name
      } else {
        this.entity.label.text = data.name
      }
      this.entity.label.horizontalOrigin = Cesium.HorizontalOrigin.LEFT

      this.entity.show = true
      this.entity.label.showBackground = true

      if (!isNaN(data.longitude) && !isNaN(data.latitude)) {
        var d = this.mapView.map.scene.sampleHeight(
          new Cesium.Cartographic(
            Cesium.Math.toRadians(data.longitude), //转换成弧度
            Cesium.Math.toRadians(data.latitude) //弧度
          )
        )
        if (d) {
          this.groundHeight = d
        }
        if (this.entity.isFirstEnter == true) {
          this.entity.isFirstEnter = false
          const position = Cesium.Cartesian3.fromDegrees(data.longitude, data.latitude, this.groundHeight)
          this.entity.position = position
          this.entity.orientation = Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(heading, 0, 0))
          Object.assign(this.entity, { lastPos: [data.longitude, data.latitude, this.groundHeight] })
          Object.assign(this.entity, { firstHeight: this.groundHeight })
        } else {
          if (data.speed != null && data.speed != 0 && data.speed != '') {
            if (this.entity.position instanceof Cesium.SampledPositionProperty) {
            } else {
              this.entity.position = new Cesium.SampledPositionProperty()
            }
            this.updatePosition(data)
          } else {
            const position = Cesium.Cartesian3.fromDegrees(data.longitude, data.latitude, this.entity.firstHeight)
            this.entity.position = position
            this.entity.lastPos = [data.longitude, data.latitude, this.entity.firstHeight]
          }
        }
      }
    }
  }
  updatePosition(data) {
    let viewer = this.mapView.map
    let positionsOld = Cesium.Cartesian3.fromDegrees(this.entity.lastPos[0], this.entity.lastPos[1], this.entity.lastPos[2])
    let positionsNew = Cesium.Cartesian3.fromDegrees(data.longitude, data.latitude, this.groundHeight)
    if (this.entity.lastPos[0] != data.longitude || this.entity.lastPos[1] != data.latitude) {
      var start = Cesium.JulianDate.fromDate(new Date())
      let numberOfSamples = 20
      let totalSeconds = 7.0 //动画时间执行完毕

      for (let i = 0; i <= numberOfSamples; ++i) {
        const factor = i / numberOfSamples
        let time = Cesium.JulianDate.addSeconds(start, factor * totalSeconds, new Cesium.JulianDate())
        const locationFactor = Math.pow(factor, 2)
        const location = Cesium.Cartesian3.lerp(
          positionsOld, //传递的上一个坐标
          positionsNew, //传递的下一个坐标
          locationFactor,
          new Cesium.Cartesian3()
        )
        this.entity.position.addSample(time, location)
        this.sampledPositionProperty.addSample(time, location)
      }
      this.entity.orientation = new Cesium.VelocityOrientationProperty(this.entity.position)
      this.entity.lastPos = [data.longitude, data.latitude, this.groundHeight]
    }
  }

  /**
   * 销毁设备
   */
  destroy() {
    this.mapView.map.entities.remove(this.entity)
    this.mapView = this.id = this.entity = this.positionsProperty = this.headingProperty = null
  }
}