Cesium视角插值跟踪

需求: 已知运动实体的位置信息,不同时刻相机视角更新

规划相机不同时间的视角

js 复制代码
const geoView = {
    time: [0, 30, 158.915, 168.195, 342.995, 352.995, 600, 620, 2400, 2450, 999999999],
    heading: [86.74122015872331, 6.220334273461615, 6.220334273461615, 6.220334273461615, 6.220334273461615, 6.220334273461615, 6.220334273461615, 193.1884462729906, 193.1884462729906, 194.86485717230025, 194.86485717230025],
    pitch: [1.6260532613189567, -17.780826762364974, -17.780826762364974, -17.780826762364974, -17.780826762364974, -17.780826762364974, -17.780826762364974, -17.57337362151895, -17.57337362151895, -23.795953470748834, -23.795953470748834],
    up: [60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    range: [300, 200, 200, 150, 150, 100, 100, 100, 100, 100, 100]
}
变量 含义
time 时间
heading 偏航
pitch 俯仰
up 向上偏移量
range 距离目标点的距离

相机视角的插值处理

js 复制代码
for(let i = 0; i < geoView.time.length; i++){
    const time = Cesium.JulianDate.addSeconds(this.TakeoffZero, geoView.time[i], new Cesium.JulianDate())
    this.headingProperty.addSample(time, Cesium.Math.toRadians(geoView.heading[i]))
    this.pitchProperty.addSample(time, Cesium.Math.toRadians(geoView.pitch[i]))
    this.upProperty.addSample(time, geoView.up[i])
    this.rangeProperty.addSample(time, geoView.range[i])
    this.headingPropertyBack.addSample(time, Cesium.Math.toRadians(geoView.heading[i]))
    this.pitchPropertyBack.addSample(time, Cesium.Math.toRadians(geoView.pitch[i]))
    this.rangePropertyBack.addSample(time, geoView.range[i])
}

需求要求可以中途四周转向缩放,所以将偏航、俯仰、距离备份

相机按时间跟踪

有了前面两个条件,就可以开始跟踪了,注意!!! 这里监听使用postRender场景渲染后再设置相机视角,否则监控的运动对象会跟不上

js 复制代码
this.viewerListen = (scene, time) => {
    if(this.headingProperty.getValue(time) && this.pitchProperty.getValue(time) && this.rangeProperty.getValue(time)){
        const heading = this.headingProperty.getValue(time)
        const pitch = this.pitchProperty.getValue(time)
        const range = this.rangeProperty.getValue(time)
        const up = this.upProperty.getValue(time)
        const position = this.position.getValue(time)
        const ellipsoid = this.viewer.scene.globe.ellipsoid
        const cartographic = ellipsoid.cartesianToCartographic(position)
        const lon = Cesium.Math.toDegrees(cartographic.longitude)
        const lat = Cesium.Math.toDegrees(cartographic.latitude)
        const height = cartographic.height
        this.viewer.camera.lookAt(Cesium.Cartesian3.fromDegrees(lon,lat,height+up), new Cesium.HeadingPitchRange(heading, pitch, range))
    }
}
this.viewer.scene.postRender.addEventListener(this.viewerListen)

相机视角改变与复原

相机跟踪过程中更改相机,并在几秒后恢复。Cesium默认控制视角按住鼠标左键可以自由进行视角旋转,所以按下鼠标左键移除监听,抬起鼠标左键记录当前的heading和pitch,并添加指定时间后原本的视角,最后启动监听即可.

js 复制代码
this.handler.setInputAction((e) => {
    this.viewer.scene.postRender.removeEventListener(this.viewerListen)
    this.viewerListen = null
},Cesium.ScreenSpaceEventType.LEFT_DOWN)
js 复制代码
this.handler.setInputAction((e) => {
    const time = Cesium.JulianDate.addSeconds(this.viewer.clock.currentTime, 5, new Cesium.JulianDate())
    const timeInterval = new Cesium.TimeInterval({
        start: this.viewer.clock.currentTime,
        stop: time,
        isStartIncluded: false
    })
    const h0 = this.viewer.camera.heading
    const p0 = this.viewer.camera.pitch
    const h1 = this.headingPropertyBack.getValue(time)
    const p1 = this.pitchPropertyBack.getValue(time)
    this.headingProperty.removeSamples(timeInterval)
    this.pitchProperty.removeSamples(timeInterval)
    this.headingProperty.addSample(this.viewer.clock.currentTime, h0)
    this.pitchProperty.addSample(this.viewer.clock.currentTime, p0)
    this.headingProperty.addSample(time, h1)
    this.pitchProperty.addSample(time, p1)
    this.viewerListen = (scene, time) => {
        if(this.headingProperty.getValue(time) && this.pitchProperty.getValue(time) && this.rangeProperty.getValue(time)){
            const heading = this.headingProperty.getValue(time)
            const pitch = this.pitchProperty.getValue(time)
            const range = this.rangeProperty.getValue(time)
            const up = this.upProperty.getValue(time)
            const position = this.position.getValue(time)
            const ellipsoid = this.viewer.scene.globe.ellipsoid
            const cartographic = ellipsoid.cartesianToCartographic(position)
            const lon = Cesium.Math.toDegrees(cartographic.longitude)
            const lat = Cesium.Math.toDegrees(cartographic.latitude)
            const height = cartographic.height
            this.viewer.camera.lookAt(Cesium.Cartesian3.fromDegrees(lon,lat,height+up), new Cesium.HeadingPitchRange(heading, pitch, range))
        }
    }
    this.viewer.scene.postRender.addEventListener(this.viewerListen)
    this.postRenderListensArr.push(this.viewerListen)
},Cesium.ScreenSpaceEventType.LEFT_UP)

这里移除指定时间段的插值防止再未恢复时再次调整

鼠标滚动处理

鼠标滚动只需调整相机距离

js 复制代码
this.handler.setInputAction(e => {
    const time0 = Cesium.JulianDate.addSeconds(this.viewer.clock.currentTime, 5, new Cesium.JulianDate())
    const time1 = Cesium.JulianDate.addSeconds(this.viewer.clock.currentTime, 10, new Cesium.JulianDate())
    const timeInterval = new Cesium.TimeInterval({
        start: this.viewer.clock.currentTime,
        stop: time1,
        isStartIncluded: false
    })
    this.rangeProperty.removeSamples(timeInterval)
    const time = this.viewer.clock.currentTime
    const range = this.rangeProperty.getValue(time)
    this.rangeProperty.addSample(time, (range - e)>=10?(range - e):10)
    this.rangeProperty.addSample(time0, (range - e)>=10?(range - e):10)
    this.rangeProperty.addSample(time1, this.rangePropertyBack.getValue(time1))
},Cesium.ScreenSpaceEventType.WHEEL)

最小为10防止穿模型,根据实际情况调整

最后完善

当我们鼠标按下时,这时相机也是在运动过程中,因此我们还需要继续跟踪 鼠标按下和抬起完整代码如下

js 复制代码
this.handler.setInputAction((e) => {
    this.viewer.scene.postRender.removeEventListener(this.viewerListen)
    this.viewerListen = null
    this.cameraPositionListen = (scene, time) => {
        const h0 = this.viewer.camera.heading
        const p0 = this.viewer.camera.pitch
        const position = entity.position.getValue(time)
        const ellipsoid = this.viewer.scene.globe.ellipsoid
        const cartographic = ellipsoid.cartesianToCartographic(position)
        const lon = Cesium.Math.toDegrees(cartographic.longitude)
        const lat = Cesium.Math.toDegrees(cartographic.latitude)
        const height = cartographic.height
        this.viewer.camera.lookAt(Cesium.Cartesian3.fromDegrees(lon,lat,height+up), new Cesium.HeadingPitchRange(h0, p0, range))
    }
    this.viewer.scene.postRender.addEventListener(this.cameraPositionListen)
},Cesium.ScreenSpaceEventType.LEFT_DOWN)
js 复制代码
this.handler.setInputAction((e) => {
    this.viewer.scene.postRender.removeEventListener(this.cameraPositionListen)
    this.cameraPositionListen = null
    const time = Cesium.JulianDate.addSeconds(this.viewer.clock.currentTime, 5, new Cesium.JulianDate())
    const timeInterval = new Cesium.TimeInterval({
        start: this.viewer.clock.currentTime,
        stop: time,
        isStartIncluded: false
    })
    const h0 = this.viewer.camera.heading
    const p0 = this.viewer.camera.pitch
    const h1 = this.headingPropertyBack.getValue(time)
    const p1 = this.pitchPropertyBack.getValue(time)
    this.headingProperty.removeSamples(timeInterval)
    this.pitchProperty.removeSamples(timeInterval)
    this.headingProperty.addSample(this.viewer.clock.currentTime, h0)
    this.pitchProperty.addSample(this.viewer.clock.currentTime, p0)
    this.headingProperty.addSample(time, h1)
    this.pitchProperty.addSample(time, p1)
    this.viewerListen = (scene, time) => {
        if(this.headingProperty.getValue(time) && this.pitchProperty.getValue(time) && this.rangeProperty.getValue(time)){
            const heading = this.headingProperty.getValue(time)
            const pitch = this.pitchProperty.getValue(time)
            const range = this.rangeProperty.getValue(time)
            const up = this.upProperty.getValue(time)
            const position = this.position.getValue(time)
            const ellipsoid = this.viewer.scene.globe.ellipsoid
            const cartographic = ellipsoid.cartesianToCartographic(position)
            const lon = Cesium.Math.toDegrees(cartographic.longitude)
            const lat = Cesium.Math.toDegrees(cartographic.latitude)
            const height = cartographic.height
            this.viewer.camera.lookAt(Cesium.Cartesian3.fromDegrees(lon,lat,height+up), new Cesium.HeadingPitchRange(heading, pitch, range))
        }
    }
    this.viewer.scene.postRender.addEventListener(this.viewerListen)
    this.postRenderListensArr.push(this.viewerListen)
},Cesium.ScreenSpaceEventType.LEFT_UP)
相关推荐
@PHARAOH3 小时前
WHAT - Remix 入门和基本实践:理解两套产物
前端·remix
mayaairi3 小时前
Vue2 生命周期完全指南:从创建到销毁的8个钩子函数
前端·javascript·vue.js
xy34534 小时前
axure9.0 如何打造一个计时器(简单版)
前端·ui·html·axure·原型·产品设计
IMPYLH4 小时前
HTML 的 <pre> 元素
前端·javascript·html
tsumikistep5 小时前
【前端】Vue + Axios 跨域问题实战:7070 代理转发 8080 + 401 报错分析(黑马外卖)
前端·javascript·vue.js
lichenyang4536 小时前
鸿蒙首页从等高商品 Grid 到双列瀑布流:同一张图也能做出小红书式浏览节奏
前端
里欧跑得慢6 小时前
Flutter主题与样式详解
前端·css·flutter·web
计算机魔术师6 小时前
TPU推理性价比翻50%,NVIDIA的护城河要见底了?
前端
IT_陈寒6 小时前
Python的多线程就是个假把式,我算是体验到了
前端·人工智能·后端
OpenTiny社区7 小时前
【直播分享】GenUI SDK 技术公开课第二讲 | GenuiChat 核心配置深度解析
前端·github