Cesium保姆教程之镜头漫游效果

1,873 阅读5分钟

Cesium保姆教程之镜头漫游效果

实现镜头漫游效果需要大量坐标点,将时间和坐标点联系在一起,czml格式的可以完美的将时间相对应的坐标点联系起来。 czml是基于JSON,可以准确的描述值随时间变化的属性,并且czml可以通过Cesium.czmlDataSource的形式,不断接收到从后端推送的数据,并且在cesium中动态展示,czml是一种json格式的文件,可简单理解为一个数组(实际上不能在czml中做注释,本文是为了对此格式做出解释,才进行简单的解释,实际上czml中不能包含任何注释),具体格式如下:

const czml = [
//packet1:id一定为document,否则会报错,这里定义的是整个显示场景的信息
  {
    id: "document",
    //名称
    name: "CZML Path",
    version: "1.0",
    //cesium时间轴的范围
    clock: {
    //动画间隔时间,开始时间/结束时间(ISO8601格式)
      interval: "2012-08-04T10:00:00Z/2012-08-04T15:00:00Z",
      //开始时间
      currentTime: "2012-08-04T10:00:00Z",
      运动速率
      multiplier: 10,
    },
  },
  //packet two id的value值随意(不能与其它id相同),若相同只显示最后一个entity
  {
    id: "path",
    name: "path with GPS flight data",
    //entity的描述信息
    description:
      "<p>Hang gliding flight log data from Daniel H. Friedman.<br>Icon created by Larisa Skosyrska from the Noun Project</p>",
      //entity的有效可持续时间
    availability: "2012-08-04T10:00:00Z/2012-08-04T15:00:00Z",
    //添加的entity类型(path、billboard、model等实体),属性可根据entity的属性进行添加,
    //最后是添加位置信息
    
    path: {
    },
    billboard: {
    },
    position: {
      epoch: "2012-08-04T10:00:00Z",
      //前四行依次是时间、经度、纬度、高程,依次循环。
      cartographicDegrees: [
        0,
        -122.93797,
        39.50935,
        1776,
        10,
        -122.93822,
        39.50918,
        1773,
        20,
        -122.9385,
        39.50883,
        1772,
        30,
        -122.93855,
        39.50842,
        1770,
        ],
       },
      },
     ]

一个czml中至少有一个packet(第一个是用来描述场景的packet),第二个packet主要用来描述场景中的entity实体以及位置信息等,czml中可添加的内容非常多,以上只是截取的一部分内容,具体内容可参照CZML Guide · AnalyticalGraphicsInc/czml-writer Wiki · GitHub

注意:当有多个czml文件进行加载时,场景会以最后一个czml文件定义的为准。

这里只是对czml文件做一个简单的介绍,没有对其进行详细的介绍,以后有机会会对czml的格式做一个具体的解读。

czml文件的加载

czml文件是通过CzmlDataSource类进行加载。

const dataSource = await viewer.dataSources.add(
  Cesium.CzmlDataSource.load(czml)
);
//通过czml文件中的第一个packet中的id属性进行跟踪
viewer.trackedEntity = dataSource.entities.getById("path"); 

实现过程及原理

飞行漫游,即让camera飞行,可通过flyTo()、setView(),并通过插值的方法不断调用setView(),但这样会造成试图卡顿,并且计算比较麻烦,所有比较好的方式就是借助entity,通过跟踪运动中的entity实现camera的飞行。

设置路径

利用官网案例中的[czml](CZML Path - Cesium Sandcastle)czml文件进行修改,将路径设置成自己的路径,并把一些无用的东西删掉即可。

[
  {
    "id": "document",
    "name": "CZML Path",
    "version": "1.0",
    "clock": {
      "interval": "2023-09-04T10:00:00Z/2023-09-04T10:03:00Z",
      "currentTime": "2020-09-04T10:00:00Z",
      "multiplier": 10
    }
  },
  {
    "id": "path",
    "name": "path with GPS flight data",
    "path": {
      "width": 0,
      "leadTime": 10,
      "trailTime": 1000,
      "resolution": 5
    },
    "position": {
      "epoch": "2023-09-04T10:00:00Z",
      "cartographicDegrees": [
        0, 120.184679, 30.250211, 176,
        10, 120.184507, 30.252099, 173,
        20, 120.185151, 30.253816, 172,
        30, 120.185795, 30.254631, 170,
        40, 120.187125, 30.255704, 170,
        50, 120.189099, 30.255704, 167,
        60, 120.19176, 30.255103, 171,
        70, 120.194249, 30.254331, 165,
        80, 120.195236, 30.252442, 170,
        90, 120.195365, 30.250211, 154,
        100, 120.19455, 30.247808, 132,
        110, 120.192575, 30.24579, 127,
        120, 120.189614, 30.244761, 117,
        130, 120.187297, 30.244803, 113,
        140, 120.185795, 30.24549, 103,
        150, 120.183864, 30.247035, 103,
        160, 120.183907, 30.248795, 107,
        170, 120.183679, 30.249211, 176,
        180, 120.184679, 30.250211, 176
      ]
    }
  }
]

飞行漫游

const dataSource = await viewer.dataSources.add(
  Cesium.CzmlDataSource.load(czml)
);

entitites = dataSource.entities.getById("path");
//采用插值,保证在拐弯处丝滑(本处是采用官方的插值算法,并不是很精确,可自行研究)
entitis.position.setInterpolationOptions({
    interpolationDegree: 5,
    interpolationAlgorithm: Cesium.LagrangePolynomialApproximation
  })
 //镜头跟踪
viewer.trackedEntity = entities 

以上代码写完之后,我们看一看效果

镜头漫游无追踪.gif

从上图中的效果可得知,仅有白线的动态绘制效果。并没有出现镜头跟踪的效果,这是因为我们没有设置entities类,viewer.trackedEntity是跟踪entity所以才有镜头跟踪的效果,本文的czml中是没有entity的,所以是无法实现这种镜头漫游的效果,如果czml中有entity类的话,viewer.trackedEntity是可以实现镜头漫游效果,那该如何实现呢?我们实现跟踪坐标的变化,设置镜头参数,调整视角,进而实现镜头漫游的效果。

调整视角

接下来就是调整视角了,可通过viewer.scene.preUpdate.addEventListener(function () {})进行监听视角的变化,在此函数中通过调用viewer.camera.lookAt()方法改变视角。

image.png

这里是对camera每时每刻对准每一点

const target = new Cesium.Cartesian3.fromDegrees(120.189, 30.254, 300)

function setRoamView() {
  if (entitites) {
    const center = entitites.position.getValue(viewer.clock.currentTime)

    if (center) {
      const vector = new Cesium.Cartesian3(target.x - center.x, target.y - center.y, 300)
      viewer.camera.lookAt(center, vector)
    }
  }
}
viewer.scene.preUpdate.addEventListener(() => {setRoamView})

现在再让我们看看效果,现在出现镜头漫游的效果,可以在不添加实体类的基础上出现镜头漫游的效果。

漫游效果

镜头漫游追踪效果.gif

前端调用

<template>
    <div>
        <div id="cesiumContainer"></div>
    </div>
</template>

<script setup>
import * as Cesium from "cesium"
import "cesium/Source/Widgets/widgets.css"

import initCesium from "@/cesiumUtils/initCesium"
import czml from "@/cesiumUtils/czml";
import { onMounted} from "vue";
let viewer = null;
let entity = null
//生命周期钩子
onMounted(async () => {
    viewer = await initCesium("cesiumContainer");
    viewer.dataSources.add(Cesium.CzmlDataSource.load(czml)).then(ds => {
        entity = ds.entities.getById('path')
        entity.position.setInterpolationOptions({
            interpolationDegree: 5,
            interpolationAlgorithm: Cesium.LagrangePolynomialApproximation
        })
        viewer.trackedEntity = entity
        const target = new Cesium.Cartesian3.fromDegrees(-122.93797, 39.50935, 1776)
        viewer.scene.preUpdate.addEventListener(() => {
            setRoamView(entity, target, viewer)
        })
    })

})
const setRoamView = (entity, target, viewer) => {
    if (entity) {
        const center = entity.position.getValue(viewer.clock.currentTime)
        console.log("center", center);
        if (center) {
            const vector = new Cesium.Cartesian3(target.x - center.x, target.y - center.y, 100)
            viewer.camera.lookAt(center, vector)
        }
    }
}
</script>
<style lang="less" scoped>
#cesiumContainer {
    width: 100vw;
    height: 100vh;
    margin: 0;
    padding: 0;
    overflow: hidden;
    position: relative;

}
</style>


当然我们也可以停止漫游效果,需要进行两步:

1、取消camera追踪,viewer.trackedEntity = null;

2、取消事件绑定, viewer.scene.preUpdate.removeEventListener(()=> {setRoamView(entity, target, viewer)})

如果对于本文有不明白的地方可以在评论区指出,只要有时间肯定会做出详细解释。欢迎各位同行批评指正。