0


迈向三维:vue3+Cesium.js三维WebGIS项目实战--持续更新中

写在前面:随着市场对数字孪生的需求日益增多,对于前端从业者的能力从对框架vue、react的要求,逐步扩展到2D、3D空间的交互,为用户提供更紧密的立体交互。近年来前端对GIS的需求日益增多。本文将记录WebGIS的学习之旅,从实战项目入门,挖掘Cesium.js API,并逐步丰富项目。

一、WebGIS简介

WebGIS(Web地理信息系统)是指利用 Web 技术来构建和展示地理信息系统(GIS),使用户可以通过 Web 浏览器访问、查询、分析和可视化地理空间数据。WebGIS 通常结合地图服务、地理信息数据库、前端地图库和相关的数据处理技术,为用户提供交互式的地图浏览和空间数据分析功能。

CesiumJS简介

Cesium是使用JavaScript开发的基于WebGL的,实现三维地球和地图可视化的JS库。支持海量的三维模型数据、影像数据,地形数据,矢量数据等丰富的地理数据加载。在智慧城市、交通、规划、城市管理、地形仿真领域有着非常广泛的应用。Cesium为三维的GIS提供了一个高效的数据可视化平台。

Viewer - Cesium Documentation

CesiumJS是目前最流行的三维数字地球渲染引擎,不仅可以在网页端高效运行,而且可以借助虚幻引擎在CS端渲染出和游戏一样的高质量效果。

Cesium支持3D、2D、2.5D形式的地图展示,可以自行绘制图形,高亮区域。

CesiumJS源代码

访问官网

Downloads – Cesium

二、项目快速搭建

快速搭建一个下面的Cesium.js官方示例

使用vite快速搭建vue3+typeScript

使用下面命令创建vue项目

  1. npm create vite@latest
  • 设置项目名称
  • 选择使用vue
  • 选择是否使用typescript+vue

安装Cesium插件

  1. npm i cesium vite-plugin-cesium vite -D

配置vite.config.js

vite.config.js中配置如下:

  1. import { defineConfig } from 'vite'
  2. import vue from '@vitejs/plugin-vue'
  3. import cesium from 'vite-plugin-cesium';
  4. export default defineConfig({
  5. plugins: [vue(),cesium()]
  6. });

清空style.css

在App.vue里面进行全局导入

需要id为cesiumContainer的div挂载后再执行Cesium的代码,给html、body加样式屏幕撑满。

注意:viewer实例的创建必须在onMounted之后进行,确保元素已经挂载到页面上,保证地图的顺利渲染

  1. <template>
  2. <div id="cesiumContainer"></div>
  3. </template>
  4. <script setup>
  5. import * as Cesium from 'cesium';
  6. import { onMounted } from 'vue';
  7. onMounted(() => {
  8. const viewer = new Cesium.Viewer('cesiumContainer',{
  9. infoBox: false, // 禁用沙箱,解决控制台报错
  10. });
  11. });
  12. </script>
  13. <style>
  14. #app {
  15. font-family: Avenir, Helvetica, Arial, sans-serif;
  16. -webkit-font-smoothing: antialiased;
  17. -moz-osx-font-smoothing: grayscale;
  18. text-align: center;
  19. color: #2c3e50;
  20. }
  21. html,body,#cesiumContainer{
  22. width: 100%;
  23. height: 100%;
  24. padding: 0;
  25. margin: 0;
  26. overflow: hidden;
  27. }
  28. </style>

这段代码中的cesiumContainer是用作Cesium Viewer(查看器)的挂载点。在WebGL渲染中,无论是二维还是三维图像,都需要一个容器来承载渲染的内容。在这里,

元素就是用来承载Cesium Viewer所渲染的内容。

当你创建一个Cesium Viewer实例时,你需要指定一个DOM元素作为其容器,Cesium会将渲染的内容放置在这个DOM元素中。

通过new Cesium.Viewer('cesiumContainer', {...}),将Cesium Viewer挂载到id为cesiumContainer的div元素上,从而实现了Cesium渲染的效果。

其实无论是二维还是三维图像渲染,都需要一个挂载点来显示渲染的内容,而在这里cesiumContainer就充当了这样的作用。原理都是相同的哈~

运行程序

  1. npm run dev

即可得到上面示例的gif效果

页面涉及的空间如下,所有的空间都可以关闭

  1. geocoder: 位置查找工具
  2. baseLayerPicker: 图层选择器(地形影像服务)
  3. homeButton: 视角返回初始位置
  4. fullscreenButton: 全屏
  5. animation: 左下角仪表盘(动画器件)
  6. timeline: 底部时间线
  7. sceneModePicker: 选择视角的模式(球体、平铺、斜视平铺)
  8. navigationHelpButton: 导航帮助按钮
  9. infoBox: 信息框控件
  10. navigationInstructionsInitiallyVisible: 导航说明初始可见
  11. shouldAnimate: 动画
  12. requestWaterMask: 请求水面罩
  13. requestVertexNormals: 请求顶点法线

三、认识Cesium四大类

查看器类Viewer

Viewer是三维数据展示的主要窗口,此外还包含了一些基础控件。在定义Viewer对象的同时需要设定基础部件、图层等的初始化状态。

用法示例:

接收两个参数,第一个参数指定地图主窗口div的id;第二个参数options是Viewer的可选设置参数。包含图层、地形、时间系统等参数;种类多样。

  1. const viewer = new Cesium.Viewer('cesiumContainer',{
  2. infoBox: false,
  3. });

第二个参数接收的对象表示页面可关闭的控件,控件参数解释见上,代码示例

Viewer类,关闭左下角仪表盘及底部时间线

  1. <script setup lang="ts">
  2. import * as Cesium from 'cesium';
  3. import { onMounted,ref} from 'vue';
  4. const viewer = ref();
  5. onMounted(() => {
  6. viewer.value = new Cesium.Viewer('cesiumContainer', {
  7. infoBox: false, // 禁用沙箱,解决控制台报错
  8. animation: false,//关闭左下角仪表盘(动画器件)
  9. timeline:false,//底部时间线
  10. });
  11. });

效果展示:红色圈出的部分是隐藏的控件

场景类Scene

在Cesium中Scene是非常重要的类,是所有3D图像对象的容器,是在Viewer内部隐式创建的。用于基础地理环境设置

代码示例:隐藏地球

  1. <script setup lang="ts">
  2. import * as Cesium from 'cesium';
  3. import { onMounted,ref} from 'vue';
  4. const viewer = ref();
  5. onMounted(() => {
  6. viewer.value = new Cesium.Viewer('cesiumContainer', {
  7. infoBox: false, // 禁用沙箱,解决控制台报错
  8. animation: false,//关闭左下角仪表盘(动画器件)
  9. timeline:false,//底部时间线
  10. });
  11. viewer.value.scene.globe.show = false;
  12. });

scene可以对场景进行交互:如鼠标事件、相机事件;可以通过scene控制相机对视口进行切换;

代码示例:

  1. viewer.value.scene.camera.setView({
  2. destination:Cesium.Cartesian3.fromDegrees(116.39,39.9,1500)
  3. })

此外scene还可以修改地球的图层 样式或地形数据,更可以在图层上绘制几何体,点、线、面

实体类Entity

Entity是由Primitive封装而来,Entity并不属于Scene。

相比较而言,Entity封装程度高,构造简单,使用便捷,使得开发者专注于数据的呈现,而不必担心底层的可视化机制。它还提供了用于构建复杂的、时间动态可视化的结构。

Entity在使用中主要用于加载实体模型,几何图形;对其进行样式设置,动效修改等

代码示例:

增加一个尺寸为100像素的绿色圆点,先设置当前圆点的位置。fromDegrees内的三个参数分别是经度、维度和高度;给圆点设置大小,pixelSize,在设置颜色;将摄像头设置在圆点处,使得我们可以观测到。

  1. <script setup lang="ts">
  2. import * as Cesium from 'cesium';
  3. import { onMounted,ref} from 'vue';
  4. const viewer = ref();
  5. onMounted(() => {
  6. viewer.value = new Cesium.Viewer('cesiumContainer', {
  7. infoBox: false, // 禁用沙箱,解决控制台报错
  8. animation: false,//关闭左下角仪表盘(动画器件)
  9. timeline:false,//底部时间线
  10. });
  11. const entity = viewer.value.entities.add({
  12. position: Cesium.Cartesian3.fromDegrees(116.39, 39.91, 400),
  13. point: {
  14. pixelSize: 100,
  15. color: new Cesium.Color(0,1,0,1)
  16. }
  17. })
  18. viewer.value.trackedEntity = entity;
  19. });
  20. </script>

效果展示

数据源集合类DataSourceCollection

在GIS开发中加载矢量数据是必不可少的功能。在Cesium加载以下几种格式的数据可实现矢量数据的加载和存取。

DataSourceCollection是Cesium中加载矢量数据的主要方式之一;最大特点是支持加载矢量数据集合外部文件的调用。主要有三种调用方法:

CzmlDataSource 加载czml格式

KmlDataSource 加载kml格式

GeoJsonDataSource 加载GeoJSON格式

缺少数据源,暂时用代码表示

  1. viewer.value.dataSource.add(
  2. Cesium.GeoJsonDataSource.load("../../**.topojson")
  3. )

四、Cesium的坐标与转换

Cesium是具有真实地理坐标的三维球体,而用户是通过二维屏幕与Cesium进行操作。我们将三维模型绘制在三维球体上就需要地理坐标和屏幕坐标之间做转换。接下来介绍Cesium的五种坐标系,及坐标系直接的相互转换

1.WGS84经纬度坐标系-没有实际的对象

简单点就是下面介绍的,WGS84是一种坐标系统,用于GPS全球定位系统使用。快把你初中高中的地理知识拿出来回忆回忆

地球质心为坐标原点,Z轴指向BIH(国际时间服务机构1984.0定义的协议地球极方向)。X轴指向BIH1984.0的零子午面和CTP赤道的交点。Y轴与Z轴,X轴垂直构成右手坐标系。

科普一下经纬度:

在WGS84经纬度坐标系中经度从-180度到正180度,纬度范围从-90度到正90度。
但在Cesium中没有实际的对象来描述WGS84经纬度坐标系。而是以下面介绍的弧度坐标系表示

2.WGS84弧度坐标系(Cartographic)

用经纬度和高度表示在地球上的坐标,这才是人类可以理解的坐标点!!!忘记上面那个经纬度坐标系吧。

构建WGS84有两种方法

构造函数法

直接传入经纬度和高度

  1. new Cesium.Cartographic(longitude,latitude,height)

静态函数法

  1. const cartographic = Cesium.Cartographic.fromDegrees(经度,纬度,高度)
  1. const cartographic = Cesium.Cartographic.fromRedians(经度弧度,纬度弧度,高度)

3.笛卡尔空间直角坐标系(Cartesian3)*重要

在 Cesium 中,实际使用的坐标系是笛卡尔空间坐标系。

笛卡尔空间坐标系是一个三维坐标系,用来描述三维空间中的点位置。在 Cesium 中,

  1. Cartesian3

类表示三维笛卡尔坐标系中的一个点,它由三个分量

  1. (x, y, z)

组成,分别表示点在 x、y、z 轴上的位置。

通过使用

  1. Cartesian3

类,您可以在 Cesium 中表示和操作三维空间中的点,例如表示摄像机位置、实体位置、矢量等。

  1. Cartesian3

类提供了许多方法和属性,可以帮助您进行点的计算、转换和操作

在Cesium中的用法如下

  1. new Cesium.Cartesian3(x,y,z)

4.平面坐标系(Cartesian2)

平面坐标系,也叫屏幕坐标系,是一个二维的笛卡尔坐标系

屏幕左上角为原点,屏幕水平方向为X轴,垂直方向为Y轴,向下为正

代码示例

  1. new Cesium.Cartesian2 ( x , y )

5.4D笛卡尔坐标系(Cartesian4)-用的较少

不介绍了

坐标系相互转换

WGS84转笛卡尔空间直角坐标系

  1. const cartesian3 = Cesium.Cartesian3.fromDegrees(经度,纬度,高度)
  1. const cartesian3 = Cesium.Cartesian3.fromDegreesArray(不带高度的数组)

不带高度的数组。eg:[-278.0,66.0,-123.0,55.0]。相应的有带高度的数组

  1. const cartesian3s = Cesium.Cartesian3.fromDegreesArrayHeights(带高度的数组)

带高度的数组示例:[-278.0,66.0,6000.0,-318,65.0,120000.0]

笛卡尔空间直角坐标系转WGS84

  1. const cartographic = Cesium.Cartographic.fromCartesian(cartesian3)
  1. const cartographic = Cesium.Ellipsoid.WGS84.cartesianToCartographic(cartesian3)

数据多个时调用的方法

  1. const cartographics = Cesium.Ellipsoid.WGS84.cartesianArrayToCartographicArray([笛卡尔对象1,对象2,对象3])

平面坐标系转场景WGS84坐标

  1. const cartesian3 = viewer.scene.pickPosition(Cartesian2)

场景坐标包含了地形、倾斜、模型的坐标

屏幕坐标转地表坐标

  1. const cartesian3 = viewer.scene.globe.pick(viewer.camera.getPickRay(Cartesian2),viewer.scene);

地表坐标指的转地球表面的WGS84坐标,包含地形、不包括模型、倾斜摄影表面

屏幕坐标转椭球面坐标

  1. const cartesian3 = viewer.scene.carmera.pickEllipsoid(Cartesian2)

椭球面坐标参考椭球的WGS84坐标,不包含地形、模型、倾斜摄影表面

五、重要Cesium相机系统

思考:为什么需要相机系统?

以往我们在二维GIS中移动视角或者漫游,如常用的地图,只需要设置视域中间点的经纬度坐标即可。但是在三维中我们不仅需要缺点视点位置,还要缺点视线方向。例如我们在三维中找到一个物体坐标,但是方向反了,在视野中看不到物体的!在Cesium或者其他的3D建模场景中通常使用carmer相机控制场景中的视域。相机就相当于我们的眼睛,控制好它才能达到更好的展示效果。

1.setView方法

setView通过定义相机飞行目的地的三维坐标和视线方向,将视角切换到所设定的视域范围内。

  1. const position = Cesium.Cartesian3.fromDegrees(116.39, 39.91, 400);
  2. viewer.value.camera.setView({
  3. destination:position ,//设置目的地
  4. orientation: {//设置视口方向
  5. heading: Cesium.Math.toRadians(0),//控制视口方向水平旋转,为0表示正北方向
  6. pitch: Cesium.Math.toRadians(-90),//视口上下旋转,-90度俯视朝向地面
  7. roll:0//控制视口的翻转角度
  8. }
  9. })
  10. });

从下面示例可以看出setView方法视角是直接切换,没有空中飞行的过程,适用于快速切换视角。

2.flyTo方法

flyTo具有空中飞行逐步切换视域的效果,可以设置飞行时间。flyTo方法能够带来比较好的视觉效果。

  1. viewer.value.camera.flyTo({
  2. destination: position,
  3. orientation: {//设置视口方向
  4. heading: Cesium.Math.toRadians(0),//控制视口方向水平旋转,为0表示正北方向
  5. pitch: Cesium.Math.toRadians(-90),//视口上下旋转,-90度俯视朝向地面
  6. roll:0//控制视口的翻转角度
  7. },
  8. duration:5,
  9. })

效果如下

3. lookAt方法

lookAt方法也是直接将视角跳转到设置的目的地上,但是鼠标任意旋转视角方向,是不会改变其位置的,常用于锁定视角。

  1. const heading = Cesium.Math.toRadians(50);
  2. const pitch = Cesium.Math.toRadians(-90);
  3. const range = 2500;
  4. viewer.value.camera.lookAt(position, new Cesium.HeadingPitchRange(heading, pitch, range));

4.viewBoundingSphere方法

它的视角切换效果也是和setView方法一样,没有飞行过渡效果,直接切换视口到指定目的地。优点是可以指定玩目标点后,可以从多个角度更好的观测

这里开始引入一个大飞机了,仔细看大飞机模型数据从哪获取的

Cesium提供了一个示例数据集,其中包含了一些常用的示例模型、影像和地形数据,可以用于展示Cesium的功能和效果。

你可以通过以下步骤下载Cesium的示例数据集中的模型文件:

  1. 访问Cesium的示例数据集页面:Cesium示例数据集
  2. 在该页面中,你可以找到各种示例模型的文件夹,包括 CesiumAir 这个示例模型。
  3. 进入 CesiumAir 文件夹,你可以找到 Cesium_Air.glb 这个模型文件。

从git上下载示例文件,并将SampleData一整个拷到我们的项目里

代码添加如下:

  1. const position = Cesium.Cartesian3.fromDegrees(116.39, 39.91, 1500);
  2. const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(-90, 0, 0));
  3. //添加飞机模型
  4. viewer.value.entities.add({
  5. position: position,
  6. orientation: orientation,
  7. model: {
  8. uri: "../src/SampleData/models/CesiumAir/Cesium_Air.glb",
  9. minimumPixelSize: 100,
  10. maximumScale: 10000,
  11. show:true
  12. }
  13. })
  14. viewer.value.camera.viewBoundingSphere(new Cesium.BoundingSphere(position, 20), new Cesium.HeadingPitchRange(0, 0, 0));

无论如何移动相机的视角都是绑定在飞机模型上的,由此发现,当我们需要对一个物体进行多角度观测时,或者建筑物进行定点漫游时,我们就需要viewBoundingSphere方法

六、Cesium地图和地形加载

使用新地图,需要注册ion账号,拿到个人token后将所需要的地图数据加入到自己的账户中,可以用github账户直接登陆

注册Cesium Ion

这步是必须的,否则后面的地图地形建筑体数据都加载不了。cesium Ion提供瓦片图和3D地理空间数据的平台,支持将数据添加到自己的应用中。

  1. 首先去注册一个免费的cesium ION账户。
  2. 打开https://cesium.com/ion/
  3. 打开“Access Token”,跳转到Access Token Page页面。
  4. 选择default默认的asscessToke拷贝到contents中。

注: 在创建Cesium Viewer的时候,将access Token填为自己的access token即可。

  1. Cesium.Ion.defaultAccessToken = window.CesiumAccessToken; //window.CesiumAccessToken:自己的access token.

加载公路地图数据

比如这里选中了公路地图 Bing Maps Road

然后到我的资产中查看,选中刚才加入的地图,在右下角有使用方式,复制即可,注意看添加的地图在你个人资产的位置,不要无脑复用我的代码

  1. viewer.value = new Cesium.Viewer('cesiumContainer', {
  2. infoBox: false, // 禁用沙箱,解决控制台报错
  3. });
  4. viewer.value.imageryLayers.addImageryProvider(
  5. await Cesium.IonImageryProvider.fromAssetId(4),

token怎么添加的截图给你搂一眼哈,加油加油

看下新地图的效果(其实加了层皮肤,这就很像道路地图了)

加载地形数据

Terrain 地形的意思。这玩意就是在学英语。选择Cesium World Terrain做示例

  1. viewer.value.scene.setTerrain(
  2. new Cesium.Terrain(
  3. Cesium.CesiumTerrainProvider.fromIonAssetId(1),
  4. ),
  5. );

通过之前介绍的scene类加入地形。效果如下

添加建筑体

在实际的应用中光有地图和地形肯定是不够的,还需要加载城市中的建筑模型信息。使用Asset Depot提供的建筑体模型。默认是放在My Assets中。同样拷贝示例代码

  1. viewer.value.scene.primitives.add(
  2. await Cesium.Cesium3DTileset.fromIonAssetId(96188),
  3. );
  4. //改变视口,将位置定位在陆家嘴
  5. viewer.value.camera.setView({
  6. destination: Cesium.Cartesian3.fromDegrees(121.49, 31.23, 2000),
  7. orientation: {
  8. heading: 20,
  9. pitch: -20,
  10. roll:0
  11. }
  12. })

viewer中的scene是Cesium虚拟场景中所有3D图形对象和状态的容器。其primitives用于获取大量的基元集合。add方法用于添加数据。Cesium3DTileset用于传输海量异构3D地理空间数据集。

示例如下:

智慧城市的感觉一下就出现了

七、Cesium空间数据加载

在Cesium中一般会使用到的数据分为两种:矢量数据和栅格数据。上面介绍的地形和地图数据的加载属于栅格数据。矢量数据包括几何体的加载,模型、标签等。主要用到Cesium的point、polyline、plane、model、label

几何体——点加载

在直接介绍entity时介绍过点的加载,还有印象吗

代码示例:

增加一个尺寸为100像素的绿色圆点,先设置当前圆点的位置。fromDegrees内的三个参数分别是经度、维度和高度;给圆点设置大小,pixelSize,在设置颜色;将摄像头设置在圆点处,使得我们可以观测到。

使用entities的add方法新增实体,接收参数point表示绘制的是点,像素是100

  1. <script setup lang="ts">
  2. import * as Cesium from 'cesium';
  3. import { onMounted,ref} from 'vue';
  4. const viewer = ref();
  5. onMounted(() => {
  6. viewer.value = new Cesium.Viewer('cesiumContainer', {
  7. infoBox: false, // 禁用沙箱,解决控制台报错
  8. animation: false,//关闭左下角仪表盘(动画器件)
  9. timeline:false,//底部时间线
  10. });
  11. const entity = viewer.value.entities.add({
  12. position: Cesium.Cartesian3.fromDegrees(116.39, 39.91, 400),
  13. point: {
  14. pixelSize: 100,
  15. color: new Cesium.Color(0,1,0,1)
  16. }
  17. })
  18. viewer.value.trackedEntity = entity;
  19. });
  20. </script>

效果展示

几何体——线加载

线条是有多个点连接起来的,positions需要放入一个经纬度坐标集合。

使用Cartesian3.fromDegreeArray()方法接收两个点位信息。还记得前面介绍坐标转换吗,如果有高度信息则使用这个方法fromDegreesArrayHeights

  1. //设置观察点
  2. viewer.value.camera.setView({
  3. destination: position,//设置目的地
  4. orientation: {//设置视口方向
  5. heading: Cesium.Math.toRadians(0),//控制视口方向水平旋转,为0表示正北方向
  6. pitch: Cesium.Math.toRadians(-90),//视口上下旋转,-90度俯视朝向地面
  7. roll: 0//控制视口的翻转角度
  8. }
  9. })
  10. //添加实体
  11. const entity = viewer.value.entities.add({
  12. polyline: {
  13. show: true,
  14. positions: Cesium.Cartesian3.fromDegreesArray([116.39, 39.91, 116.40, 39.91]),
  15. width: 5,
  16. material:new Cesium.Color(0,0,1,1)
  17. }
  18. })

几何体——面加载

为了观察到添加的平面,让视角高于添加的entity哦

  1. const position = Cesium.Cartesian3.fromDegrees(116.39, 39.912, 4000);
  2. //设置观察点
  3. viewer.value.camera.setView({
  4. destination: position, //设置目的地
  5. orientation: {
  6. //设置视口方向
  7. heading: Cesium.Math.toRadians(0), //控制视口方向水平旋转,为0表示正北方向
  8. pitch: Cesium.Math.toRadians(-90), //视口上下旋转,-90度俯视朝向地面
  9. roll: 0 //控制视口的翻转角度
  10. }
  11. });
  12. viewer.value.entities.add({
  13. position: Cesium.Cartesian3.fromDegrees(116.391, 39.914, 1000),
  14. plane: {
  15. plane: new Cesium.Plane(Cesium.Cartesian3.UNIT_Z, 0),
  16. dimensions: new Cesium.Cartesian2(400, 300), //面的长度和宽度
  17. material: Cesium.Color.RED.withAlpha(0.5), //显示材质
  18. outline: true, //显示边框
  19. outlineColor: Cesium.Color.BLACK
  20. }
  21. });

添加面的关键字是plane

几何体——3D模型加载

前面在介绍viewBoundingSphere方法时介绍过飞机模型的引入。还记得吗

  1. //设置观察点
  2. viewer.value.camera.setView({
  3. destination: position, //设置目的地
  4. orientation: {
  5. //设置视口方向
  6. heading: Cesium.Math.toRadians(0), //控制视口方向水平旋转,为0表示正北方向
  7. pitch: Cesium.Math.toRadians(-90), //视口上下旋转,-90度俯视朝向地面
  8. roll: 0 //控制视口的翻转角度
  9. }
  10. });
  11. //添加飞机模型
  12. const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(-90, 0, 0));
  13. viewer.value.entities.add({
  14. position: position,
  15. orientation: orientation,
  16. model: {
  17. uri: '../src/SampleData/models/CesiumAir/Cesium_Air.glb',
  18. minimumPixelSize: 100,
  19. maximumScale: 10000,
  20. show: true
  21. }
  22. });
  23. viewer.value.camera.viewBoundingSphere(
  24. new Cesium.BoundingSphere(position, 20),
  25. new Cesium.HeadingPitchRange(0, 0, 0)
  26. );

从点、线、面到3D模型,可以看出大致原理都是差不多,即给模型一个合适的位置,观察的视角,设置模型的尺寸。

文字——标签加载

除了几何体外,还需要对标签进行加载,说明或解释模型

在飞机模型后面追加label

  1. viewer.value.entities.add({
  2. position: Cesium.Cartesian3.fromDegrees(116.39, 39.912, 810),
  3. label: {
  4. text: '3D模型飞机',
  5. font: '50px Helvetica',
  6. fillColor: Cesium.Color.SKYBLUE
  7. }
  8. });

可以看到通过cesium生成的标签无论如何旋转视角,文字都是朝向屏幕的。对齐缩放也不会影像文字大小。即使回到地球默认视口,见下面示例。

八、Cesium空间数据管理

空间数据管理是三维场景开发的主要内容,包含对数据的创建、增加、修改、删除等。主要学习entity方法

添加我的博客立牌

perfect!看我的博客打在了故宫的上面

  1. viewer.value.camera.viewBoundingSphere(
  2. new Cesium.BoundingSphere(position, 1000),
  3. new Cesium.HeadingPitchRange(30, 0, 0)
  4. );
  5. viewer.value.entities.add({
  6. position: Cesium.Cartesian3.fromDegrees(116.39, 39.91, 0),
  7. plane: {
  8. plane: new Cesium.Plane(Cesium.Cartesian3.UNIT_X, 0.0),
  9. dimensions: new Cesium.Cartesian2(400, 400), //面的长度和宽度
  10. material: '../src/libs/images/blog.png', //显示材质
  11. outline: true, //显示边框
  12. outlineColor: Cesium.Color.BLACK
  13. }
  14. });

entity增删改

add新增

新增当然用entities.add方法啦。这里先创建三个三角体

  1. let redPolygon = viewer.value.entities.add({
  2. polygon: {
  3. hierarchy: Cesium.Cartesian3.fromDegreesArray([116.39, 39.91, 116.39, 39.915, 116.395, 39.91]),
  4. material: Cesium.Color.RED,
  5. extrudedHeight: 200 //拉伸高度
  6. }
  7. });
  8. let bluePolygon = viewer.value.entities.add({
  9. polygon: {
  10. hierarchy: Cesium.Cartesian3.fromDegreesArray([116.38, 39.92, 116.38, 39.915, 116.4, 39.92]),
  11. material: Cesium.Color.BLUE,
  12. extrudedHeight: 200 //拉伸高度
  13. }
  14. });
  15. let yellowPolygon = viewer.value.entities.add({
  16. polygon: {
  17. hierarchy: Cesium.Cartesian3.fromDegreesArray([116.375, 39.905, 116.37, 39.915, 116.375, 39.92]),
  18. material: Cesium.Color.YELLOW,
  19. extrudedHeight: 200 //拉伸高度
  20. }
  21. });

remove移除

把蓝色的移除。调用entities的remove方法。传入创建时返回的实体对象名称

  1. viewer.value.entities.remove(bluePolygon);

getById获取当个实体

有时还需对单独模型属性进行修改,需要使用到getById方法。给生成的几何体加id号。如下代码获取红色的实体将其修改为绿色

  1. let redPolygon = viewer.value.entities.add({
  2. id: 'RedPolygon',
  3. polygon: {
  4. hierarchy: Cesium.Cartesian3.fromDegreesArray([116.39, 39.91, 116.39, 39.915, 116.395, 39.91]),
  5. material: Cesium.Color.RED,
  6. extrudedHeight: 200 //拉伸高度
  7. }
  8. });
  9. viewer.value.entities.getById('RedPolygon').polygon.material = Cesium.Color.GREEN;

removeAll移除全部

有时候需要移除场景中全部的实体,需要使用到remove方法。用于快速移除场景中的实体。

  1. viewer.value.entities.removeAll();

九、Cesium鼠标交互

Cesium提供了多种鼠标交互的方式,提供了用户与客户端信息交互更多的可能

scene.pick返回的是包含给定窗口位置基元的对象。

scene.drillpick返回的是给定窗口位置所有对象列表

Globe.pick返回的是给定光线和地形的交点

Cesium提供了多种鼠标监听可以使用

Cesium.ScreenSpaceEventType.MIDDLE_CLICK 鼠标中键点击事件

Cesium.ScreenSpaceEventType.MOUSE_MOVE 鼠标移入事件

Cesium.ScreenSpaceEventType.RIGHT_CLICK 鼠标右击事件

案例-鼠标左击弹出消息

  1. viewer.value.entities.add({
  2. id: 'planeLogo',
  3. position: Cesium.Cartesian3.fromDegrees(116.39, 39.91, 0),
  4. plane: {
  5. plane: new Cesium.Plane(Cesium.Cartesian3.UNIT_Z, 0.0),
  6. dimensions: new Cesium.Cartesian2(400, 400), //面的长度和宽度
  7. material: '../src/libs/images/blog.png', //显示材质
  8. outline: true, //显示边框
  9. outlineColor: Cesium.Color.BLACK
  10. }
  11. });
  12. let handler = new Cesium.ScreenSpaceEventHandler(viewer.value.scene.canvas); //创建一个屏幕控制实例
  13. //监听各种点击、移入事件等
  14. //第一个参数传入回调函数;第二个传入监听鼠标点击事件类型
  15. handler.setInputAction(function (movement: any) {
  16. let pick = viewer.value.scene.pick(movement.position);
  17. if (Cesium.defined(pick) && pick.id.id === 'planeLogo') {
  18. alert('欢迎关注我的博客');
  19. }
  20. }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

通过id确认点击的位置和目标一致。

十、三维数据格式3DTiles

3D Tiles是Cesium于2016年3月定义的一种三维模型瓦片数据结构。它将海量的三维数据用分块、分层的形式组织起来,很大程度上减轻了浏览器的负担。

此外它还提供了细节层次的LOD功能。在远观时降低模型的面数和精度,拉进后再将细节加载出来。增强了页面的加载速度,更可以用于跨桌面使用,移动和app数据共享。

  1. 3D Tiles

专为流式传输和渲染3D地理数据而设计的,如倾斜摄影测量、BIM、点云、建筑数据等。特点有:

(1)开放且灵活。3D Tiles作为开放数据规范,其切片方案灵活可变,三维模型的切片大小和覆盖范围可以人为设置。此外,3D Tiles还可以适配三维空间中多种空间分区方案,如四叉树、八叉树、KD树等。

(2)异质性支持。通过一组已定义的文件格式,将多种三维地理空间要素(如倾斜摄影测量、BIM、点云、建筑数据、实例化要素等)转换为三维形式的单个数据集,同时又允许多种不同格式标准的模型显示在同一场景中。

(3)专为三维可视化设计。3D Tiles建立在gltf格式之上,并引入了三维图形学技术,以树状的层级细节模型(

  1. HLOD

)进行组织,降低海量数据可视化过程中的浏览器负担,减少WebGL绘制请求的数量。

(4)可交互。3D Tiles支持交互旋转和样式的设置。如单击高亮,修改单个模型的材质,根据属性信息设置不同的显示效果等。

测试使用数据来源可以去前面第六节介绍的Cesium ion添加到自己的Assets中

通过这种方式引入到自己的代码里

  1. const tileset = viewer.value.scene.primitives.add(await Cesium.Cesium3DTileset.fromIonAssetId(96188));

使用下面代码测试一下

  1. viewer.value.camera.flyTo({
  2. destination: new Cesium.Cartesian3(-2703640.80485846, -4261161.990345464, 3887439.511104276),
  3. orientation: new Cesium.HeadingPitchRoll(0.22426651143535548, -0.2624145362506527, 0.000006972977223185239),
  4. duration: 0
  5. });
  6. //加载3D Tiles数据
  7. const tileset = viewer.value.scene.primitives.add(await Cesium.Cesium3DTileset.fromIonAssetId(96188));

十一、Cesium时间系统

时间系统在动态数据可视化中发挥了重要的作用。在三维数据的基础上增加了时间纬度信息。Cesium初始化是自带时间控件的,默认显示当前时间。Cesium的时间控件Clock是由两部分构成。第一部分是Animation控件,控制时间的启动和暂停。第二部分是时间线Timeline控件

隐藏的方法是

  1. viewer.value = new Cesium.Viewer('cesiumContainer', {
  2. infoBox: false, // 禁用沙箱,解决控制台报错
  3. timeline: false,
  4. animation:false,
  5. });

示例:控制时间播放为true,设置时间轴开始和结束时间分别为2011-01-05至2011-01-20

  1. viewer.value.clock.shouldAnimate = true;
  2. viewer.value.clock.multiplier = 1000;
  3. let start = Cesium.JulianDate.fromIso8601('2011-01-05');
  4. let end = Cesium.JulianDate.fromIso8601('2011-01-20');
  5. viewer.value.timeline.zoomTo(start, end);

十二、Cesium粒子系统

粒子系统用于实现各种特效。原理就是将多个小图像组成的集合形成一个模糊对象,从而产生特效。注意,一定要将 shouldAnimate: true,否则动画效果出不来。。

示例,制作飞机火焰效果

  1. viewer.value = new Cesium.Viewer('cesiumContainer', {
  2. infoBox: false, // 禁用沙箱,解决控制台报错
  3. shouldAnimate: true
  4. });
  5. const position = Cesium.Cartesian3.fromDegrees(116.39, 39.912, 1500);
  6. //添加飞机模型
  7. const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(-90, 0, 0));
  8. let entity = viewer.value.entities.add({
  9. position: position,
  10. orientation: orientation,
  11. model: {
  12. uri: '../src/SampleData/models/CesiumAir/Cesium_Air.glb',
  13. minimumPixelSize: 100,
  14. maximumScale: 10000,
  15. show: true
  16. }
  17. });
  18. viewer.value.camera.viewBoundingSphere(
  19. new Cesium.BoundingSphere(position, 20),
  20. new Cesium.HeadingPitchRange(0, 0, 0)
  21. );
  22. //加载粒子类的数据需要使用primitives 该数据类型更接近底层的图形开发
  23. //ParticleSystem 粒子系统管理粒子集合的更新和显示
  24. viewer.value.scene.primitives.add(
  25. new Cesium.ParticleSystem({
  26. image: '../src/SampleData/fire.png', //粒子样式
  27. imageSize: new Cesium.Cartesian2(20, 20), //粒子大小
  28. startScale: 1.0, //初始大小
  29. endScale: 4.0, //结束大小
  30. particleLife: 3.0, //粒子存在的时间
  31. speed: 5.0,
  32. emitter: new Cesium.CircleEmitter(0.5),
  33. emissionRate: 5.0,
  34. modelMatrix: entity.computeModelMatrix(viewer.value.clock.startTime, new Cesium.Matrix4()),
  35. lifetime: 16.0
  36. })
  37. );
  1. entity.computeModelMatrix(viewer.value.clock.startTime, new Cesium.Matrix4())

来计算模型矩阵,并将其应用在粒子系统的设置中。这样可以确保粒子效果能够正确附着在飞机模型上,并跟随模型的位置、旋转和缩放变化而变化。

以上算是Cesium入门了,等实战项目出来后再更~

本文涉及的代码可见我的git

GitHub - OneDayInMarch/cesium-test: cesium项目实践


本文转载自: https://blog.csdn.net/qq_36384657/article/details/136194962
版权归原作者 三月的一天 所有, 如有侵权,请联系我们删除。

“迈向三维:vue3+Cesium.js三维WebGIS项目实战--持续更新中”的评论:

还没有评论