# 基于开源项目cesium进行梳理

// 可以把这个文件直接放到我们的public,或者这样引用
import "cesium/Build/Cesium/Widgets/widgets.css";

# cesium Viewer类

  const viewer = new Cesium.Viewer("cesiumContainer", {
    infoBox: false,
    timeline: false, // 是否显示时间线控件
    imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
      url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer",
    }),
    terrainProvider: new Cesium.CesiumTerrainProvider({
      url: "http://data.marsgis.cn/terrain",
    }),
    // terrain: Cesium.Terrain.fromWorldTerrain({
    //   requestVertexNormals: true, //Needed to visualize slope
    // }),

  });
  • infoBox :是否显示Cesium Viewer中的信息框(InfoBox)。信息框通常用于在鼠标悬停在某个实体(如地标、建筑物等)上时显示有关该实体的额外信息。将其设置为false意味着不会显示这些信息框。
  • imageryProvider:用于指定地图的图像提供者
    • 类 ArcGisMapServerImageryProvider (options)提供由ArcGIS MapServer托管的平铺图像。默认情况下,服务器的预缓存磁贴为使用(如果有)。
    • 类 UrlTemplateImageryProvider通过使用指定的URL模板请求图块来提供图像。
  • terrainProvider:用于指定地形数据的提供者

# cesium Viewer开发一些基本配置

// 不显示底图
// viewer.imageryLayers.get(0).show = false;
// 去除logo
viewer.cesiumWidget.creditContainer.style.display = "none";
// 显示帧率
viewer.scene.debugShowFramesPerSecond = true;
viewer.scene.globe.depthTestAgainstTerrain = true;

# cesium 开启抗锯齿

/**
 * 开启抗锯齿
 */
import * as Cesium from "cesium";

const disableSawtooth = (viewer) => {
    // 是否支持图像渲染像素化处理
    if (Cesium.FeatureDetection.supportsImageRenderingPixelated()) {
        viewer.resolutionScale = window.devicePixelRatio;
    }

    // 开启抗锯齿
    viewer.scene.postProcessStages.fxaa.enabled = true;
};

export default disableSawtooth;

# cesium zoomTo

// 缩放视图到实体  
viewer.zoomTo(entity);

# Cesium.createWorldTerrainAsync

用于异步地创建并返回一个全球地形提供者(CesiumTerrainProvider)的实例

const worldTerrainProvider = await Cesium.createWorldTerrainAsync();
viewer.terrainProvider = worldTerrainProvider;

# cesium天空盒SkyBox

  // 外天空盒
  viewer.scene.skyBox = new Cesium.SkyBox({
    sources: {
      positiveX: "/images/Standard-Cube-Map/px1.png",
      negativeX: "/images/Standard-Cube-Map/nx1.png",
      positiveY: "/images/Standard-Cube-Map/pz.png",
      negativeY: "/images/Standard-Cube-Map/nz1.png",
      positiveZ: "/images/Standard-Cube-Map/py.png",
      negativeZ: "/images/Standard-Cube-Map/ny1.png",
    },
  });

# Cesium各类事件系统详解

Cesium中监听鼠标事件主要是ScreenSpaceEventHandler类,该类主要就是处理用户输入的,而鼠标事件类型主要是ScreenSpaceEventType

# cesium事件相关

类ScreenSpaceEventHandler ( element ):处理用户输入事件。可以添加自定义功能以在以下位置执行当用户输入输入时。

// 监听点击事件,拾取坐标
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction((e) => {
const clickPosition = viewer.scene.camera.pickEllipsoid(e.position);
const randiansPos = Cesium.Cartographic.fromCartesian(clickPosition);
console.log(
    "经度:" +
    Cesium.Math.toDegrees(randiansPos.longitude) +
    ", 纬度:" +
    Cesium.Math.toDegrees(randiansPos.latitude)
);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
viewer.screenSpaceEventHandler.setInputAction(function(click) {  
    // 使用viewer.pick来检查点击位置下的实体  
    var pickedObject = viewer.scene.pick(click.position);  
    if (Cesium.defined(pickedObject) && pickedObject.id === polygon) {  
        console.log('多边形被点击');  
        // 在这里可以添加更多的处理逻辑  
    }  
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);  

# cesium 实体事件

selectedEntityChanged​,这样的话,如果确定是监听Entity的点击事件,就不用那么麻烦了。​

# cesium 相机事件​

handler.setInputAction((e) => {​
    console.log("相机左键拖拽", e);}, CameraEventType.LEFT_DRAG);

# cesium 键盘事件

在三维场景中,比较常用的键盘事件就是前进,后退,向左,向右等操作

<template>
  <OperateBox>
    <el-button type="primary" @click="onClick">监听鼠标单击</el-button>
    <el-button type="primary" @click="onRightDown">监听右键按下</el-button>
    <el-button type="primary" @click="onMiddleClick">监听滚轮键单击</el-button>
    <el-button type="primary" @click="onMouseMove">监听鼠标移动</el-button>
    <el-button type="primary" @click="removeMonitor">解除鼠标事件监听</el-button>
  </OperateBox>
  <OperateBox style="top: 40px">
    <el-button type="primary" @click="onCameraDrag">相机监听左键拖拽</el-button>
  </OperateBox>
  <OperateBox style="top: 80px">
    <el-button type="primary" @click="onKeyboard">开启键盘事件</el-button>
  </OperateBox>
</template>
<script setup>
import { onUnmounted, ref } from "vue";
import { ScreenSpaceEventHandler, ScreenSpaceEventType, GeoJsonDataSource, CameraEventType } from "cesium";

const { __viewer } = window;

GeoJsonDataSource.load("/json/chuzhong.geojson").then((dataSource) => {
  __viewer.dataSources.add(dataSource);
  __viewer.zoomTo(dataSource);
});

const handler = new ScreenSpaceEventHandler(__viewer.scene.canvas);

const onClick = () => {
  handler.setInputAction((e) => {
    console.log("鼠标左键点击", e);
  }, ScreenSpaceEventType.LEFT_CLICK);
};

const onRightDown = () => {
  handler.setInputAction((e) => {
    console.log("鼠标右键按下", e);
  }, ScreenSpaceEventType.RIGHT_DOWN);
};

const onMiddleClick = () => {
  handler.setInputAction((e) => {
    console.log("滚轮键单击", e);
  }, ScreenSpaceEventType.MIDDLE_CLICK);
};

const onMouseMove = () => {
  handler.setInputAction((e) => {
    console.log("鼠标移动", e);
  }, ScreenSpaceEventType.MOUSE_MOVE);
};
// Entity独有的实体选择事件
__viewer.selectedEntityChanged.addEventListener((e) => {
  console.log("当前点击实体", e);
});

const cameraController = __viewer.scene.screenSpaceCameraController;

const onCameraDrag = () => {
  handler.setInputAction((e) => {
    console.log("相机左键拖拽", e);
    // cameraController.enableRotate = false;
    // cameraController.enableTranslate = false; // 不允许拖拽
    // cameraController.enableZoom = false; // 不允许缩放
    // cameraController.enableLook = false;
    // cameraController.enableTilt = false; // 禁用倾斜
    cameraController.enableInputs = false; // 禁用相机控制
  }, CameraEventType.LEFT_DRAG);
};

const onKeyboard = () => {
  // 监听键盘事件
  document.addEventListener("keydown", cameraMove, false);
};
const cameraMove = (event) => {
  // 设置相机移动的步长
  var moveAmount = 100.0;
  var camera = __viewer.camera;
  switch (event.key) {
    case "w": // 前进
      camera.moveForward(moveAmount);
      break;
    case "s": // 后退
      camera.moveBackward(moveAmount);
      break;
    case "a": // 向左
      camera.moveLeft(moveAmount);
      break;
    case "d": // 向右
      camera.moveRight(moveAmount);
      break;
    case "q": // 向上
      camera.moveUp(moveAmount);
      break;
    case "e": // 向下
      camera.moveDown(moveAmount);
      break;
    default:
      break;
  }
};

const removeMonitor = () => {
  handler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK);
  handler.removeInputAction(ScreenSpaceEventType.RIGHT_DOWN);
  handler.removeInputAction(ScreenSpaceEventType.MIDDLE_CLICK);
  handler.removeInputAction(ScreenSpaceEventType.MOUSE_MOVE);
  handler.removeInputAction(CameraEventType.LEFT_DRAG);
  cameraController.enableInputs = true;
  document.removeEventListener("keydown", cameraMove, false);
  __viewer.dataSources.removeAll();
};
onUnmounted(() => {
  removeMonitor();
});
</script>
<style lang="less" scoped></style>
  • setInputAction (action, type, modifier ):设置要在输入事件上执行的功能。
  • removeInputAction (type, modifier ):移除
  • destroy ():删除此对象持有的侦听器

# Cesium的四种点击拾取方法

  1. viewer.scene.pick():通过坐标位置,拾取实体(Entity),图元(Primitive),3DTiles对象,返回的是scene中指定位置最上层的对象。例如点击获取Entity的pick对象,通过pick.id可以拾取当前的entity对象。拾取后,可以用于改变对象的属性参数,如颜色,图片等。

  2. viewer.scene.globe.pick():返回一个射线(ray)和地球表面的一个交点的Cartesian3坐标。此方法一般用于获取加载地形后的经纬度和高程,不包括模型、倾斜摄影等表面高度。

let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (event) {
     let ray = viewer.camera.getPickRay(event.position);
     let position = viewer.scene.globe.pick(ray, viewer.scene);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  1. viewer.scene.camera.pickEllipsoid():返回相机视角下鼠标点击的对应椭球面位置。接收屏幕坐标,返回Cartesian3坐标。适用裸球表面的选取,是基于数学模型的椭圆球体。

注意事项:在有地形的情况下误差较大,在使用时需要关闭深度测试。

let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (event) {
     let position = viewer.scene.camera.pickEllipsoid(event.position, viewer.scene.globe.ellipsoid);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  1. viewer.scene.pickPosition()

拾取对应位置的Cartesian3,适用于模型表面位置的选取,拾取三维物体的坐标等。

注意事项: 一定开启深度检测(viewer.scene.globe.depthTestAgainstTerrain = true),否则在没有没有3dTile模型的情况下,会出现空间坐标不准的问题。

let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (event) {
     let position = viewer.scene.pickPosition(event.position);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

# Cesium.defined

Cesium.defined 是一个工具函数,用于判断给定的值是否已定义且非空。这个函数在处理可能未定义(undefined)或为空(null)的变量时非常有用

# Cesium.ScreenSpaceEventType

LEFT_CLICK, 单击,Cesium.ScreenSpaceEventType 枚举中还有其他许多事件类型,如 RIGHT_CLICK(右键点击)、MOUSE_MOVE(鼠标移动)、WHEEL(鼠标滚轮滚动)

# gis基础

# GIS坐标系​

  1. 经纬度坐标(球面坐标): 经纬度坐标通常被称为地理坐标或地球坐标,它是一种基于地球表面的坐标系统,用于确定地球上任何点的位置。这种坐标系统使用两个主要的数值来表示位置:经度和纬度。​

经纬度坐标也常常被称为:球面坐标/大地坐标/WGS84坐标

  1. 地理坐标(弧度) ​:在地理信息系统(GIS)中,地理坐标通常指的是地球上某个点的位置,使用经纬度来表示。然而,由于地球是一个近似的椭球体,使用弧度而非角度来表示经纬度坐标可以避免在计算中引入的某些复杂性,尤其是在进行距离和面积的测量时。​

弧度是一种角度的度量单位,它基于圆的周长和半径之间的关系。一个完整的圆周被定义为 2π弧度。

  1. 屏幕坐标系(Screen Coordinate System): 是一种二维坐标系统,它用于描述屏幕上的点或区域的位置。屏幕坐标系通常以屏幕的左上角为原点,水平向右为 x 轴正方向,垂直向下为 y 轴正方向。​
new Cesium.Cartesian2(x, y)
  1. 空间直角坐标系​:在地理信息系统(GIS)中,空间直角坐标系(Spatial Cartesian Coordinate System)是一种三维坐标系统,用于在三维空间中精确地表示点、线、面的位置。这种坐标系通常由三个正交的坐标轴组成:X、Y 和 Z 轴。​世界坐标通常指的是笛卡尔空间直角坐标系中的坐标,也被称为Cartesian3坐标。

# cesium位置计算转换

  1. 经纬度转空间直角
const cartesian3 = Cesium.Cartesian3.fromDegrees(lng, lat, height);
  1. 经纬度转地理坐标(弧度)
const radians = Cesium.Math.toRadians(degrees) 
  1. 地理坐标(弧度)转经纬度​
const degrees = Cesium.Math.toDegrees(radians)
  1. 空间直角转经纬度
// 先将3D笛卡尔坐标转为地理坐标(弧度) ​
const cartographic = Cesium.Cartographic.fromCartesian(cartesian3);// 再将地理坐标(弧度)转为经纬度​
const lat = Cesium.Math.toDegrees(cartographic.latitude);const lng = Cesium.Math.toDegrees(cartographic.longitude);const height = cartographic.height;
  1. 屏幕坐标转经纬度​
// 监听点击事件,拾取坐标​
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);​
handler.setInputAction((e) => {// 点击点的位置
  const clickPosition = viewer.scene.camera.pickEllipsoid(e.position);const randiansPos = Cesium.Cartographic.fromCartesian(clickPosition);​
  console.log("经度:" +​
      Cesium.Math.toDegrees(randiansPos.longitude) +", 纬度:" +​
      Cesium.Math.toDegrees(randiansPos.latitude));}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  1. 屏幕坐标转空间直角坐标​ ​
var cartesian3 = viewer.scene.globe.pick(viewer.camera.getPickRay(windowPostion), viewer.scene); ​
​
  1. 世界坐标转屏幕坐标​ ​
windowPostion = Cesium.SceneTransforms.wgs84ToWindowCoordinates(viewer.scene, cartesian3);
  // 转换为笛卡尔坐标
  const cartesian1 = Cesium.Cartesian3.fromDegrees(110, 20, 20);
  const cartesian2 = Cesium.Cartesian3.fromDegrees(110, 20, 30);

  // 转为弧度制
  let cartographic = Cesium.Cartographic.fromCartesian(cartesian1);
  // 角度制
  let lon = Cesium.Math.toDegrees(cartographic.longitude);
  let lat = Cesium.Math.toDegrees(cartographic.latitude);

  console.log("笛卡尔坐标:",cartesian1, cartesian2);
  console.log("弧度制",cartographic);
  console.log('经纬度',lon, lat);

//   笛卡尔坐标: Je {x: -2050703.247253584, y: 5634260.865831492, z: 2167703.6282316237} 
//   Je {x: -2050706.4611916323, y: 5634269.696053707, z: 2167707.0484330566}

//  弧度制 Fr {longitude: 1.9198621771937627, latitude: 0.34906585039886584, height: 19.999999999848896}
  
//  经纬度 110.00000000000001 19.999999999999996

# cesium carmera方法

console.log(viewer.camera === viewer.scene.camera);
//true
  • viewer.scene.camera.pickEllipsoid(e.position):pickEllipsoid方法如果选择了椭球或地图,则返回椭球或地图表面上的点在世界坐标系中。如果未选择椭球或地图,则返回undefined。

# cesium carmera 飞行模式

  • viewer.camera.flyTo
viewer.camera.flyTo({  
    destination : Cesium.Rectangle.fromDegrees(west, south, east, north)  
});
// 这个示例展示了如何飞行到一个由经纬度界定的矩形区域。Cesium会尝试找到一个视角,
//以便整个矩形都能被相机看到,但通常不会保证是顶部向下的视角。要实现顶部向下的视角,您可能需要额外设置相机的方向。
viewer.camera.flyTo({  
    destination : Cesium.Cartesian3.fromDegrees(-122.19, 46.25, 5000.0),  
    orientation : {  
        direction : new Cesium.Cartesian3(-0.04231243104240401, -0.20123236049443421, -0.97862924300734),  
        up : new Cesium.Cartesian3(-0.47934589305293746, -0.8553216253114552, 0.1966022179118339)  
    }  
});
//这个示例中,相机被设置为飞行到一个特定的位置,并具有一个由单位向量指定的方向和向上的方向。
//单位向量需要是标准化的(即它们的长度等于1),并且在这个上下文中,它们定义了相机在到达目标位置时应该看向哪里以及它的“上方”应该指向哪里。
viewer.camera.flyTo({  
    destination : Cesium.Cartesian3.fromDegrees(-122.19, 46.25, 5000.0),  
    orientation : {  
        heading : Cesium.Math.toRadians(175.0),  
        pitch : Cesium.Math.toRadians(-35.0),  
        roll : 0.0  
    }  
});
// 相机被设置为飞行到一个特定的位置,并具有通过航向(heading)、俯仰(pitch)和翻滚(roll)角指定的方向。
// 航向是围绕垂直轴(即Z轴)的旋转,俯仰是围绕水平轴(即Y轴)的旋转,而翻滚是围绕相机的视线方向(即X轴)的旋转。
// 在这个例子中,相机将面向几乎正北方向(航向175度),向下倾斜35度,且没有翻滚。这种方式比使用单位向量更直观
  • flyTo 飞行至指定模型:当在场景里渲染了一个3D模型,但是找不到的时候,可以使用__viewer.flyTo(tileset)将相机自动定位到模型位置;​
const tileset = new Cesium.Cesium3DTileset(tilesetJson);​
__viewer.flyTo(tileset);
  • setView: 方法用于设置相机的视图,包括目标位置、朝向、以及可能的缩放级别,类似flyto只是没有过渡动画
viewer.camera.setView({
  // 从以度为单位的经度和纬度值返回笛卡尔3位置。
  destination: Cesium.Cartesian3.fromDegrees(120.36, 36.09, 40000),
});
  • flyToBoundingSphere 指定边界​
const flyBounding = () => {const boundingSphere = new BoundingSphere(​
        Cartesian3.fromDegrees(120.36, 36.09, 0),40000);​
    camera.flyToBoundingSphere(boundingSphere, {duration: 3,});};

BoundingSphere:具有中心和半径的包围球,API接口,第一个参数是指定中心点,第二个参数是球体半径,单位m;​

  • zoomTo 相机追踪​:当你创建好entity实例之后,可以直接将视角定位到实例的可视区域;​
const entity = __viewer.entities.add({position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),billboard: {image: "../images/Cesium_Logo_overlay.png",sizeInMeters: true,},});​
​
__viewer.zoomTo(entity);

# cesium各种Collection的作用和使用​

在Cesium中,Collection类用于管理和组织一组相关对象。这些集合提供了一种高效的方法来管理和操作多个对象,从而简化了对大规模数据的操作和渲染。​

# cesium EntityCollection​

EntityCollection 是用于存储和管理 Entity 对象的集合。它为管理和操作大量的实体提供了一种结构化的方法,并提供了事件机制,用于响应实体的添加、删除或修改。​

应用场景:​

  • 在动态场景中使用EntityCollection管理一组动态变化的实体,例如车辆、飞机等。
  • 使用 DataSource(例如 CzmlDataSource)加载复杂数据集时,EntityCollection被用来管理这些数据。​
  • 实现选择、过滤和批量操作实体。

与Entity的关系:​

  • 每个Entity代表一个独立的对象,可以包含多种几何形状、图标、标签等。
  • EntityCollection 提供了一个组织结构,将多个Entity对象组合在一起进行管理。​
  • EntityCollection 提供了事件支持(如 collectionChanged),帮助开发者跟踪实体的变化。

通常不直接实例化EntityCollection 类,而是通过__viewer.entities直接获取​

  const point = __viewer.entities.add({
    id: "point_01",
    position: Cartesian3.fromDegrees(-75.59777, 40.03883),
    billboard: {
      image: "/images/garden-icon.png",
    },
  });
  const line = __viewer.entities.add({
    id: "polyline_01",
    polyline: {
      positions: Cartesian3.fromDegreesArray([-75.59777, 40.03883, -100.03883, 40.03883]),
      width: 3,
      material: Color.RED,
    },
  });
  const polygon = __viewer.entities.add({
    id: "polygon_01",
    polygon: {
      hierarchy: Cartesian3.fromDegreesArray([-109.0, 30.0, -95.0, 30.0, -95.0, 40.0, -109.0, 40.0]),
      material: Color.RED.withAlpha(0.5),
    },
  });
  console.log("entityCollection", __viewer.entities);
  console.log("entityCollection的values", __viewer.entities.values);

将点、线、面通过__viewer.entities.add都添加进集合entityCollection实例中,而add就是entityCollection提供的方法

# cesium 实体

Entity是Cesium中封装好的一个非常强大的类,封装了复杂的底层操作,使开发者可以更简单地创建和管理各种对象,而不需要深入了解底层图形学细节。​ ​ Entity能够支持渲染点、线、面、多边形、圆柱、圆锥、盒子、椭球等各种几何形状,还可以为这些形状指定颜色、材质、透明度等属性。​ ​ 在viewer对象中就包含一个entities对象.

一种直接new Entity,然后添加到场景中。

第二个用viewer.scene.add添加。

链接

以billboard为例

创建

const { entities } = window.__viewer;​
entities.add({id: "point_01",position: Cartesian3.fromDegrees(-75.59777, 40.03883),billboard: {image: "/images/garden-icon.png",},});

修改:通过entities.getById(id)找到要修改的几何体,然后重新设置属性

const modifyBillboard = () => {​
    entities.getById("point_01").billboard.width = 100;​
    entities.getById("point_01").billboard.height = 100;​
    entities.getById("point_01").billboard.color = Color.BLUE.withAlpha(0.7);};

删除:

const removeBillboard = () => {// 指定删除​
    entities.removeById("point_01");// 删除所有,但注意:这里不仅仅删除的是billboard,而是所有用entity渲染的几何体​
    // entities.removeAll();​
};

Polyline 用于在地图上绘制线条

const addPolyline = () => {​
    entities.add({id: 'polyline_01',polyline: {positions: Cartesian3.fromDegreesArray([-75.59777, 40.03883, -100.03883, 40.03883]),width: 3,material: Color.RED,},});};

fromDegreesArray:线段,至少由2个点位相连形成,所有positions需要是一个坐标数组:[经度、纬度、经度、纬度...]​

Polygon 用于在地图上绘制多边形

hierarchy:现在可以简单理解为绘制多边形的所需的坐标点位,我这里画了个矩形,所以需要4个经纬度坐标组成的数组;​

const addPolygon = () => {​
    entities.add({id: "polygon_01",polygon: {hierarchy: Cartesian3.fromDegreesArray([-109.0, 30.0, -95.0, 30.0, -95.0, 40.0, -109.0, 40.0]),material: Color.RED.withAlpha(0.5),},});};

# cesium PrimitiveCollection​

PrimitiveCollection 是 Cesium 中用于管理和渲染一组图元(Primitives)的集合。图元是 Cesium 中的低级别渲染对象,通常用于直接控制渲染管线。​

PrimitiveCollection 的作用​ ​

  • 管理低级别渲染对象:PrimitiveCollection 用于管理和组织一组图元(如 Billboards、Polylines、Points、Models 等)。这些图元直接与 WebGL 图形管线交互,可以提供高效的渲染性能。
  • 自定义渲染流程:提供了一种直接控制渲染顺序和方式的机制,允许更精细的图形控制和优化。​​

PrimitiveCollection 的优势​ ​

  • 性能优化:由于直接操作图元,PrimitiveCollection 提供了比 EntityCollection 更高的性能。这在需要渲染大量对象时尤其明显​。绘制大量Primitive时,可以将其合并为单个Geometry以减轻CPU负担、更好的使用GPU。而且合并Primitive是由web worker线程执行,采取多线程极大的提升了前端渲染性能;​
  • 灵活性:允许开发者使用 Cesium API 直接操作图元的属性,如几何形状、材质、变换等,提供了更多的灵活性。​
  • 低级控制:适用于需要特定渲染效果或性能优化的场景,开发者可以直接管理图元的生命周期和渲染顺序。​

EntityCollection:适用于大多数应用场景,特别是动态变化、与数据源绑定的场合

# Primitive组成​

  • GeometryInstances:几何形状,例如点、线、面等;​
  • Appearance:外观,可以编写着色器语言渲染几何体的样式;​

Cesium 中自定义了多种 PointPrimitive用于渲染点的基本单元。还包括PointPrimitiveCollection、BillboardCollection、LabelCollection、PolylineCollection等 ​

const billboardCollection = new BillboardCollection();
const addBillboardCollection = () => {
  __viewer.scene.primitives.add(billboardCollection);
  billboardCollection.add({
    id: "point_02",
    position: Cartesian3.fromDegrees(-75.59777, 40.03883),
    image: "/images/garden-icon.png",
  });
  billboardCollection.add({
    id: "point_03",
    position: Cartesian3.fromDegrees(-97.59777, 40.03883),
    image: "/images/mark-icon.png",
  });
  console.log("billboardCollection", billboardCollection);
  console.log("primitives", __viewer.scene.primitives);
};

# 20万点位渲染优化​

const addLargePoint = () => {​
  pointPrimitiveCollection = new PointPrimitiveCollection();for (let i = 0; i < 200000; i++) {const opts = {position: Cartesian3.fromDegrees(116.39 + Math.random() * 50, 39.9 + Math.random() * 50),color: Color.GREEN,pixelSize: 10,}​
    pointPrimitiveCollection.add(opts)}​
  __viewer.scene.primitives.add(pointPrimitiveCollection)}

这里需要注意一个影响性能的点,千万不要在循环中执行__viewer.scene.primitives.add(pointPrimitiveCollection),因为这个操作会导致视图在循环过程中不断的渲染,正确的做法就是先把pointPrimitiveCollection处理好,最后一次性添加到视图中,这样视图也只会渲染一次即可。​

# 性能优化-合并单个Geometry​

let polylinePrimitive​
const addPolyline = () => {// 创建一个新的 PolylineGeometry 实例​
  const polyline = new PolylineGeometry({positions: Cartesian3.fromDegreesArray([-75.10, 39.57,-105.02, 38.53]),width: 5.0});​
​
  // 创建一个实例化几何的 Appearance​
  const instance = new GeometryInstance({geometry: polyline,attributes: {color: ColorGeometryInstanceAttribute.fromColor(Color.RED)}});​
​
  // 创建并添加 Primitive​
  polylinePrimitive = new Primitive({geometryInstances: instance,appearance: new PolylineMaterialAppearance({material: Material.fromType('Color', {color: Color.RED})})});​
​
  // 将 Primitive 添加到场景​
  __viewer.scene.primitives.add(polylinePrimitive);}
  • GeometryInstance:用于定义一个或多个几何实例,包括其形状、大小和位置。​
  • Appearance:用于定义几何体的外观属性,如材质和着色器。
  • Primitive:在场景中实例化并渲染几何体

当有多个个polylineGeometry的时候,合并Geometry为单个Primitive,减轻CPU负担、更好的使用GPU​

const intances = []​
intances.push( new GeometryInstance())​
// 把处理好的geometry实例合并成单个Primitive​
polylinePrimitive = new Primitive({geometryInstances: instances,appearance: //...​
});

primitive的删除最好不要直接执行viewer.scene.primitives.removeAll()去删除所有,不然大概率会受到一份大礼:​ ​

This object was destroyed, i.e., destroy() was called.​ ​ 这是因为当你执行primitives.removeAll()之后,场景中的图元对象就都被删除了,当你再次添加图元的时候是需要基于图元对象添加的,所以会报出:This object was destroyed​

# primitives.remove()指定删除对象

当primitives.add()添加的是一些上层对象,例如BillboardCollection,PolylineCollection的时候,可以利用primitives.remove()指定删除对象

let pointPrimitiveCollection;const addPointPrimitive = () => {​
  pointPrimitiveCollection = new PointPrimitiveCollection();​
  pointPrimitiveCollection.add(//...)​
  __viewer.scene.primitives.add(pointPrimitiveCollection)}​
​
const clear = () => {​
  __viewer.scene.primitives.remove(pointPrimitiveCollection);}
  • 再删除的时候指定删除某个collection,也就是clear方法里的操作;​
  • 每次重新新增primitive的时候,要重新实例化collection,因为你已经把之前的collection删除了;

# cesium中的GeometryInstance的attributes​

通过GeometryInstance去构建几何体,attributes 是一个对象,用于定义每个几何实例的属性。每个属性都是一个 GeometryInstanceAttribute,并且可以用于控制几何体的外观、位置、颜色等。​

# 常用属性​

color​:定义几何体的颜色。通常使用 ColorGeometryInstanceAttribute。

const colorAttribute = Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED);​
​
const instance = new Cesium.GeometryInstance({geometry: new Cesium.RectangleGeometry({rectangle: Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0)}),attributes: {color: colorAttribute​
  }});

show​:控制几何体的显示或隐藏。使用 ShowGeometryInstanceAttribute。

const showAttribute = new Cesium.ShowGeometryInstanceAttribute(true);​
​
const instance = new Cesium.GeometryInstance({geometry: new Cesium.CircleGeometry({center: Cesium.Cartesian3.fromDegrees(-75.0, 40.0),radius: 100000.0}),attributes: {show: showAttribute​
  }});

distanceDisplayCondition​:设置几何体在不同距离下的可见性范围。使用 DistanceDisplayConditionGeometryInstanceAttribute。

const distanceDisplayConditionAttribute = new Cesium.DistanceDisplayConditionGeometryInstanceAttribute(0.0, 500000.0);​
​
const instance = new Cesium.GeometryInstance({geometry: new Cesium.PolygonGeometry({polygonHierarchy: new Cesium.PolygonHierarchy(​
      Cesium.Cartesian3.fromDegreesArray([-109.0, 30.0,-95.0, 30.0,-95.0, 40.0,-109.0, 40.0]))}),attributes: {distanceDisplayCondition: distanceDisplayConditionAttribute​
  }});

offset​:定义几何体的高度偏移。使用 GeometryInstanceAttribute。

const offsetAttribute = new Cesium.GeometryInstanceAttribute({componentDatatype: Cesium.ComponentDatatype.FLOAT,componentsPerAttribute: 1,value: [10.0]  // Height offset​
});​
​
const instance = new Cesium.GeometryInstance({geometry: new Cesium.CylinderGeometry({length: 400000.0,topRadius: 200000.0,bottomRadius: 200000.0}),modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(​
    Cesium.Cartesian3.fromDegrees(-100.0, 40.0)),attributes: {offset: offsetAttribute​
  }});

# cesium 几何体外观材质类

Appearance 类用于定义几何体的外观和材质属性。它决定了几何体如何渲染,包括其材质、着色器(Material)和其他视觉特性。Appearance 通常与 Primitive 结合使用,以控制几何体在场景中的呈现方式。​

# cesium常见的 Appearance 类型​

  • MaterialAppearance​:用于将 Material 应用于几何体。这允许使用材质系统定义几何体的外观,包括颜色、纹理和透明度等。
  • PerInstanceColorAppearance​:每个几何实例有不同的颜色。这种外观常用于需要为每个几何实例设置不同颜色的情况,例如当你渲染了多个polygon,每个polygon都有自己独有的颜色,那么外观材质就比较适合使用PerInstanceColorAppearance。
  • PolylineMaterialAppearance​:专门用于多段线的外观。允许为多段线设置材质。
  • EllipsoidSurfaceAppearance​:专为椭球表面设计的外观。通常用于地球表面几何体。

参数

translucent​:Boolean​; #指定几何体是否是半透明的。默认值为 true。​
closed​:Boolean​; #指定几何体是否为闭合体。对于需要显示阴影或光照效果的几何体,通常设置为 true。​

MaterialAppearance 特有参数​

material​ 类型:Material #描述:指定几何体的材质,包括颜色、纹理等。​
faceForward​:Boolean #描述:指定是否正面渲染几何体,有控制光照的作用。默认值为 true。​
flat​:Boolean #描述:指定是否使用平面着色,不考虑光线的作用。默认值为 false。​
const { __viewer } = window


let circlePrimitive = null
const addMaterailAppearance = () => {
  // 创建一个圆实例
  const circleInstance = new GeometryInstance({
    geometry: new CircleGeometry({
      center: Cartesian3.fromDegrees(-100.0, 40.0),
      radius: 1000000.0
    })
  });

  // 创建一个材质
  const material = Material.fromType('Grid', {
    color: Color.GREEN,
    cellAlpha: 0.5
  });

  // 创建一个 MaterialAppearance 外观
  const materialAppearance = new MaterialAppearance({
    material: material,
    translucent: false
  });

  // 创建一个 Primitive 并将 MaterialAppearance 应用于其上
  circlePrimitive = new Primitive({
    geometryInstances: circleInstance,
    appearance: materialAppearance
  });

  // 将 Primitive 添加到场景
  __viewer.scene.primitives.add(circlePrimitive);
}

PolylineMaterialAppearance 的主要参数​

  • material​ 类型:Material​ 描述:用于多段线的材质,可以是颜色、纹理或其他复杂效果。​
  • translucent​ 类型:Boolean​ 描述:指定多段线是否为半透明。默认值为 true。​
  • vertexFormat​ 类型:VertexFormat​ 描述:指定顶点属性格式,通常不需要手动设置。​

Material 中常用的Polyline类型​

  • PolylineGlow​ 用于创建带有发光效果的多段线。​
    • 参数:glowPower(发光强度)、color(颜色)。​
  • PolylineOutline​ 用于创建带有轮廓线的多段线。​
    • 参数:color(主体颜色)、outlineColor(轮廓颜色)、outlineWidth(轮廓宽度)。​
  • PolylineArrow​ 创建带箭头的多段线​
  • PolylineDash​ 创建虚线样式的多线段​

# 应用场景​

PolylineMaterialAppearance 适用于以下场景:​

  • 高亮路线:用于表示导航路径或其他需要高亮显示的线条。​
  • 绘制电网、管道等:用来显示具有特定视觉效果的基础设施。​
  • 动态线条:需要通过材质实现复杂动态效果的多段线。​
let polylinePrimitive = null
const addPolylineMaterial = () => {

  // 定义多段线的几何体
  const polyline = new PolylineGeometry({
    positions: Cartesian3.fromDegreesArray([
      -75.10, 39.57,
      -77.02, 38.53,
      -80.50, 35.14,
      -93.01, 29.67
    ]),
    width: 50.0
  });

  // 创建几何体实例
  const geometryInstance = new GeometryInstance({
    geometry: polyline
  });

  // 创建材质
  const material = Material.fromType('PolylineGlow', {
    glowPower: 0.2,
    color: Color.ORANGE
  });

  // 创建 PolylineMaterialAppearance 外观
  const appearance = new PolylineMaterialAppearance({
    material: material
  });

  // 创建 Primitive 并将 PolylineMaterialAppearance 应用于其上
  polylinePrimitive = new Primitive({
    geometryInstances: geometryInstance,
    appearance: appearance,
    asynchronous: false
  });

  // 将 Primitive 添加到场景
  __viewer.scene.primitives.add(polylinePrimitive);

}

EllipsoidSurfaceAppearance:用于在球体(椭球体)表面应用着色。对 Cesium 的 Ellipsoid 几何体进行自定义外观处理,适用于地球表面、行星等场景的渲染。​

  1. 椭球体几何体创建:​EllipsoidGeometry 用于创建椭球面几何体,参数 radii 指定椭球体的半径(x、y、z)。
  2. 几何体实例:​GeometryInstance 用于包装几何体,modelMatrix 用于指定椭球体在场景中的位置。
  3. 使用 EllipsoidSurfaceAppearance:​EllipsoidSurfaceAppearance 的 material 属性定义了椭球体的材质,可以使用颜色、纹理等。
  4. 添加到场景:​创建 Primitive 对象,将几何体实例和 EllipsoidSurfaceAppearance 结合,添加到 Cesium 的场景中进行渲染。

应用场景​

  • 模拟天体表面:使用纹理或颜色模拟行星、卫星等天体表面。​
  • 地球表面着色:用于自定义地球表面的视觉效果,适用于需要覆盖整个地表的场景。​
  • 数据可视化:可以在椭球体上叠加不同的材质以展示不同的数据图层。​

# cesium中自定义封装材质类

Material提供了灵活的方式来定义材质的纹理、颜色、透明度等特性,并允许用户通过 GLSL 着色器语言自定义复杂的视觉效果。Material 可以应用于多种外观(Appearance)。通过Material.fromType(type)可以直接使用Cesium提供的一些现成的材质效果。​ ​

const addMaterail = () => {
  // 定义多段线的初始位置
  const positions = Cartesian3.fromDegreesArray([
    -109, 35,
    -79.02, 35,
  ]);

  // 创建自定义材质,用于动态流动效果
  Material.PolylineTrailType = 'PolylineTrail';
  Material.PolylineTrailSource = `
    uniform vec4 color;
    uniform float speed;
    czm_material czm_getMaterial(czm_materialInput materialInput)
    {
        czm_material material = czm_getDefaultMaterial(materialInput);
        vec2 st = materialInput.st;
        float t = czm_frameNumber * speed / 500.0;
        float alpha = sin((st.s + t) * 3.14159);
        material.diffuse = color.rgb;
        material.alpha = color.a * alpha;
        return material;
    }
    `;

  Material._materialCache.addMaterial(Material.PolylineTrailType, {
    fabric: {
      type: Material.PolylineTrailType,
      uniforms: {
        color: new Color(1.0, 1.0, 0.0, 1.0),
        speed: 1.0
      },
      source: Material.PolylineTrailSource
    }
  });

  // 创建 PolylineMaterialAppearance 外观
  const appearance = new PolylineMaterialAppearance({
    material: new Material({
      fabric: {
        type: Material.PolylineTrailType,
        uniforms: {
          color: Color.CYAN,
          speed: 10.0
        }
      }
    })
  });

  // 创建多段线几何体实例
  const polylineGeometryInstance = new GeometryInstance({
    geometry: new PolylineGeometry({
      positions: positions,
      width: 10.0
    })
  });

  // 创建 Primitive 并将 PolylineMaterialAppearance 应用于其上
  moveLinePrimitive = new Primitive({
    geometryInstances: polylineGeometryInstance,
    appearance: appearance,
    asynchronous: false
  });

  // 将 Primitive 添加到场景
  __viewer.scene.primitives.add(moveLinePrimitive);

}

# 代码说明​

  1. 定义多段线位置:​使用 Cesium.Cartesian3.fromDegreesArray 定义多段线的路径。​
  2. 自定义材质:​在 Cesium.Material 中添加新的材质类型 PolylineTrail。​
  3. source 中定义了 GLSL 着色器代码,用于实现流动效果。​
  4. czm_frameNumber 是 Cesium 提供的内建变量,用于获取当前的帧编号,可以用于动画效果。​
  5. 使用 PolylineMaterialAppearance:​使用 PolylineMaterialAppearance 结合自定义材质创建流动效果。​
  6. 创建几何体实例:​使用 Cesium.PolylineGeometry 创建多段线的几何体实例。​
  7. 创建和添加 Primitive:​创建 Primitive 对象,并将几何体实例和 PolylineMaterialAppearance 组合,然后将其添加到 Cesium 的场景中。​

其中,Material.fromType(type, uniforms)是new Material({fabric : {type : type, uniforms: {}}})的另一种写法​

# GLSL 代码分析​

  1. Uniforms(定义变量)​
float t = czm_frameNumber * speed / 1000.0;

czm_frameNumber 是 Cesium 提供的内置变量,表示当前帧的编号。​ t 计算基于帧编号和速度的时间变量,产生动画的时间轴。​ 速度被缩放(除以 1000.0)以控制其影响范围和动画流畅度。​ ​ 2. 动画效果​ ​

float alpha = sin((st.s + t) * 3.14159);

alpha 计算每个片段的透明度,使用 sin 函数实现动态的透明度变化。​

(st.s + t) 使得透明度在时间和空间上变化,产生流动效果。​

3.14159 近似 π,用于将 sin 函数的周期设定为适合的范围。​ ​ 3. 材质属性设置​ ​

material.diffuse = color.rgb;
material.diffuse 设置材质的漫反射颜色,即物体在受到光照时表现出的颜色。​

color.rgb 提供了基础的 RGB 颜色值,来自传入的 uniform。​

material.alpha = color.a * alpha;​

material.alpha 设置材质的透明度。​
color.a 是传入的基础透明度,alpha 是动态变化的透明度因子,两者相乘控制最终透明度。​
通过 alpha 的变化实现线条的动态流动效果。​
​
return material;​

返回计算后的 czm_material 对象,Cesium 使用它来渲染当前片段的外观。​

# cesium白模及自定义皮肤

<template>
  <OperateBox>
    <el-button type="primary" @click="addBuildings">加载白模</el-button>
    <el-button type="primary" @click="modifyColor">修改白模颜色</el-button>
    <el-button type="primary" @click="perColor">分层设色</el-button>
    <el-button type="primary" @click="smartBuild">泛光特效</el-button>
    <el-button type="primary" @click="clear">清空</el-button>
  </OperateBox>
</template>
<script setup>
import * as Cesium from "cesium"

let build3Dtileset = null
const addBuildings = async () => {
  const url = "/models/qd_buildings/tileset.json";
  const tilesetUrl = await Cesium.Cesium3DTileset.fromUrl(url);
  build3Dtileset = window.__viewer.scene.primitives.add(tilesetUrl);

  // window.__viewer.scene.globe.depthTestAgainstTerrain = true;
  // window.__viewer.extend(Cesium.viewerCesium3DTilesInspectorMixin);

  console.warn(build3Dtileset,window.__viewer)

  window.__viewer.flyTo(build3Dtileset);
}

const modifyColor = () => {
  if (build3Dtileset) {
    build3Dtileset.style = new Cesium.Cesium3DTileStyle({
      color: {
        conditions: [['true', "color('rgb(51, 153, 255)',1)"]]
      },
    });
  }
}

const perColor = () => {
  if (build3Dtileset) {
    build3Dtileset.style = new Cesium.Cesium3DTileStyle({
      color: {
        conditions: [
          ['${Elevation} >= 100', "color('rgb(255, 0, 0)',1)"],
          ['${Elevation} >= 50', "color('rgb(255, 255, 0)',1)"],
          ['true', "color('rgb(0, 0, 255)',1)"]
        ]
      }
    });
  }
}

const smartBuild = () => {
  if (!build3Dtileset) {
    return
  }
  build3Dtileset.style = new Cesium.Cesium3DTileStyle({
    color: {
      conditions: [['true', "color('rgb(51, 153, 255)',1)"]]
    },
  });
  var customShader = new Cesium.CustomShader({
    //不考虑光照模型
    lightingModel: Cesium.LightingModel.UNLIT,
    // fract 用于提取其参数的小数部分
    // czm_frameNumber 是一个内置的全局变量,它代表了当前渲染帧的编号。帧计数器,每次渲染时自增1,可用于实现动画效果。
    //这个变量是由CesiumJS的渲染引擎自动管理的,用于各种与帧相关的计算,比如动画、时间流逝效果等。
    fragmentShaderText: `
            void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
              float _baseHeight = 180.0; // 物体的基础高度,需要修改成一个合适的建筑基础高度
              float _heightRange = 100.0; // 高亮的范围
              float _glowRange = 400.0; // 光环的移动范围(高度)
              float vtxf_height = fsInput.attributes.positionMC.z-_baseHeight;
              float vtxf_a11 = fract(czm_frameNumber / 360.0) * 20.14159265 * 2.0;
              float vtxf_a12 = vtxf_height / _heightRange + sin(vtxf_a11) * 0.1;
              material.diffuse*= vec3(vtxf_a12, vtxf_a12, vtxf_a12);
              float vtxf_a13 = fract(czm_frameNumber / 360.0);
              float vtxf_h = clamp(vtxf_height / _glowRange, 0.0, 1.0);
              vtxf_a13 = abs(vtxf_a13 - 0.5) * 2.0;
              float vtxf_diff = step(0.31, abs(vtxf_h - vtxf_a13));
              material.diffuse += material.diffuse * (1.0 - vtxf_diff);
            }     
            `,
  });
  //将定义好的着色器作用域建筑tilesets
  build3Dtileset.customShader = customShader;
}

const clear = () => {
  if (build3Dtileset) {
    window.__viewer.scene.primitives.remove(build3Dtileset);
    build3Dtileset = null;
  }
}
</script>
<style lang='less' scoped></style>

# cesium 实体model

var entity = viewer.entities.add({  
    name: 'MyModelName', // 使用一个描述性的名称  
    position: position,  
    orientation: orientation,  
    model: {  
        uri: url, // 假设 url 变量包含模型的 URI  
        minimumPixelSize: 128,  
        maximumScale: 20000,  
        // 模型轮廓(或边缘)的颜色
        silhouetteColor: Cesium.Color.fromCssColorString(viewModel.silhouetteColor),  
        // 轮廓线的粗细 
        silhouetteSize: parseFloat(viewModel.silhouetteSize),  
    },  
});  

// 如果需要动态更改颜色,请考虑使用其他方法,如后期处理或修改模型文件

# cesium中与图形学相关的变量和方法

一、常用的变量

1、czm_model:模型变换矩阵,将对象从模型空间变换到世界空间。

2、czm_view:视图变换矩阵,将世界空间中的对象变换到摄像机空间。

3、czm_projection:投影变换矩阵,将摄像机空间中的对象进行透视或正交投影,得到剪辑空间坐标。

4、czm_modelView:模型视图变换矩阵,即 czm_view * czm_model。

5、czm_modelViewProjection:模型视图投影变换矩阵,即 czm_projection * czm_view * czm_model,将对象从模型空间变换到剪辑空间。

6、czm_inverseModel:模型变换矩阵的逆矩阵,用于将世界空间中的坐标变换到模型空间中。

7、czm_inverseView:视图变换矩阵的逆矩阵,用于将摄像机空间中的坐标变换到世界空间中。

8、czm_inverseProjection:投影变换矩阵的逆矩阵,用于将剪辑空间中的坐标变换到摄像机空间中。

9、czm_normal:变换后的法线向量,用于进行光照计算。

10、czm_ellipsoid:椭球体参数,用于进行地球表面坐标系和笛卡尔坐标系之间的转换。

11、czm_frameNumber:帧计数器,每次渲染时自增1,可用于实现动画效果。

12、czm_pixelRatio:设备像素比,用于实现高分辨率屏幕上的渲染。

二、常用的函数

1、czm_unpackDepth:它用于将从深度纹理中读取到的深度值进行解压缩,以便在着色器中进行深度测试和深度值比较等操作。深度纹理通常用于实现阴影效果、深度检测等功能。在Cesium中,深度值通常被存储在一个16位的纹理单元中,这个值被压缩成0到1之间的浮点数,以便节省显存空间。

2、czm_inverseTranspose:返回一个矩阵的逆转置矩阵,通常用于变换法线向量。

3、czm_saturation:返回一个颜色的饱和度,通常用于实现色彩调整。

4、czm_sceneMode:返回当前场景的模式,例如二维地图、三维场景等。

5、czm_ellipsoidContainsPoint:判断一个点是否在当前椭球体内,通常用于进行鼠标拾取等操作。

6、czm_eastNorthUpToFixedFrame:返回从东-北-天坐标系到固定坐标系的变换矩阵,通常用于实现坐标系转换。

7、czm_geodeticSurfaceNormal:返回一个点在椭球体表面上的法线向量,通常用于进行光照计算。

8、czm_transformColor:将一个颜色从一种颜色空间转换为另一种颜色空间,通常用于进行色彩调整。

9、czm_decompressTextureCoordinates:解压缩纹理坐标,通常用于将纹理坐标从一个压缩格式转换为普通的二维坐标。

10、czm_eyeOffset:返回一个向量,表示摄像机视点在局部坐标系中的偏移量。

11、czm_fractionAndIntervalToIndex:根据插值系数和区间范围计算索引,通常用于实现纹理合并。

# cesium 广告牌

  1. 广告牌集合
// 先把广告牌实例化,然后再添加到场景中
const billboardsCollection = viewer.scene.primitives.add(
  new Cesium.BillboardCollection()
);

billboardsCollection._id = `mark`;
// add的是Billboard,将一个个Billboard添加到集合当中
billboardsCollection.add({
  image: "/images/mark-icon.png",
  width: 32,
  height: 32,
  position,
});
  1. 单独一个广告牌
var billboard = viewer.entities.add({
    position:Cesium.Cartesian3.fromDegrees(120, 40, 0),
    billboard: {
      image: "./static/a.jpg",
      scale: 0.5,
      color: Cesium.Color.RED
    },
  });
 viewer.entities.add({
    position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
    billboard: {
      image: "../images/Cesium_Logo_overlay.png", // default: undefined
      show: true, // default 显示隐藏
      pixelOffset: new Cesium.Cartesian2(0, -50), // default: (0, 0) 图像相对于其锚点的像素偏移量。这里设置为(0, -50),意味着图像会向下偏移50像素。
      eyeOffset: new Cesium.Cartesian3(0.0, 0.0, 0.0), // default 相对于观察者的偏移量,以米为单位
      horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // default  控制billboard水平方向的锚点。这里设置为Cesium.HorizontalOrigin.CENTER,表示图像的中心将与其位置对齐。
      verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // default: CENTER 控制billboard垂直方向的锚点。
      scale: 2.0, // default: 1.0 放大背书
      color: Cesium.Color.LIME, // default: WHITE 颜色设置
      rotation: Cesium.Math.PI_OVER_FOUR, // default: 0.0 旋转角度
      alignedAxis: Cesium.Cartesian3.ZERO, // default  控制billboard对齐的轴
      width: 100, // default: undefined
      height: 25, // default: undefined
      sizeInMeters: true,// 启用以米为单位的大小
      scaleByDistance: new Cesium.NearFarScalar(1.5e2, 2.0, 1.5e7, 0.5), //scaleByDistance属性来根据距离调整billboard的大小。
      // 当距离小于或等于150米(1.5e2)时,billboard的缩放比例为2.0(即原始大小的2倍)。
      // 当距离大于或等于15,000,000米(1.5e7)时,billboard的缩放比例为0.5(即原始大小的一半)。

      // 控制透明度
       translucencyByDistance: new Cesium.NearFarScalar(  
            150.0, // 近点距离(米),当距离小于或等于这个值时,使用近点透明度  
            0.0,   // 近点透明度(0表示完全透明,1表示完全不透明)  
            15000000.0, // 远点距离(米),当距离大于或等于这个值时,使用远点透明度  
            0.5    // 远点透明度(半透明)  
        ),  
      // 类似精灵图
       imageSubRegion: new Cesium.BoundingRectangle(49, 43, 18, 18),
    },
  });

# cesium PrimitiveCollection

用于创建一个新的 PrimitiveCollection 实例。PrimitiveCollection 是 Cesium 中用于管理渲染原语(primitives)的容器。渲染原语是低层次的图形对象,如点、线、多边形等,它们用于在三维空间中渲染可视元素。

// 创建一个新的 PrimitiveCollection 实例  
var primitives = new Cesium.PrimitiveCollection();  
  
// 创建一个点原语  
var point = new Cesium.PointPrimitive({  
    position : Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883, 300000.0),  
    color : Cesium.Color.RED,  
    pixelSize : 10.0  
});  
  
// 将点原语添加到 PrimitiveCollection 中  
primitives.add(point);  
  
// 将整个 PrimitiveCollection 添加到 Cesium 场景的 primitives 集合中  
viewer.scene.primitives.add(primitives);

# cesium BoundingRectangle

由角、宽度和高度给出的边界矩形。

class BoundingRectangle {
    constructor(x?: number, y?: number, width?: number, height?: number);
}

# Cesium.NearFarScalar

一个用于定义基于距离变化的标量值(如透明度、缩放比例等)的类。它允许你指定两个距离点(近点和远点)以及在这些距离点上标量值的大小。然后,CesiumJS会根据实体与相机的距离,在这两个点之间线性插值来计算标量值。

# cesium 颜色控制

 Cesium.Color.LIME
Cesium.Color.RED.withAlpha(0.95)//可设置透明度
var anotherSemiTransparentRed = new Cesium.Color(1.0, 0.0, 0.0, 0.5);
// 假设 viewModel.silhouetteColor 是从某处获取的 CSS 颜色字符串,比如 "#FF0000"  
// 它允许从 CSS 颜色字符串(如 #RRGGBB、#RRGGBBAA、rgb()、rgba()、hsla() 等)中创建一个 Cesium.Color 实例
var silhouetteColor = Cesium.Color.fromCssColorString(viewModel.silhouetteColor); 

# cesium 影像​图层和地形

Cesium中的影像图层类主要用于在地球表面上加载和显示各种类型的影像数据。这些影像数据可以是地图、卫星图像或其他类型的地理空间数据。影像图层类ImageryLayer提供了将这些数据整合到三维地球中的功能,使用户能够以直观和动态的方式查看和分析地理信息。​

Cesium中的影像图层类及其功能和可加载的影像类型包括以下内容:

# cesium 影像图层类​

1.1 ImageryLayer​:ImageryLayer是Cesium中影像图层的基本类。它代表一个影像图层,可以叠加在地球表面,可以控制影像的透明度、亮度、对比度、伽马值(色调)等。​ ​ API接口,它接受2个参数​

  • 第一个参数:ImageryProvider​
  • 第二个参数:设置图层样式​

Viewer的options配置​:

注意

注意:新版本初始化时使用baseLayer加载图层​

1.2 添加地址

window.__viewer = new Viewer("cesiumContainer", {// 地形影像​
    terrain: Terrain.fromWorldTerrain(),// 注意:想用baseLayer,baseLayerPicker必须=false​
    baseLayerPicker: false,// 自定义加载影像图层,这里先用OSM影像距离,下边具体细说其他图层类​
    baseLayer: new ImageryLayer(new OpenStreetMapImageryProvider({url: "https://tile.openstreetmap.org/",})),});

1.3 动态添加图层​

利用__viewer.imageryLayers.add()方法动态添加图层,其中ImageryLayerCollection是__viewer.imageryLayers的实例​

const { imageryLayers } = window.__viewer;​
​
const tms = new ImageryLayer(new UrlTemplateImageryProvider({url:buildModuleUrl("Assets/Textures/NaturalEarthII") +"/{z}/{x}/{reverseY}.jpg",tilingScheme: new GeographicTilingScheme(),maximumLevel: 5,}));​
 imageryLayers.add(tms);
const addMapbox = () => {const mapbox = new ImageryLayer(new MapboxImageryProvider({mapId: "mapbox.mapbox-terrain-v2",accessToken: "自己的mapbox Token",}));​
    imageryLayers.add(mapbox);};
/**​
 * 添加arcgis图层​
 */const addArcgis = () => {const arcgis = ImageryLayer.fromProviderAsync(​
        ArcGisMapServerImageryProvider.fromBasemapType(​
            ArcGisBaseMapType.SATELLITE));​
    imageryLayers.add(arcgis);};

# cesium地形

为了模拟逼真的三维场景,不仅需要地图影像,还需要有地形,加载一些山地丘陵湖泊等元素,加载地形才能执行一些与高程相关的空间分析和计算。​

Cesium提供了TerrainProvider地形基类,但是一般不直接实例化这个类,Cesium基于它衍生出了其他可以实例化的地形大类​,不同于上边的影像图层类可以无限叠加,在一个三维场景中只能加载一个地形类​

  1. 加载方式​
  • Viewer的options配置​

在Viewer中的terrainProvider参数指定,默认值:new EllipsoidTerrainProvider()​

option配置

window.__viewer = new Viewer("cesiumContainer", {// 地形影像​
    terrainProvider: new EllipsoidTerrainProvider(),});

动态配置

const terrain = new EllipsoidTerrainProvider();​
window.__viewer.terrainProvider = terrain;

# 地形种类​

  • EllipsoidTerrainProvider​:Cesium初始化的默认地形,不支持水面,也没有法向量,所以即使开启光照,也是没有环境效果的。默认的地形全球高度都是0,对于局域网或者无法连接互联网来说是一个不错的选择。​

  • CesiumTerrainProvider 是 Cesium 中的基本地形提供者类,可以从 Cesium ion 平台直接加载,数据量比较小,支持水面和法向。​

const terrain = await CesiumTerrainProvider.fromIonAssetId(1);​
window.__viewer.terrainProvider = terrain;
  • GoogleEarthEnterpriseTerrainProvider​:用于从 Google Earth Enterprise (GEE) 服务器加载地形数据。GEE 服务器允许用户在私有云或内部网络中托管和访问 Google 地图和地球数据,有相关地形服务的可以尝试下。​
const geeMetadata = await GoogleEarthEnterpriseMetadata.fromUrl("xxx");const gee = Cesium.GoogleEarthEnterpriseTerrainProvider.fromMetadata(geeMetadata);

# cesium接入国产地图

npm install @cesium-china/cesium-map​
//高德
import { AMapImageryProvider, BaiduImageryProvider, TencentImageryProvider } from "@cesium-china/cesium-map";const addAMap = () => {const options = {style: "dark", // style: img、elec、cva、dark​
        crs: "WGS84", // 使用84坐标系,默认为:GCJ02​
    };​
    __viewer.imageryLayers.add(new ImageryLayer(new AMapImageryProvider(options)));};
// 腾讯
const addTencent = () => {const options = {style: 1, //style: img、1:经典​
        crs: "WGS84", // 使用84坐标系,默认为:GCJ02,​
    };​
    __viewer.imageryLayers.add(new ImageryLayer(new TencentImageryProvider(options)));};
// Baidu
const addBaidu = () => {const options = {style: "dark", // style: img、vec、normal、dark​
        crs: "WGS84", // 使用84坐标系,默认为:GCJ02​
    };​
    __viewer.imageryLayers.add(new ImageryLayer(new BaiduImageryProvider(options)));};

# cesium 获取数据

 Cesium.Resource.fetchImage("../images/Cesium_Logo_overlay.png") //promise
// 创建一个新的实体并将其添加到Cesium Viewer的实体集合中  
const entity = viewer.entities.add({  
    // 设置实体的名称  
    // 这里使用url作为名称可能不是最佳选择,因为名称通常用于识别,而URL可能很长且不易读  
    // 建议使用更具描述性的名称  
    name: url,  
      
    // 设置实体的位置  
    position: position,  
      
    // 设置实体的方向  
    orientation: orientation,  
      
    // 设置实体的模型属性  
    model: {  
        // uri是模型的URI(统一资源标识符),即模型文件的网络地址或本地路径  
        // 这里再次使用了url作为模型URI,如果url确实指向一个有效的3D模型文件,则这是可以的  
        // 但如果name和model.uri使用了相同的url,则建议为它们分配不同的变量名以增加代码的可读性  
        uri: url,  
          
        // minimumPixelSize定义了模型在屏幕上的最小像素尺寸  
        // 这有助于防止在远离时模型变得太小而无法看到  
        // 此值设置为128是一个常见的起点,但可以根据需要进行调整  
        minimumPixelSize: 128,  
          
        // maximumScale定义了模型可以缩放到的最大比例  
        // 这有助于防止在非常接近时模型变得过大  
        // 20000可能是一个相对较大的值,具体取决于模型的原始尺寸和所需的缩放效果  
        maximumScale: 20000,  
    },  
});  
  
// 将viewer的trackedEntity属性设置为刚刚创建的实体  
// 这将使得当视图(相机)移动时,该实体将被自动跟踪并保持在视图的中心  
// 注意,如果实体在地球上移动(例如,通过动态更新其位置),可能需要额外的逻辑来确保相机能够持续跟踪  
viewer.trackedEntity = entity;

# cesium矢量数据

矢量数据(Vector Data)是用X、Y、Z坐标表示地图图形或地理实体位置的数据,一般是通过记录坐标的方式来将地理实体的空间位置表现的准确无误。​ ​ 矢量数据类型非常丰富,用于表示点、线、面和其他几何形状。它们可以用于绘制标记、路线、区域等。

# GeoJSON​

一种基于 JSON 格式的矢量数据格式,支持点、线、多边形等几何类型,并包含属性信息,Cesium中GeoJsonDataSource支持直接加载;​

数据示例

 {"type": "FeatureCollection","features": [{ "type": "Feature","geometry": {"type": "Point", "coordinates": [-66.0, 139]},"properties": {"name": "xxx"}},{ "type": "Feature","geometry": {"type": "LineString","coordinates": [[33, 0.0], [33.0, 1.0], [104.0, 0.0], [105.0, 1.0]]},"properties": {"name": "xxx"}},{ "type": "Feature","geometry": {"type": "Polygon","coordinates": [[ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],[100.0, 1.0], [100.0, 0.0] ]]},"properties": {"name": "xxx"}}]}
  • FeatureCollection:表示特征集合;​
  • Feature:表示特征;
  • geometry:表示几何体,它有一些子属性:​
    • type:表示几何体的类型,包含:Point、MultiPoint、LineString、MultiLineString、Polygon、MultiPolygon、GeometryCollection、Feature、FeatureCollection
    • coordinates:描述生成几何体所需要的坐标点位;​
    • properties:几何体一些其它的属性,例如ID,NAME等;
GeoJsonDataSource.load("/json/chuzhong.geojson").then((dataSource) => {​
    window.__viewer.dataSources.add(dataSource);​
    window.__viewer.zoomTo(dataSource);});

GeoJsonDataSource 大类可以直接加载和显示 GeoJSON 格式的矢量数据,值得一提的是它还能处理Topojson格式的数据;​

GeoJsonDataSource.load("/json/chuzhong.geojson").then((dataSource) => {// 利用__viewer下的dataSources(是DataSourceCollection的实例)集合去添加解析到的dataSource
    window.__viewer.dataSources.add(dataSource);​
​   // 根据需求做样式调整
    dataSource.entities.values.forEach((entity) => {​
        entity.billboard = {image: "/images/garden-icon.png",width: 30,height: 30,};});​
​
    window.__viewer.zoomTo(dataSource);});

# KML(Keyhole Markup Language)​

一种基于 XML 的格式,用于表示地理数据,广泛用于 Google Earth 等应用,支持复杂的样式和注释。​

KML文件有两个文件扩展名:.KML 和.KMZ(一个或几个 KML 文件的压缩集,采用 zip 格式压缩),Cesium中支持KmlDataSource直接加载;

<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2"><description>This is a simple point</description><Point><coordinates>-75.59777,40.03883,0</coordinates></Point></Placemark>​
    ​
    <!-- Line --><Placemark><name>Simple Line</name><description>This is a simple line</description><LineString><coordinates>​
          -75.10,39.57,0​
          -80.12,25.46,0​
        </coordinates></LineString></Placemark>​
    ​
    <!-- Polygon --><Placemark><name>Simple Polygon</name><description>This is a simple polygon</description><Polygon><outerBoundaryIs><LinearRing><coordinates>​
              -109.0,30.0,0​
              -95.0,30.0,0​
              -95.0,40.0,0​
              -109.0,40.0,0​
              -109.0,30.0,0​
            </coordinates></LinearRing></outerBoundaryIs></Polygon></Placemark>​
    ​
  </Document></kml>
  • Document:KML 文件的根元素,用于包含多个地理要素。​
  • Placemark:表示一个地理要素,可以是点、线或多边形。
  • Point:定义一个点,使用 coordinates 元素指定其经度、纬度和高度。​
  • LineString:定义一条线,使用 coordinates 元素指定其路径。
  • Polygon:定义一个多边形,使用 outerBoundaryIs 和 LinearRing 元素指定其边界。​
const addKML = () => {​
    __viewer.camera.flyHome(0);​
    __viewer.dataSources.add(​
        KmlDataSource.load("/kml/facilities/facilities.kml", {camera: __viewer.scene.camera,canvas: __viewer.scene.canvas,}));};

CZML​:CZML 是 Cesium 的专用格式,也是json格式类型,但支持复杂的时间动态数据,可以通过它去渲染与时间有关的动画等。Cesium支持CzmlDataSource类去直接加载;​

[{"id": "document","name": "Dynamic CZML Example","version": "1.0"},{"id": "dynamicPath","name": "Dynamic Path","availability": "2024-07-01T00:00:00Z/2024-07-02T00:00:00Z","path": {"material": {"solidColor": {"color": {"rgba": [255, 0, 0, 255]}}},"width": 5,"leadTime": 0,"trailTime": 60,"resolution": 5},"position": {"epoch": "2024-07-01T00:00:00Z","cartographicDegrees": [0, -75.10, 39.57, 0,3600, -75.12, 39.58, 0,7200, -75.14, 39.59, 0,10800, -75.16, 39.60, 0,14400, -75.18, 39.61, 0]}}]

# cesium中使用shp

SHP(Shapefile)是一种由ESRI(Environmental Systems Research Institute)开发的地理信息系统(GIS)矢量数据格式,主要用于存储地理空间数据。SHP文件格式广泛应用于各种GIS软件和应用程序中,用来表示和分析地理数据。Shapefile格式由一组文件组成,其中每个文件负责存储不同的信息。通常一个Shapefile包含以下几个主要文件。

.shp 文件:这是主文件,包含几何数据,例如点、线和多边形。它存储了空间实体的形状和位置。​ ​ .shx 文件:这是索引文件,包含几何数据的索引信息,以便快速访问和查找。它帮助软件快速定位和读取主文件中的几何数据。

.dbf 文件:这是属性文件,使用dBASE格式存储每个空间实体的属性数据。例如,如果SHP文件表示一个国家边界,每个国家的名称、人口等信息会存储在这个文件中。​

# cesium中加载3D模型

# glTF模型​

glTF(GL Transmission Format)是由Khronos Group开发的一种开放标准文件格式,用于高效传输和加载3D模型。它被广泛应用于包括Cesium在内的各种3D应用和平台。glTF旨在提供一种简洁且高效的方式来传输和展示3D模型。​glTF作为一种高效、轻量的3D模型传输格式,具有快速加载、高质量表现和广泛兼容性的优势,非常适合在Cesium中使用。​

glTF 格式有两种:​

*.glb:基于二进制的二进制文件,通常用于传输和加载,不支持编辑;

*.gltf: 基于JSON的文本文件,可使用文本编辑器轻松编辑,通常会引用外部文件,例如纹理贴图、二进制网格数据等;

如果使用VS Code编辑器,建议安装 glTF Tools 扩展工具,能够非常方便的查看glTF的数据结构、glTF和glb互转等。​

  • scene: glTF格式的场景结构描述条目。它通过引用node来定义场景图;​
  • node: 场景图层次中的一个节点。它可以包含一个变换(比如旋转或平移),并且可以引用其他(子)节点 。此外,它可以引用网格和相机,以及描述网格变换的蒙皮;
  • camera: 定义了用于渲染场景的视锥体配置;​
  • mesh: 描述了出现在场景中几何对象实际的几何数据。它是指accessor用于访问实际几何数据material的对象,并且是指在渲染对象时定义其外观的 ;
  • skin: 定义了用于蒙皮的参数,参数的值通过一个accessor对象获得。​
  • animation: 描述了一些结点如何随时间进行变换的动画(比如旋转或平移);
  • accessor: 一个访问任意数据的抽象数据源。被mesh、skin和animation元素使用来提供几何数据、蒙皮参数和基于时间的动画值。它通过引用一个bufferView对象,来引用实际的二进制数据;​
  • material: 包含了定义3D对象外观的参数。它通常引用了用于3D对象渲染的texture对象;
  • texture: 定义了一个sampler对象和一个image对象。sampler对象定义了image对象在3D对象上的张贴方式。
const addGltf = () => {
  const url = "/models/31.gltf";
  const entity = __viewer.entities.add({
    name: url,
    position: position,
    orientation: orientation, // 获取或设置相对于地球固定地球中心 (ECEF) 的方向。默认为实体位置的东北偏东。
    model: {
      uri: url,
      minimumPixelSize: 128,
      maximumScale: 20000,
    },
  });
};

# 3D Tiles​

Cesium中的3D Tiles是一种用于高效传输和渲染大规模3D地理数据的开源标准。它主要用于在Web浏览器中展示包含数百万或数十亿几何体的复杂三维场景,如城市模型、建筑物、点云数据、地形数据等。3D Tiles的设计目标是通过空间分割和层级细化技术,实现对大规模数据的流畅浏览和交互。​

3D Tiles的主要概念和组件:

  1. Tileset:​一个Tileset是由许多瓦片(Tile)组成的树状结构,用于管理和组织3D Tiles数据。每个Tileset的根节点定义了整个数据集的边界和元数据。
  2. Tile:​每个Tile包含了一部分3D数据,并且可以引用其子Tile。Tile可以包含几种不同类型的数据,如Batched 3D Model(b3dm)、Instanced 3D Model(i3dm)、点云(pnts)、地形(terrain)、矢量数据(vector data)等。
  3. LOD(Level of Detail):​通过层级细化(Level of Detail, LOD)技术,不同层级的Tile包含不同分辨率的数据。视图靠近时会加载更高分辨率的Tile,视图远离时则加载低分辨率的Tile,以减少数据传输量和渲染负担。
const add3DTiles = async () => {
  const url = "/models/changsha/tileset.json";
  const tilesetUrl = await Cesium3DTileset.fromUrl(url); // Cesium3DTileset from cesium
  tileset = __viewer.scene.primitives.add(tilesetUrl);
  __viewer.flyTo(tileset);
};

# CZML矢量数据​

CZML(Cesium Language)也是一种基于JSON的格式,用于描述时间动态场景中的3D数据。CZML可以表示不同类型的几何图形、模型、路径、标签和其他图形元素,特别适用于需要时间动态更新的场景,例如卫星轨道、飞机航迹等。​

[{"clock": {"interval": "2024-07-29T00:00:00Z/2024-07-29T24:00:00Z","currentTime": "2024-07-29T00:00:00Z","multiplier": 60}},{"id": "path","availability": "2024-07-29T00:00:00Z/2024-07-29T24:00:00Z","path": {"material": {"polylineOutline": {"color": {"rgba": [255, 0, 0, 255]},"outlineColor": {"rgba": [0, 0, 0, 255]},"outlineWidth": 2}},"width": 5,"leadTime": 0,"trailTime": 60,"resolution": 5},"position": {"epoch": "2024-07-29T00:00:00Z","cartographicDegrees": [0, 0, 0, 1000000,3600, 10, 10, 1000000,7200, 20, 20, 1000000,10800, 30, 30, 1000000]}}]

这段JSON代码定义了一个CZML(Cesium Language)文档,用于在Cesium中显示一个时间动态的路径​

  • id: "document"​ ,标识符,表示这是一个文档级别的对象。

  • version: "1.0"​ ,CZML版本,当前为1.0。

  • clock: ​

    • interval: "2024-07-29T00:00:00Z/2024-07-29T24:00:00Z" 表示时间范围,从2024年7月29日的00:00:00到24:00:00。​
    • currentTime: "2024-07-29T00:00:00Z"​ 初始时间设置为2024年7月29日的00:00:00。​
    • multiplier: 60​ 时间的流速,时间会以真实时间的60倍速流逝。
  • id: "path"​ ,标识符,表示这是一个路径对象。

  • availability: "2024-07-29T00:00:00Z/2024-07-29T24:00:00Z"​ ,路径的有效时间范围,从2024年7月29日的00:00:00到24:00:00。

  • 【path对象】​

    • material:​
      1. polylineOutline:
        1. color:​rgba: [255, 0, 0, 255] 路径的颜色,红色,不透明(RGBA: 红色,绿色,蓝色,透明度)。​
        2. outlineColor:​ rgba: [0, 0, 0, 255] 路径轮廓的颜色,黑色,不透明。​
        3. outlineWidth: 2​路径轮廓的宽度为2个像素。
      2. width: 5​ 路径的宽度为5个像素。
      3. leadTime: 0​ 路径前导时间,为0秒。
      4. trailTime: 60​ 路径拖尾时间,为60秒。
      5. resolution: 5​ 路径的分辨率,为5秒。
  • 【position对象】​

    • epoch: "2024-07-29T00:00:00Z"​ 位置数据的基准时间为2024年7月29日的00:00:00。
    • cartographicDegrees:​位置数据,以经度、纬度、高度的顺序排列,单位为度和米。时间间隔(以秒为单位)与位置数据配对。
      1. 数据解释:​ 第1组:时间0秒,位置为(0°经度, 0°纬度, 1000000米高度)。
      2. 第2组:时间3600秒(1小时),位置为(10°经度, 10°纬度, 1000000米高度)。​
      3. 第3组:时间7200秒(2小时),位置为(20°经度, 20°纬度, 1000000米高度)。
      4. 第4组:时间10800秒(3小时),位置为(30°经度, 30°纬度, 1000000米高度)。​

这段CZML定义了一个文档,其中包括一个在2024年7月29日全天有效的路径。该路径以红色显示,带有黑色轮廓,并且会根据时间动态更新位置。路径的宽度为5像素,拖尾时间为60秒,分辨率为5秒。路径的起点在0°经度、0°纬度,高度为1000000米,并且每小时更新一次位置,最终到达30°经度、30°纬度。​

注意:czml加载的模型格式通常是glb格式​

const addCZML = () => {
  const czml = [
    {
      id: "document",
      name: "CZML Model",
      version: "1.0",
    },
    {
      id: "aircraft model",
      name: "Cesium Air",
      position: {
        cartographicDegrees: [-77, 37, 10000],
      },
      model: {
        gltf: "/models/Cesium_Air.glb",
        scale: 2.0,
        minimumPixelSize: 128,
      },
    },
  ];
    /**
   * __viewer.clock 是与视图器(viewer)相关联的一个时钟对象,它负责控制时间流的模拟。
   * Cesium是一个用于创建3D地球、2D地图和地球仪的开源JavaScript库,
   * 它支持多种时间动态的数据可视化,比如卫星轨道、飞行路径、气象数据等。
   */
  __viewer.clock.shouldAnimate = true;
  __viewer.dataSources
    .add(CzmlDataSource.load(czml))// CzmlDataSource from cesium
    .then((dataSource) => {
      // 获取或设置相机当前正在跟踪的 Entity 实例。
      __viewer.trackedEntity = dataSource.entities.getById("aircraft model");
    })
    .catch((err) => {
      console.log(err);
    });
};

# czml实际应用

[{"minimumPixelSize":100, // 模型最小值​
      "maximumScale":50 // 最大缩放值​
    },"orientation" : { // 定义对象的旋转状态​
      "velocityReference": "#position" // 用于指定对象的朝向是基于其速度向量(velocity vector)的。这里的 "#position" 表示这个 CZML 对象的速度是根据其 position 属性的变化来计算的。​
    },"viewFrom": { // 定义观察者相对于对象的位置。通常用于设置默认视角位置​
      "cartesian": [ -2080, -1715, 779 ]},"properties" : { // 定义对象的自定义属性。这些属性可以是动态的,可以随时间变化,并且可以包含复杂的时间序列数据。​
        "fuel_remaining" : { // 自定义汽车的油量属性,表示某一时刻的剩余燃料量​
            "epoch":"2012-08-04T16:00:00Z", // 时间基准点,表示数据开始的时间。这是所有后续时间点的参考。​
            "number": [ // 一个数组,定义了时间序列数据。​
                0, 22.5, // 表示在 epoch 时刻(2012-08-04T16:00:00Z),也就是0秒刚开始的时候,燃料为 22.5 单位​
                1500, 21.2 // 表示在 epoch 之后的 1500 秒,燃料降至 21.2 单位​
            ]}},"path":{ // 模型移动轨迹​
      "material":{"solidColor":{"color":{"interval":"2012-08-04T16:00:00Z/2012-08-04T16:25:00Z","rgba":[255,255,0,255]}}},"width":[ // 轨迹线的宽度​
        {"interval":"2012-08-04T16:00:00Z/2012-08-04T16:25:00Z","number":5.0}],"show":[{"interval":"2012-08-04T16:00:00Z/2012-08-04T16:25:00Z","boolean":true}]},"position":{ // 运动轨迹的坐标位置​
      "interpolationAlgorithm":"LAGRANGE", // 指定用于插值位置数据的算法​
      "interpolationDegree":1, // 指定插值算法的多项式阶数,决定了用于插值的多项式的复杂程度​
      "epoch":"2012-08-04T16:00:00Z", // 时间基准点​
      "cartesian":[ // 坐标数据,每排第一个数是时间,以秒为单位,后边3个数是坐标xyz​
        0.0,-2379754.6637012,-4665332.88013588,3628133.68924173,10.0,-2379510.08905552,-4665419.64840452,3628182.20006795,20.0,-2379568.4769522,-4665555.3441867,3627970.83323261,30.0,-2379638.93786855,-4665691.63561896,3627750.82085873,40.0,-2379709.29834665,-4665827.9679646,3627530.80187124,50.0,-2379837.28064915,-4665847.7494347,3627422.12874017,60.0,-2379624.98289073,-4665907.50853722,3627484.1191848,70.0,-2379386.12743523,-4666029.54174431,3627483.83297459,80.0,-2379147.26777171,-4666151.56669944,3627483.5403492,90.0,-2378908.40390057,-4666273.58340244,3627483.24130864,1e2,-2378663.69301645,-4666379.62408751,3627507.14485116,110.0,-2378416.29648478,-4666444.93145547,3627584.82610021,120.0,-2378184.75115833,-4666458.05260387,3627718.84628509,]
<template>
  <OperateBox>
    <el-button type="primary" @click="onStart">开始移动</el-button>
    <el-button type="primary" @click="onClock">实时获取汽车油量</el-button>
    <el-button type="primary" @click="onStop">暂停动画</el-button>
    <el-button type="primary" @click="onResume">恢复动画</el-button>
  </OperateBox>
  <OperateBox v-show="isShowText" style="top: 40px">
    <span style="color: #009900; font-size: 20px">汽车油量剩余:{{ fuel }}</span>
  </OperateBox>
</template>
<script setup>
import { onUnmounted, ref } from "vue";
import { CzmlDataSource } from "cesium";

const { __viewer } = window;
let carEntity;
const fuel = ref(22.5);
const isShowText = ref(false);
const onStart = () => {
  __viewer.clock.shouldAnimate = true;
  __viewer.dataSources
    .add(CzmlDataSource.load("/czml/MultipartVehicle_part1.czml"))
    .then((dataSource) => {
      // 获取或设置相机当前正在跟踪的 Entity 实例。
      carEntity = __viewer.trackedEntity = dataSource.entities.getById("Vehicle");
    })
    .catch((err) => {
      console.log(err);
    });
};

const onClock = () => {
  isShowText.value = true;
  __viewer.clock.onTick.addEventListener((clock) => {
    if (carEntity) {
      const _fuel = carEntity.properties.fuel_remaining.getValue(clock.currentTime);
      fuel.value = _fuel.toFixed(1);
    }
  });
};

const onStop = () => {
  __viewer.clock.shouldAnimate = false;
};

const onResume = () => {
  __viewer.clock.shouldAnimate = true;
};

onUnmounted(() => {
  __viewer.dataSources.removeAll();
  __viewer.clock.shouldAnimate = false;
});
</script>
<style lang="less" scoped></style>

# KML​

KML(Keyhole Markup Language)是一种用于描述地理数据的XML格式,最初由Keyhole公司开发,并在2008年被Google标准化为OGC(开放地理空间联盟)标准。​

通过Cesium.KmlDataSource加载全球的所有国家的三维区域模型

const addKML = () => {
  __viewer.camera.flyHome(0);
  __viewer.dataSources.add(
    KmlDataSource.load("/kml/gdpPerCapita2008.kmz", {
      camera: __viewer.scene.camera,
      canvas: __viewer.scene.canvas,
    })
  );
};

# cesium clock

Clock 是一个用于模拟和控制时间流动的对象。它为场景中的时间管理提供了统一的接口,并用于协调动态对象(如卫星、飞机等)在虚拟时间轴上的行为。Clock 对象可以模拟不同的时间速率和方向,以及启动和暂停时间流动。​

Clock 的作用​ ​

  1. 时间模拟:Clock 提供了一种机制来模拟现实时间的流逝,可以设置时间的速率、方向和起始时间。​
  2. 协调场景内的动态实体:许多动态实体(例如,CZML 描述的对象)依赖 Clock 来更新它们的位置和状态。Clock 允许开发者同步这些对象在场景中的变化。​
  3. 控制动画:通过 Clock,可以实现暂停、快进、慢放等效果,使得用户可以更好地观察动态过程。​

Clock 的主要属性​ ​

  1. startTime:模拟时间的起始点。表示场景模拟的开始时间。​
  2. stopTime:模拟时间的结束点。通常用于限制时间范围。​
  3. currentTime:场景当前的模拟时间。它会随着时间推进而改变。​
  4. multiplier:控制时间的流速。例如,multiplier 为 2 时,时间流逝速度是实际时间的两倍。​
  5. clockStep:定义 Clock 如何推进时间,包括 TICK_DEPENDENT、SYSTEM_CLOCK 和 SYSTEM_CLOCK_MULTIPLIER 三种方式。​
  • TICK_DEPENDENT: 每次渲染帧时时间推进一个固定的步长。​
  • SYSTEM_CLOCK: 使用系统时钟。​
  • SYSTEM_CLOCK_MULTIPLIER: 使用系统时钟并乘以 multiplier。​
  1. clockRange:定义 Clock 如何处理时间范围的结束,包括 UNBOUNDED、CLAMPED 和 LOOP_STOP。​
  • UNBOUNDED: 时间无界,继续推进。​
  • CLAMPED: 到达 stopTime 后停止。​
  • LOOP_STOP: 到达 stopTime 后回到 startTime。​

主要方法​: 推进时间,通常在每一帧中调用,更新 currentTime。​

应用场景​ ​

  1. 动态对象模拟:例如,模拟卫星在轨道上运行的过程,通过调整时间流速可以快速查看卫星在不同时间点的位置。​
  2. 历史数据回放:使用 Clock 可以重现和分析历史事件的数据,观察事件如何随时间变化。​
  3. 实时监控:通过同步 Clock 和实际时间,可以实现对实时数据的可视化,如跟踪飞行中的航班。​
  4. 教学和演示:在教育场景中,通过调整时间速率,可以更好地展示和解释复杂的时间相关过程。​
// 直接通过viewer.clock获取Clock类的实例,然后通过onTick事件监听时间推进​
carEntity = __viewer.trackedEntity = dataSource.entities.getById("Vehicle");​
__viewer.clock.onTick.addEventListener((clock) => {const _fuel = carEntity.properties.fuel_remaining.getValue(clock.currentTime);​
    fuel.value = _fuel.toFixed(1);});

# Cesium.knockout

绑定页面上的元素后,监控数据的变化更新视图

//将viewModel成员转换为Knockout可观察对象(Observables)
Cesium.knockout.track(viewModel);
//页面元素toolbar
const toolbar = document.getElementById("toolbar");
Cesium.knockout.applyBindings(viewModel, toolbar);
Cesium.knockout
  .getObservable(viewModel, "color")
  .subscribe(function (newValue) {
    entity.model.color = getColor(newValue, viewModel.alpha);
  });

# cesium 粒子系统

粒子系统是一种图形技术,用于模拟复杂的基于物理的效果。粒子系统是小图像的集合,当它们密集的密布在一个空间里时聚集在一起看的时候,会形成一个更复杂的“模糊”对象,例如火焰、烟雾、雨雪雾或烟花。通过设定初始位置、速度和生命周期等属性指定单个粒子的行为来实现这些复杂的效果。可以说粒子系统控制着每个粒子对象随着时间的显示和变化​ ​ Cesium中提供了ParticleSystem类 ,用来支持构建常见的粒子效果。

<template>
  <OperateBox>
    <el-button type="primary" @click="baseParticle">基础粒子</el-button>
    <el-button type="primary" @click="boxEmitter">盒子发射器</el-button>
    <el-button type="primary" @click="coneEmitter">锥体发射器</el-button>
    <el-button type="primary" @click="sphereEmitter">球体发射器</el-button>
    <el-button type="primary" @click="onBursts">粒子爆裂</el-button>
    <el-button type="primary" @click="updateCallback">粒子更新</el-button>
    <el-button type="primary" @click="openWater">喷水效果</el-button>
    <el-button type="primary" @click="clear">清空</el-button>
  </OperateBox>
  <OperateBox style="top: 50px;">
    <el-button type="primary" @click="openWater">喷水效果</el-button>
  </OperateBox>
  <OperateBox style="top: 100px;">
    <div id="toolbar">
      <table>
        <tbody>
          <tr>
            <td>Gravity</td>
            <td>
              <input type="range" min="-20.0" max="20.0" step="1" data-bind="value: gravity, valueUpdate: 'input'">
              <input type="text" size="5" data-bind="value: gravity">
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </OperateBox>

</template>
<script setup>
import { onMounted, ref } from 'vue'
import * as Cesium from "cesium"

const { __viewer } = window
__viewer.clock.shouldAnimate = true;

let smokeParticle = null
let adjustedModelMatrix = null
let carEntity = null
const baseParticle = () => {
  carEntity = __viewer.entities.add({
    model: {
      uri: "/czml/CesiumMilkTruck.glb",
      minimumPixelSize: 64,
    },
    position: Cesium.Cartesian3.fromDegrees(-94.6, 41.8),
  });
  __viewer.trackedEntity = carEntity;
  // 计算汽车当前时间的模型矩阵
  let modelMatrix = carEntity.computeModelMatrix(
    __viewer.clock.startTime,
    new Cesium.Matrix4()
  );

  // 定义平移向量
  let translation = new Cesium.Cartesian3(-2.5, 0.0, 0.0); // 

  // 对模型矩阵进行平移
  adjustedModelMatrix = Cesium.Matrix4.multiplyByTranslation(
    modelMatrix,
    translation,
    new Cesium.Matrix4()
  );
  smokeParticle = __viewer.scene.primitives.add(
    new Cesium.ParticleSystem({
      image: "/images/smoke.png",
      imageSize: new Cesium.Cartesian2(20, 20),
      startScale: 1.0,
      endScale: 4.0,
      particleLife: 1.0,
      speed: 5.0,
      emitter: new Cesium.CircleEmitter(0.5),
      emissionRate: 5.0,
      modelMatrix: adjustedModelMatrix,
      lifetime: 16.0
    })
  );
}
// 不同的发射器
const boxEmitter = () => {
  carEntity.show = false
  smokeParticle.emissionRate = 20
  smokeParticle.emitter = new Cesium.BoxEmitter(new Cesium.Cartesian3(0.5, 0.5, 0.5))
}
const coneEmitter = () => {
  carEntity.show = false
  smokeParticle.emissionRate = 20
  smokeParticle.emitter = new Cesium.ConeEmitter(Cesium.Math.toRadians(30.0)) // 30度转弧度渲染
}
const sphereEmitter = () => {
  carEntity.show = false
  smokeParticle.emissionRate = 20
  smokeParticle.emitter = new Cesium.SphereEmitter(5.0) // 半径5
}
// ParticleSystem里有个bursts属性,以在指定时间发射粒子突发,模拟粒子爆炸。​
const onBursts = () => {
  carEntity.show = false
  smokeParticle.emissionRate = 0
  smokeParticle.bursts = [
    new Cesium.ParticleBurst({
      time: 5.0, // 粒子系统生命周期开始后发生爆发的时间(以秒为单位)。
      minimum: 300, // 最小粒子数
      maximum: 500, // 最大粒子数
    }), new Cesium.ParticleBurst({ time: 10.0, minimum: 50, maximum: 100 }),
    new Cesium.ParticleBurst({ time: 15.0, minimum: 200, maximum: 300 }),
  ];
}

const viewModel = {
  gravity: 0.0,
};

onMounted(() => {
  Cesium.knockout.track(viewModel);
  Cesium.knockout.applyBindings(viewModel, document.getElementById("toolbar"));
})
const gravityScratch = new Cesium.Cartesian3();
const updateCallback = () => {
  carEntity.show = true
  smokeParticle.updateCallback = (particle, dt) => {
    const position = particle.position;
    // 归一化处理
    Cesium.Cartesian3.normalize(position, gravityScratch);
    // 计算重力影响:根据 viewModel.gravity 和时间增量 dt,计算重力向粒子速度的影响,并存储在 gravityScratch 中
    Cesium.Cartesian3.multiplyByScalar(
      gravityScratch,
      viewModel.gravity * dt,
      gravityScratch
    );
    // 更新粒子速度,将计算后的重力向量加到粒子的速度向量上,更新粒子的运动状态。
    particle.velocity = Cesium.Cartesian3.add(
      particle.velocity,
      gravityScratch,
      particle.velocity
    );
  };
}

const openWater = () => {
  carEntity = __viewer.entities.add({
    model: {
      uri: "/czml/CesiumMilkTruck.glb",
      minimumPixelSize: 64,
    },
    position: Cesium.Cartesian3.fromDegrees(-94.6, 41.8),
  });
  __viewer.trackedEntity = carEntity;
  // 计算汽车当前时间的模型矩阵
  let modelMatrix = carEntity.computeModelMatrix(
    __viewer.clock.startTime,
    new Cesium.Matrix4()
  );

  // 定义平移向量
  let translation = new Cesium.Cartesian3(-2.5, 0.0, 0.0); // 根据右手坐标系,x轴粒子应该往左移动若干举例,暂定-2.5

  // 对模型矩阵进行平移 使用Cesium.Matrix4.multiplyByTranslation,将变换矩阵modelMatrix乘以由笛卡尔 3 定义的隐式平移矩阵​
  adjustedModelMatrix = Cesium.Matrix4.multiplyByTranslation(
    modelMatrix,
    translation,
    new Cesium.Matrix4()
  );
  smokeParticle = __viewer.scene.primitives.add(
    new Cesium.ParticleSystem({
      image: "/images/smoke.png", // 指定粒子的图像纹理,这里是烟雾效果的图片。​
      imageSize: new Cesium.Cartesian2(20, 20),//粒子的初始大小,以像素为单位。Cartesian2对象表示二维向量,这里设置粒子图像的宽度和高度为20像素。​
      startScale: 1.0,//粒子开始时的缩放比例。1.0表示粒子将按原始大小开始。​
      endScale: 4.0,//粒子消失前的缩放比例。4.0表示粒子在消亡前会逐渐扩大到原始大小的4倍。​
      particleLife: 1.0,//每个粒子的寿命,以秒为单位。1.0表示粒子将在1秒后消亡。
      speed: 5.0,//粒子的发射速度,以米每秒为单位。5.0表示粒子将以5米每秒的速度从发射器发射出来。​
      emitter: new Cesium.CircleEmitter(0.5),//粒子的发射器类型,这里是一个圆形发射器,半径为0.5米。粒子将从这个圆形区域内随机发射。​
      emissionRate: 20.0,//粒子的发射率,即每秒发射的粒子数量。20.0表示每秒发射20个粒子。​
      modelMatrix: adjustedModelMatrix,//粒子系统的位置和方向由modelMatrix确定。
      lifetime: 16.0,//整个粒子系统的寿命,以秒为单位。16.0表示这个粒子系统将在16秒后被销毁。​
      updateCallback: (particle, dt) => {
        var position = particle.position;
        Cesium.Cartesian3.normalize(position, gravityScratch);
        Cesium.Cartesian3.fromElements(
          -10 * dt,
          gravityScratch.y * dt,
          -20 * dt,
          gravityScratch
        );
        particle.velocity = Cesium.Cartesian3.add(
          particle.velocity,
          gravityScratch,
          particle.velocity
        )
      }
    })
  );
}
const clear = () => {
  smokeParticle && __viewer.scene.primitives.remove(smokeParticle)
}

</script>
<style lang='less' scoped></style>

https://blog.csdn.net/qq_34447899/article/details/123224443

https://www.cnblogs.com/s313139232/p/14317366.html

https://juejin.cn/post/7316968265057583114

https://blog.csdn.net/qq_41400354/article/details/132160648

https://blog.csdn.net/weixin_45782925/article/details/123269490

https://www.jianshu.com/p/678cb44fbbc2

https://blog.csdn.net/fywindmoon/article/details/134308314

https://zhuanlan.zhihu.com/p/157070339

最后更新: 11/2/2024, 10:36:52 AM