vite5-vue3--cesium1.118 学习(一)

269 阅读8分钟

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 需要引入

image.png

  • 创建项目时也踩坑vite5版本需要 node18或者20+,不然会跑不起来,报错如下

image.png

  • 当前项目使用 node20.14.0 以及项目配置如下 image.png

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>

image.png

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 // 颜色 
    }
})

image.png

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
    },
})

image.png

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
    }
})

image.png

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
    }
  })

image.png

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,
    },
 
})

image.png

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
    }
})

image.png

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'
    }
})

image.png

4.9 ellipse 椭圆

const ellipse = viewer.entities.add({
    position:Cesium.Cartesian3.fromDegrees(118,30),
    ellipse:{
      semiMajorAxis:500, // 半长轴
      semiMinorAxis:300, // 半短轴
      material: Cesium.Color.YELLOW,
      
    }
})
  

image.png

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
    }
})

image.png

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();