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)
相关推荐
百万蹄蹄向前冲1 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5812 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路2 小时前
GeoTools 读取影像元数据
前端
ssshooter3 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
Jerry3 小时前
Jetpack Compose 中的状态
前端
dae bal4 小时前
关于RSA和AES加密
前端·vue.js
柳杉4 小时前
使用three.js搭建3d隧道监测-2
前端·javascript·数据可视化
lynn8570_blog5 小时前
低端设备加载webp ANR
前端·算法
LKAI.5 小时前
传统方式部署(RuoYi-Cloud)微服务
java·linux·前端·后端·微服务·node.js·ruoyi
刺客-Andy5 小时前
React 第七十节 Router中matchRoutes的使用详解及注意事项
前端·javascript·react.js