cesium 根据SampledPositionProperty与timeline实现轨迹回放功能(跟随视角、上帝视角) 详细代码与理解记录

36 篇文章 0 订阅
16 篇文章 5 订阅

cesium 根据SampledPositionProperty与timeline实现轨迹回放功能(跟随视角、上帝视角) 详细代码与理解,文末附完整代码

最终类似效果展示

大致原理摘要 引用出处

这个主要是用entity的orientation方向属性实现的,问题就在于怎么获取到实时的四元素,我们知道VelocityOrientationProperty这个是根据当前位置实时计算方向的,但是这个方向在转角时是瞬间转的,达不到均匀的转动效果,所以我们需要两个插值模型SampledPositionProperty, 一个位置和一个方向的,但是现在就出现另外一个问题,那就是orientation应该用哪个。其实我们需要将两个插值模型结合起来,orientation使用回调,获取当前时间下的位置和方向,查看SampledPositionProperty的源码,实现自己需要的方向模型,也就是把SampledPositionProperty的方向替换成我们自己的插值方向,然后回调函数里面返回一个四元素。那么我们就可以计算出orientation了,然后得到需要移动的相机的direction和up,结合position, 相机实时setView就可以了,这样实现的就是视角在中心处沿着路径移动,还可以加上lookDown偏移角度和moveBackward距离,就是我示例上演示的效果了。

一、生成cesium Access Token

cesium ion(获取地址)
1、登录
在这里插入图片描述

2、Default Token
在这里插入图片描述

二、创建cesium实体

代码与注解

Cesium.Ion.defaultAccessToken = 'token'
var viewer = new Cesium.Viewer('cesiumContainer', {
  baseLayerPicker: false,
  timeline: true,// 必须为true显示时间线组件(如不想显示可以使用样式层叠表修改display:none) 否则viewer.timeline.zoomTo会报undefined错误
  homeButton: false,
  fullscreenButton: false,
  infoBox: false,
  sceneModePicker: false,
  navigationInstructionsInitiallyVisible: false,
  navigationHelpButton: false,
  animation: false,
  shouldAnimate: true // 必须为true开启动画 否则不会达到飞机模型飞行动画效果
});
// viewer.animation.container.style.display = 'none';//隐藏动画控件
// viewer.timeline.container.style.display = 'none';//隐藏时间线控件
// viewer.geocoder.container.style.display = 'none';//隐藏地名查找控件
// viewer.cesiumWidget.creditContainer.style.display = 'none';//隐藏ceisum标识
viewer.scene.globe.enableLighting = true;
三、根据模拟数据配置timeline

代码与注解

let data = [{ longitude: 116.405419, latitude: 39.918034, height: 0, time: 0 },
{ longitude: 120.2821, latitude: 33.918145, height: 0, time: 30 },
{ longitude: 115.497402, latitude: 39.344641, height: 70000, time: 60 },
{ longitude: 107.942392, latitude: 29.559967, height: 70000, time: 110 },
{ longitude: 106.549265, latitude: 29.559967, height: 0, time: 120 }];
// 起始时间
let start = Cesium.JulianDate.fromDate(new Date());
// 结束时间
let stop = Cesium.JulianDate.addSeconds(start, 120, new Cesium.JulianDate());
// 设置始时钟始时间
viewer.clock.startTime = start.clone();
// 设置时钟当前时间
viewer.clock.currentTime = start.clone();
// 设置始终停止时间
viewer.clock.stopTime = stop.clone();
// 时间速率,数字越大时间过的越快
viewer.clock.multiplier = 1;
// 时间轴
viewer.timeline.zoomTo(start, stop);
// 循环执行,即为2,到达终止时间,重新从起点时间开始
// viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
// 摄像机聚焦到开始位置
viewer.camera.flyTo({
  destination: Cesium.Cartesian3.fromDegrees(116.405419, 39.918034, 100000)
})
四、根据模拟数据创建对应时间与坐标的SampledPositionProperty并生成entity实体

代码与注解

let property = createProperty(data);
// 添加entity实体
let planeModel = viewer.entities.add({
  // 和时间轴关联
  availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({
    start: start,
    stop: stop
  })]),
  id: 'move',
  position: property,
  // 根据所提供的位置计算模型的朝向
  orientation: new Cesium.VelocityOrientationProperty(property),
  // 模型
  model: {
    uri: './Apps/SampleData/models/CesiumAir/Cesium_Air.glb',
    minimumPixelSize: 128
  },
  path: {
    resolution: 1,
    material: new Cesium.PolylineGlowMaterialProperty({
      glowPower: 0.1,
      color: Cesium.Color.YELLOW
    }),
    // leadTime、trailTime 不设置 path全显示
    // leadTime:0,// 设置为0时 模型通过后显示path
    trailTime: 0,// 设置为0时 模型通过后隐藏path
    width: 10
  }
});

createProperty

/**
 * 计算飞行路径
 * @param source 数据坐标
 * @returns {SampledPositionProperty|*}
 */
function createProperty(source) {
  // 取样位置 相当于一个集合
  let property = new Cesium.SampledPositionProperty();
  for (let i = 0; i < source.length; i++) {
    let time = Cesium.JulianDate.addSeconds(start, source[i].time, new Cesium.JulianDate);
    let position = Cesium.Cartesian3.fromDegrees(source[i].longitude, source[i].latitude, source[i].height);
    // 添加位置,和时间对应
    property.addSample(time, position);
  }
  return property;
}
五、实现视角处理的timeline的监听方法(设置方向、俯仰和横摇属性存在偏差未解决)

原理:

1、根据clock.onTick事件获取timeline变化时的JulianDate
2、通过entity的position属性getValue方法传入此刻的JulianDate获取世界坐标(Cartesian3)
3、根据viewer.scene.camera.setView方法设置跟随视角
4、根据clock.onStop事件监听轨迹完成,可以处理轨迹完成后的逻辑代码

代码与注解

viewer.clock.onTick.addEventListener((tick) => {
    // let q = viewer.entities.getById('move').orientation.getValue(tick.currentTime)
    // 根据四元素获取方位角heading ,pitch, roll 设置仰角等
    // 此方法设置方向、俯仰和横摇属性存在偏差未解决
    // if (q == undefined) return
    // let hpr = Cesium.HeadingPitchRoll.fromQuaternion(q)
    // let heading = Cesium.Math.toDegrees(hpr.heading);
    // let pitch = Cesium.Math.toDegrees(hpr.pitch);
    // let roll = Cesium.Math.toDegrees(hpr.roll);
    
    //世界坐标转换为经纬度
    let position = viewer.entities.getById('move').position.getValue(tick.currentTime)
    let ellipsoid = viewer.scene.globe.ellipsoid
    let cartographic = ellipsoid.cartesianToCartographic(position);
    let lat = Cesium.Math.toDegrees(cartographic.latitude);
    let lng = Cesium.Math.toDegrees(cartographic.longitude);
    let alt = cartographic.height;
    viewer.scene.camera.setView({
      destination: Cesium.Cartesian3.fromDegrees(lng, lat, 100000),
      // orientation: {
      //   heading, 
      //   pitch,    
      //   roll                            
      // }
    })
  })
viewer.clock.onStop.addEventListener((tick) => {
  console.log(tick, 'stop')
  // 动画执行到结束时间时调用
  // 逻辑代码 removeEventListener => onTick
})

创作不易,如对你有所帮助,点赞评论加收藏,三扣…在这里插入图片描述

五、完整代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesiumDemoflyMove</title>
  <script src="./Cesium/Cesium.js"></script>
  <style>
    @import url(Cesium/Widgets/widgets.css);
    html,
    body,
    #cesiumContainer {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
      overflow: hidden;
    }
  </style>
</head>

<body>
  <div id="cesiumContainer"></div>
</body>
<script>
  // 1.cesium 根据SampledPositionProperty与timeline实现轨迹回放功能 详细代码与理解
  // 2.cesium 世界坐标与经纬度转换,四元素与方位角转换
  Cesium.Ion.defaultAccessToken = 'token'
  var viewer = new Cesium.Viewer('cesiumContainer', {
    baseLayerPicker: false,
    timeline: true,// 必须为true显示时间线组件(如不想显示可以使用样式层叠表修改display:none) 否则viewer.timeline.zoomTo会报undefined错误
    homeButton: false,
    fullscreenButton: false,
    infoBox: false,
    sceneModePicker: false,
    navigationInstructionsInitiallyVisible: false,
    navigationHelpButton: false,
    animation: false,
    shouldAnimate: true // 必须为true开启动画 否则不会达到飞机模型飞行动画效果
  });
  // viewer.animation.container.style.display = 'none';//隐藏动画控件
  // viewer.timeline.container.style.display = 'none';//隐藏时间线控件
  // viewer.geocoder.container.style.display = 'none';//隐藏地名查找控件
  // viewer.cesiumWidget.creditContainer.style.display = 'none';//隐藏ceisum标识
  viewer.scene.globe.enableLighting = true;
  let data = [{ longitude: 116.405419, latitude: 39.918034, height: 0, time: 0 },
  { longitude: 120.2821, latitude: 33.918145, height: 0, time: 30 },
  { longitude: 115.497402, latitude: 39.344641, height: 70000, time: 60 },
  { longitude: 107.942392, latitude: 29.559967, height: 70000, time: 110 },
  { longitude: 106.549265, latitude: 29.559967, height: 0, time: 120 }];
  // 起始时间
  let start = Cesium.JulianDate.fromDate(new Date());
  // 结束时间
  let stop = Cesium.JulianDate.addSeconds(start, 120, new Cesium.JulianDate());
  // 设置始时钟始时间
  viewer.clock.startTime = start.clone();
  // 设置时钟当前时间
  viewer.clock.currentTime = start.clone();
  // 设置始终停止时间
  viewer.clock.stopTime = stop.clone();
  // 时间速率,数字越大时间过的越快
  viewer.clock.multiplier = 1;
  // 时间轴
  viewer.timeline.zoomTo(start, stop);
  // 循环执行,即为2,到达终止时间,重新从起点时间开始
  viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
  // 摄像机聚焦到开始位置
  viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(116.405419, 39.918034, 100000)
  })
  let property = createProperty(data);;
  // 添加entity实体
  let planeModel = viewer.entities.add({
    // 和时间轴关联
    availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({
      start: start,
      stop: stop
    })]),
    id: 'move',
    position: property,
    // 根据所提供的位置计算模型的朝向
    orientation: new Cesium.VelocityOrientationProperty(property),
    // 模型
    model: {
      uri: './Apps/SampleData/models/CesiumAir/Cesium_Air.glb',
      minimumPixelSize: 128
    },
    path: {
      resolution: 1,
      material: new Cesium.PolylineGlowMaterialProperty({
        glowPower: 0.1,
        color: Cesium.Color.YELLOW
      }),
      // leadTime、trailTime 不设置 path全显示
      // leadTime:0,// 设置为0时 模型通过后显示path
      trailTime: 0,// 设置为0时 模型通过后隐藏path
      width: 10
    }
  });

  viewer.clock.onTick.addEventListener((tick) => {
  	// let q = viewer.entities.getById('move').orientation.getValue(tick.currentTime)
    // 根据四元素获取方位角heading ,pitch, roll 设置仰角等
    // if (q == undefined) return
    // let hpr = Cesium.HeadingPitchRoll.fromQuaternion(q)
    // let heading = Cesium.Math.toDegrees(hpr.heading);
    // let pitch = Cesium.Math.toDegrees(hpr.pitch);
    // let roll = Cesium.Math.toDegrees(hpr.roll);
    let position = viewer.entities.getById('move').position.getValue(tick.currentTime)
    //世界坐标转换为经纬度
    let ellipsoid = viewer.scene.globe.ellipsoid
    let cartographic = ellipsoid.cartesianToCartographic(position);
    let lat = Cesium.Math.toDegrees(cartographic.latitude);
    let lng = Cesium.Math.toDegrees(cartographic.longitude);
    let alt = cartographic.height;
    viewer.scene.camera.setView({
      destination: Cesium.Cartesian3.fromDegrees(lng, lat, 100000),
      // orientation: {
      //   heading, 
      //   pitch,    
      //   roll                            
      // }
    })
  })
  viewer.clock.onStop.addEventListener((tick) => {
    console.log(tick, 'stop')
    // 动画执行到结束时间时调用
    // 逻辑代码 removeEventListener => onTick
  })
  /**
   * 计算飞行路径
   * @param source 数据坐标
   * @returns {SampledPositionProperty|*}
   */
  function createProperty(source) {
    // 取样位置 相当于一个集合
    let property = new Cesium.SampledPositionProperty();
    for (let i = 0; i < source.length; i++) {
      let time = Cesium.JulianDate.addSeconds(start, source[i].time, new Cesium.JulianDate);
      let position = Cesium.Cartesian3.fromDegrees(source[i].longitude, source[i].latitude, source[i].height);
      // 添加位置,和时间对应
      property.addSample(time, position);
    }
    return property;
  }
</script>

</html>
  • 25
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 41
    评论
Cesium是一个基于WebGL的虚拟地球和地图引擎,可以用于展示三维地理数据。如果你想要在Cesium实现轨迹回放,可以参考以下步骤: 1. 创建一个Cesium Viewer对象: ```javascript var viewer = new Cesium.Viewer('cesiumContainer'); ``` 2. 添加一个ImageryProvider用于显示地球表面的图像: ```javascript viewer.imageryLayers.addImageryProvider(new Cesium.UrlTemplateImageryProvider({ url : 'http://a.tile.openstreetmap.org/{z}/{x}/{y}.png' })); ``` 3. 创建一个Entity用于表示轨迹: ```javascript var entity = viewer.entities.add({ polyline : { positions : Cesium.Cartesian3.fromDegreesArrayHeights([ lon1, lat1, height1, lon2, lat2, height2, ... lonN, latN, heightN ]), width : 3, material : Cesium.Color.RED } }); ``` 其中,lon1、lat1、height1、lon2、lat2、height2等表示轨迹经过的经纬度和高度。这里使用Cartesian3.fromDegreesArrayHeights方法将经纬度和高度转换为Cesium的笛卡尔坐标系。 4. 创建一个时间线并添加一个ClockViewModel: ```javascript var clockViewModel = new Cesium.ClockViewModel(viewer.clock); var clock = viewer.clock; clock.shouldAnimate = true; ``` 5. 启动轨迹回放: ```javascript clock.startTime = Cesium.JulianDate.fromIso8601("2013-10-01T16:00:00Z"); clock.stopTime = Cesium.JulianDate.fromIso8601("2013-10-01T16:30:00Z"); clock.currentTime = clock.startTime; clock.clockRange = Cesium.ClockRange.LOOP_STOP; // 回放完毕后停止 clock.clockStep = Cesium.ClockStep.SYSTEM_CLOCK_MULTIPLIER; ``` 这里设置了轨迹回放的起始时间、结束时间,以及回放方式等参数。 6. 在每一帧中更新时间和实体位置: ```javascript viewer.clock.onTick.addEventListener(function(clock) { var time = clock.currentTime; entity.position = entity.computePosition(time); entity.orientation = entity.computeOrientation(time); }); ``` 7. 最后,启动Cesium Viewer: ```javascript viewer.scene.globe.enableLighting = true; viewer.camera.flyHome(0); ``` 以上就是Cesium轨迹回放的基本步骤,你可以根据自己的需求对代码进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 41
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值