Cesium的基本认知
Cesium 是一个Cesium是一个基于JavaScript开发的WebGL三维地球和地图可视化库。它利用了现代Web技术,如HTML5、WebGL和WebAssembly,来提供跨平台和跨浏览器的三维地理空间数据可视化。
Cesium的主要特点包括:
- 跨平台、跨浏览器:无需额外插件,即可在多种操作系统和浏览器上运行。
- 海量数据支持:Cesium定义了3D Tiles数据格式,支持大规模三维模型和地形数据的加载与渲染。
- 丰富的地图模式:支持三维、二维和哥伦布视图(2.5D),提供多种地图和地形图层选择。
- 交互功能:支持地址搜索、信息属性框等用户交互功能,以及全屏模型和WebVR虚拟现实体验
概括的来说,CesiumJS 主要是做3D场景地理信息的开发库,也就是WebGIS,也可以做一些模型等等,不局限于地理行业。
为了更好的感知这个库,我使用了 Vite5 + Vue3 + Cesium1.118 初始化一个工程,进行学习并记录相关示例。 在学习的之前,我通读了官网的入门教程,以及B站上相关入门视频,找到了一些学习资料,当然版本是老版本,但是我学习的是最新版,可能有一些偏差,但是问题不大。
1 从 Hello World !!! 开始
1.1 工程初始化
// 1. 创建 vite-vue3 工程
npm create vite
// 2. 安装依赖
npm i cesium vite-plugin-cesium -D
- vite.config.js 需要引入
- 创建项目时也踩坑vite5版本需要 node18或者20+,不然会跑不起来,报错如下
- 当前项目使用 node20.14.0 以及项目配置如下
1.2 配置完成后最简单的示例
这里需要去官网注册token 官网传送门
这个的Cesium.Ion 是官网的一种资源服务,可以上传地图、图层、模型等数据,并提供了相关API进行快捷使用,这里就演示了。 options 配置项,这里演示了一些界面交互组件,当然还有很多其他功能的
<template>
<div id="cesiumContainer"></div>
</template>
<script setup>
import * as Cesium from "cesium";
import { onMounted } from "vue";
onMounted(() => {
// console.log(Cesium); 判断是否引入成功
const token = "your token";
Cesium.Ion.defaultAccessToken = token; // 设置个人资产账户的token
const options = {
// 界面控件 全部关闭
// timeline: false, // 时间轴控件
// animation: false, // 动画控件
// geocoder: false, // 搜索按钮
// homeButton: false, // 主页按钮
// sceneModePicker: false, // 投影方式按钮
// baseLayerPicker: false, // 图层选择按钮
// navigationHelpButton: false, // 帮助手势按钮
// fullscreenButton: false, // 全屏按钮
}
const viewer = new Cesium.Viewer("cesiumContainer",options);
});
</script>
<style scoped>
#cesiumContainer {
width: 100%;
height: 100%;
}
</style>
2 补充一点概念 坐标系
Cesium 在绘制3D图形时,无法直接使用(经度,维度,高度)去绘制一个点,计算机图形学使用了笛卡尔坐标系去描述界面上的位置方向信息,因此在使用相关api时,需要转化为对应的坐标去使用
2.1 坐标系的类型
-
平面直角坐标系(笛卡尔坐标系)
- 一般用于表示屏幕的坐标拾取
new Cesium.Cartesian2 ( x , y )
-
空间直角坐标系(笛卡尔坐标系) 世界坐标
- Cesium 以地球的球心为原点,建立了空间直角坐标系(笛卡尔坐标系),用于描述3D空间中的位置和方向
new Cesium.Cartesian3 ( x , y , z )- x :横轴(默认指向东经0度,北纬0度);
- y:纵轴(默认指向东经90度,北纬0度);
- z:竖轴(默认指向东经0度,北纬90度
-
WGS84坐标系(球坐标系)
- 以弧度描述 3D空间的位置和方向,就是我们熟知的经纬度
new Cesium.Cartographic(longitude, latitude, height)// 经度,维度,高度- height 为球心距,单位米
2.2 坐标系之间的相互转化
2.2.1 (经、纬、高度)➡️ Cartesian3
const cartesian = Cesium.Cartesian3.fromDegrees(110,20,30) // 经度、纬度、高度
console.log('笛卡尔坐标 cartesian',cartesian);
2.2.2 Cartesian3 ➡️(经、纬、高度)
// 笛卡尔坐标转经纬度 要分两步
// 1. 笛卡尔坐标 转弧度坐标
const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
console.log('弧度坐标 cartographic',cartographic);
// 2. 弧度坐标转(角坐标)经纬度、高度
const lon = Cesium.Math.toDegrees(cartographic.longitude)
const lat = Cesium.Math.toDegrees(cartographic.latitude)
// 3. 得到 经度、纬度、高度
console.log(lon,lat,cartographic.height)
2.2.3 Cartesian2 ➡️ Cartesian3
鼠标点击屏幕上的坐标 转化为 空间坐标
const handelCartesian2ToCartesian3 = () => {
let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (event) {
const cartesian2 = event.position;
console.log("屏幕坐标(平面坐标系cartesian2)", event.position);
const cartesian3 = viewer.scene.globe.pick(
viewer.camera.getPickRay(cartesian2),
viewer.scene
);
console.log("空间坐标系(笛卡尔坐标系cartesian3)", cartesian3);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
2.2.3 Cartesian3 ➡️ Cartesian2
鼠标点击事件
const handelCartesian3ToCartesian2 = () => {
let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (event) {
const cartesian = event.position;
console.log("屏幕坐标(平面坐标系cartesian2)", event.position);
const cartesian3 = viewer.scene.globe.pick(
viewer.camera.getPickRay(cartesian),
viewer.scene
);
console.log("空间坐标系(笛卡尔坐标系cartesian3)", cartesian3);
const cartesian2 = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
viewer.scene,
cartesian3
);
console.log("转换后:屏幕坐标(平面坐标系cartesian2)", cartesian2);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
3 Camera 相机的简单使用
相机的使用有两个属性position、orientation
-
position : 位置,需要世界坐标(Cartesian3)
-
orientation: 观察角度,类比人的眼睛去解释
- heading(偏航角): 相当于左右摆头
- pitch(俯仰角):相当于上下点头
- roll(翻滚角): 相当于歪头,药水哥经典技能之一
3.1 camera.setView 观察到某个点
// viewer 为 Cesium.Viewer 实例
const position = Cesium.Cartesian3.fromDegrees(110, 20); // 经纬度的位置
viewer.camera.setView({
// 位置 接受不了笛卡尔坐标系坐标
destination:position,
// 视角 默认是(0,-90,0) 看向地面
orientation:{
// heading 偏航角
heading:Cesium.Math.toRadians(0),
// pitch 仰俯角
pitch:Cesium.Math.toRadians(-90),
// roll 翻滚角
roll:Cesium.Math.toRadians(0),
}
})
3.2 camera.flyto 飞行方法
// viewer 为 Cesium.Viewer 实例
const position = Cesium.Cartesian3.fromDegrees(110, 20); // 经纬度的位置
viewer.camera.flyTo({
// 位置
destination: position,
// 视角
orientation: {
// heading 偏航角
heading: Cesium.Math.toRadians(0),
// pitch 仰俯角
pitch: Cesium.Math.toRadians(0),
// roll 翻滚角
roll: Cesium.Math.toRadians(0),
},
// 动画时长 单位秒
duration: 5,
});
3.3 camera.lookAt 视角聚焦一个点,但是可以改变其他方位
// lookAt 将视角锁定到设置的点位上,可以选择视角,但是不能改变位置
const position2 = Cesium.Cartesian3.fromDegrees(110, 20); // 经纬度的位置
viewer.camera.lookAt(
position2,
new Cesium.HeadingPitchRange(
Cesium.Math.toRadians(0),
Cesium.Math.toRadians(-90),
20000
)
);
4. Entity 实体
实体是3D空间中的物体,比如点、线、面模型等等
4.1 基本的两种写法
// 新增一个点
// 写法一
const point = new Cesium.Entity({
position: Cesium.Cartesian3.fromDegrees(120,30,100),
point:{
pixelSize:20, // 点到大小,单位:像素点
color:Cesium.Color.RED // 颜色
}
})
viewer.entities.add(point) // 加入到视图的实体容器中
viewer.zoomTo(point)// 跳转到这个点
// 写法二
viewer.entities.add({
id:'point1',
position: Cesium.Cartesian3.fromDegrees(120,30,100),
point:{
pixelSize:20, // 点到大小,单位:像素点
color:Cesium.Color.RED // 颜色
}
})
4.2 Point 点
const point = viewer.entities.add({
id:'point1',
position: Cesium.Cartesian3.fromDegrees(120,30,100),
point:{
pixelSize:20, // 点到大小,单位:像素点
color:Cesium.Color.BLUE // 颜色
}
})
4.3 billBoard 广告牌
const billBoard = viewer.entities.add({
position:Cesium.Cartesian3.fromDegrees(130,31),
billboard:{
image:'/src/assets/image/position.png', // 资源
scale:0.2 ,// 广告牌大小
color:Cesium.Color.YELLOW
},
})
4.4 label 文本区
const label = viewer.entities.add({
position:Cesium.Cartesian3.fromDegrees(140,30,20000),
label:{
text:'label1',
font:'30px',
fillColor:Cesium.Color.BLUE,
showBackground:true,
backgroundColor:Cesium.Color.RED
}
})
4.5 polyline 线
const polyline = viewer.entities.add({
polyline:{
positions: Cesium.Cartesian3.fromDegreesArray([120,40,125,40,150,50]),// 经纬度数组(120,40)(125,40)(150,50)
width:10,
material:Cesium.Color.YELLOW
}
})
4.6 polygon 多边型(平面 or 立体)
const polygon = viewer.entities.add({
polygon:{
hierarchy:{
positions: Cesium.Cartesian3.fromDegreesArray([140,40,145,40,170,50]),// 经纬度数组(120,40)(125,40)(150,50)
},
// material:Cesium.Color.GREEN, // 颜色
material:new Cesium.Color(0,0,0,0), // 颜色
height:100000, // 高度
extrudedHeight:200000, // 加了这个物体就变成了立体图形,且该高度为顶面高度
outline:true,
outlineColor:Cesium.Color.WHITE
// fill:true,
},
})
4.7 box 长方体
const box = viewer.entities.add({
position:Cesium.Cartesian3.fromDegrees(100,70,3000),// 位置
box:{
dimensions: new Cesium.Cartesian3(2000,1000,3000),// 长宽高
material:Cesium.Color.SKYBLUE
}
})
4.8 rect 矩形
定义一个矩形只需要左下、右上两个坐标点就可以
const rectangle = viewer.entities.add({
rectangle:{
coordinates:Cesium.Rectangle.fromDegrees(110,40,113,45) ,// 左下角、右上角的坐标
extrudedHeight:30000,
material:'/src/assets/image/pic1.webp'
}
})
4.9 ellipse 椭圆
const ellipse = viewer.entities.add({
position:Cesium.Cartesian3.fromDegrees(118,30),
ellipse:{
semiMajorAxis:500, // 半长轴
semiMinorAxis:300, // 半短轴
material: Cesium.Color.YELLOW,
}
})
4.10 实体类的组合使用
各种实体类直接可以共用,有一种组的概念在里面
const entities = viewer.entities.add({
position: new Cesium.Cartesian3.fromDegrees(100,108,1000),
billboard:{
image:'/src/assets/image/position.png', // 资源
scale:0.2 ,// 广告牌大小
color:Cesium.Color.YELLOW
},
label:{
text:'定位点1',
font:'30px',
pixelOffset:new Cesium.Cartesian2(0,-40),
fillColor:Cesium.Color.BLUE,
showBackground:false,
backgroundColor:Cesium.Color.RED,
},
polyline:{
positions: Cesium.Cartesian3.fromDegreesArrayHeights([100,108,0,100,108,1000]),// 经纬度数组(120,40)(125,40)(150,50)
width:1,
material:Cesium.Color.SKYBLUE
}
})
4.11 实体类的删除
//如果已删除则返回true,如果该集合中不存在该实体,则返回false
viewer.entities.remove(boxEntity);
//如果已删除则返回true,如果该集合为空,则返回false
viewer.entities.removeAll();
//如果已删除则返回true,如果该集合中不存在该实体,则返回false
viewer.entities.removeById("box");
//先获取实体,再删除
const box = viewer.entites.getById("box");
viewer.entities.remove(box);
4.12 实体类的增删操作的性能优化
频繁处理大量的增删时,避免过剩的性能消耗
// 暂停事件处理 会先暂停实体类的相关事件触发
viewer.entities.suspendEvents();
// 执行批量更新
for (let i = 0; i < 1000; i++) {
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(Math.random() * 360 - 180, Math.random() * 180 - 90),
point: {
pixelSize: 10,
color: Cesium.Color.RED,
},
});
}
// 恢复事件处理
viewer.entities.resumeEvents();