第03章 - 核心架构与模块设计
第03章:核心架构与模块设计
3.1 CesiumJS 整体架构
3.1.1 分层架构设计
CesiumJS 采用清晰的分层架构设计,从底层渲染引擎到高级应用 API,层次分明:
┌─────────────────────────────────────────────────────────────────┐
│ 应用层 (Application Layer) │
│ Viewer │ Widget │ UI Controls │ 业务逻辑 │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 高级 API 层 (High-Level API) │
│ Entity │ DataSource │ Imagery │ Terrain │ 3D Tiles │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 核心层 (Core Layer) │
│ Scene │ Camera │ Globe │ Primitive │ Geometry │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 渲染层 (Renderer Layer) │
│ WebGL Context │ Shader │ Texture │ FrameBuffer │ Buffer │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 基础层 (Foundation Layer) │
│ Math │ Geometry │ Coordinate │ Time │ Event │ Promise │
└─────────────────────────────────────────────────────────────────┘
3.1.2 核心模块组成
CesiumJS 的核心模块及其职责:
| 模块 | 职责 | 主要类 |
|---|---|---|
| Viewer | 应用入口,集成所有功能 | Viewer |
| Scene | 场景管理,渲染调度 | Scene, Globe, SkyBox |
| Camera | 视角控制 | Camera, CameraController |
| Entity | 高级数据表示 | Entity, EntityCollection |
| Primitive | 底层渲染 | Primitive, GeometryInstance |
| DataSource | 数据源管理 | GeoJsonDataSource, CzmlDataSource |
| Imagery | 影像图层 | ImageryLayer, ImageryProvider |
| Terrain | 地形数据 | TerrainProvider, QuantizedMeshTerrainData |
| 3D Tiles | 大规模三维数据 | Cesium3DTileset, Cesium3DTile |
3.2 Viewer 架构详解
3.2.1 Viewer 组件构成
Viewer 是 CesiumJS 的核心容器,整合了所有主要功能:
┌─────────────────────────────────────────────────────────────────┐
│ Viewer │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ CesiumWidget │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Scene │ │ Clock │ │ Canvas │ │ │
│ │ │ │ │ │ │ (WebGL) │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ UI Widgets │ │
│ │ Animation│Timeline│Geocoder│HomeButton│SceneModePicker │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Data Management │ │
│ │ EntityCollection │ DataSourceCollection │ ImageryLayers │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
3.2.2 Viewer 主要属性
const viewer = new Cesium.Viewer('cesiumContainer');
// 核心组件访问
viewer.scene // Scene 对象
viewer.camera // Camera 对象
viewer.canvas // WebGL Canvas
viewer.clock // Clock 时钟
viewer.cesiumWidget // 底层 Widget
// 数据集合
viewer.entities // EntityCollection
viewer.dataSources // DataSourceCollection
viewer.imageryLayers // ImageryLayerCollection
viewer.terrainProvider // TerrainProvider
// UI 控件
viewer.animation // AnimationViewModel
viewer.timeline // Timeline
viewer.homeButton // HomeButton
viewer.geocoder // Geocoder
viewer.sceneModePicker // SceneModePicker
viewer.infoBox // InfoBox
viewer.selectionIndicator // SelectionIndicator
3.2.3 Viewer 配置选项
const viewer = new Cesium.Viewer('cesiumContainer', {
// ===== 场景配置 =====
scene3DOnly: false, // 是否仅 3D 模式
sceneMode: Cesium.SceneMode.SCENE3D, // 初始场景模式
// ===== 地形配置 =====
terrainProvider: Cesium.createWorldTerrain({
requestWaterMask: true,
requestVertexNormals: true
}),
// ===== 影像配置 =====
imageryProvider: new Cesium.IonImageryProvider({ assetId: 2 }),
baseLayerPicker: true, // 底图选择器
// ===== 时间配置 =====
shouldAnimate: true, // 是否自动播放动画
clockViewModel: undefined, // 时钟视图模型
// ===== UI 控件 =====
animation: true, // 动画控件
timeline: true, // 时间轴
fullscreenButton: true, // 全屏按钮
vrButton: false, // VR 按钮
geocoder: true, // 地理编码搜索
homeButton: true, // 主页按钮
infoBox: true, // 信息框
sceneModePicker: true, // 场景模式选择器
selectionIndicator: true, // 选择指示器
navigationHelpButton: true, // 导航帮助按钮
navigationInstructionsInitiallyVisible: false,
// ===== 渲染配置 =====
contextOptions: {
webgl: {
alpha: false,
antialias: true,
powerPreference: 'high-performance'
}
},
// ===== 其他 =====
targetFrameRate: undefined, // 目标帧率
useBrowserRecommendedResolution: true,
orderIndependentTranslucency: true,
shadows: false, // 阴影
terrainShadows: Cesium.ShadowMode.RECEIVE_ONLY
});
3.3 Scene 渲染架构
3.3.1 Scene 组件结构
Scene 是渲染引擎的核心,管理所有可渲染内容:
┌─────────────────────────────────────────────────────────────────┐
│ Scene │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Globe │ │ SkyBox │ │ Sun │ │
│ │ (地球) │ │ (天空盒) │ │ (太阳) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Moon │ │SkyAtmosphere│ │ Fog │ │
│ │ (月亮) │ │ (大气层) │ │ (雾) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ PrimitiveCollection │ │
│ │ Primitives │ GroundPrimitives │ 3DTiles │ Models │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Camera │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
3.3.2 Scene 渲染流程
// Scene 渲染循环(简化版)
function renderLoop() {
// 1. 更新时间
scene.clock.tick();
// 2. 更新相机
scene.camera.update(frameState);
// 3. 视锥体剔除
scene.globe.update(frameState);
scene.primitives.update(frameState);
// 4. 渲染命令排序
scene.frameState.commandList.sort();
// 5. 执行渲染
scene.render();
// 6. 请求下一帧
requestAnimationFrame(renderLoop);
}
3.3.3 Scene 配置与控制
const scene = viewer.scene;
// ===== 渲染配置 =====
scene.fog.enabled = true; // 雾效
scene.fog.density = 0.0002; // 雾密度
scene.globe.enableLighting = true; // 光照
scene.globe.dynamicAtmosphereLighting = true; // 动态大气光照
scene.globe.showGroundAtmosphere = true; // 地面大气
scene.skyAtmosphere.show = true; // 大气层
scene.sun.show = true; // 太阳
scene.moon.show = true; // 月亮
// ===== 性能配置 =====
scene.debugShowFramesPerSecond = true; // 显示帧率
scene.requestRenderMode = true; // 按需渲染
scene.maximumRenderTimeChange = 0.0; // 最大渲染时间变化
// ===== 深度测试 =====
scene.globe.depthTestAgainstTerrain = true; // 地形深度测试
// ===== 场景模式 =====
scene.mode = Cesium.SceneMode.SCENE3D; // 3D 模式
// Cesium.SceneMode.SCENE2D // 2D 模式
// Cesium.SceneMode.COLUMBUS_VIEW // 哥伦布视图
// ===== 事件监听 =====
scene.preRender.addEventListener(function(scene, time) {
// 渲染前回调
});
scene.postRender.addEventListener(function(scene, time) {
// 渲染后回调
});
3.4 坐标系统
3.4.1 坐标系类型
CesiumJS 中使用多种坐标系:
┌─────────────────────────────────────────────────────────────────┐
│ CesiumJS 坐标系统 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 笛卡尔坐标 (Cartesian3) │
│ - 以地球中心为原点 │
│ - X: 指向赤道与本初子午线交点 │
│ - Y: 指向赤道与东经90°交点 │
│ - Z: 指向北极 │
│ │
│ 2. 经纬度坐标 (Cartographic) │
│ - longitude: 经度(弧度) │
│ - latitude: 纬度(弧度) │
│ - height: 高度(米) │
│ │
│ 3. 屏幕坐标 (Cartesian2) │
│ - x: 屏幕水平像素 │
│ - y: 屏幕垂直像素 │
│ │
│ 4. 度数坐标 (Degrees) │
│ - 便于人类理解的经纬度格式 │
│ │
└─────────────────────────────────────────────────────────────────┘
3.4.2 坐标转换
// ===== 度数 -> 笛卡尔坐标 =====
const cartesian = Cesium.Cartesian3.fromDegrees(116.4, 39.9, 100);
// 或
const cartesianArray = Cesium.Cartesian3.fromDegreesArray([
116.0, 39.0,
117.0, 39.0,
117.0, 40.0
]);
// ===== 笛卡尔坐标 -> 经纬度(弧度)=====
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
console.log(cartographic.longitude); // 弧度
console.log(cartographic.latitude); // 弧度
console.log(cartographic.height); // 米
// ===== 弧度 -> 度数 =====
const lonDegrees = Cesium.Math.toDegrees(cartographic.longitude);
const latDegrees = Cesium.Math.toDegrees(cartographic.latitude);
// ===== 度数 -> 弧度 =====
const lonRadians = Cesium.Math.toRadians(116.4);
const latRadians = Cesium.Math.toRadians(39.9);
// ===== 屏幕坐标 -> 笛卡尔坐标 =====
const screenPosition = new Cesium.Cartesian2(400, 300);
const worldPosition = viewer.scene.pickPosition(screenPosition);
// ===== 笛卡尔坐标 -> 屏幕坐标 =====
const screenPos = Cesium.SceneTransforms.wgs84ToWindowCoordinates(
viewer.scene, cartesian
);
// ===== 从经纬度创建 Cartographic =====
const carto = Cesium.Cartographic.fromDegrees(116.4, 39.9, 100);
const carto2 = Cesium.Cartographic.fromRadians(
Cesium.Math.toRadians(116.4),
Cesium.Math.toRadians(39.9),
100
);
3.4.3 坐标工具函数
// 坐标工具类
class CoordinateUtils {
/**
* 度数转笛卡尔坐标
*/
static degreesToCartesian(lon, lat, height = 0) {
return Cesium.Cartesian3.fromDegrees(lon, lat, height);
}
/**
* 笛卡尔坐标转度数
*/
static cartesianToDegrees(cartesian) {
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
return {
longitude: Cesium.Math.toDegrees(cartographic.longitude),
latitude: Cesium.Math.toDegrees(cartographic.latitude),
height: cartographic.height
};
}
/**
* 计算两点间距离
*/
static distance(point1, point2) {
return Cesium.Cartesian3.distance(point1, point2);
}
/**
* 计算中心点
*/
static center(positions) {
return Cesium.BoundingSphere.fromPoints(positions).center;
}
/**
* 屏幕坐标转世界坐标
*/
static screenToWorld(viewer, screenPosition) {
// 射线拾取地球表面
const ray = viewer.camera.getPickRay(screenPosition);
return viewer.scene.globe.pick(ray, viewer.scene);
}
}
3.5 时间系统
3.5.1 时间类型
// JulianDate - CesiumJS 的核心时间类型
const now = Cesium.JulianDate.now();
// 从 JavaScript Date 创建
const date = new Date('2024-01-01T00:00:00Z');
const julianDate = Cesium.JulianDate.fromDate(date);
// 从 ISO 8601 字符串创建
const isoDate = Cesium.JulianDate.fromIso8601('2024-01-01T12:00:00Z');
// 时间加减
const later = Cesium.JulianDate.addSeconds(now, 3600, new Cesium.JulianDate());
const earlier = Cesium.JulianDate.addDays(now, -1, new Cesium.JulianDate());
// 时间比较
const diff = Cesium.JulianDate.secondsDifference(later, now); // 秒差
const compare = Cesium.JulianDate.compare(now, later); // -1, 0, 1
3.5.2 Clock 时钟系统
const clock = viewer.clock;
// ===== 时钟配置 =====
clock.startTime = Cesium.JulianDate.fromIso8601('2024-01-01T00:00:00Z');
clock.stopTime = Cesium.JulianDate.fromIso8601('2024-01-02T00:00:00Z');
clock.currentTime = Cesium.JulianDate.fromIso8601('2024-01-01T12:00:00Z');
// 时钟范围
clock.clockRange = Cesium.ClockRange.LOOP_STOP; // 循环播放
// Cesium.ClockRange.UNBOUNDED // 无限制
// Cesium.ClockRange.CLAMPED // 限制在范围内
// 播放速度(倍率)
clock.multiplier = 1; // 实时
clock.multiplier = 60; // 60 倍速
clock.multiplier = -1; // 倒放
// 是否播放
clock.shouldAnimate = true;
// ===== 时钟事件 =====
clock.onTick.addEventListener(function(clock) {
console.log('当前时间:', clock.currentTime);
});
clock.onStop.addEventListener(function(clock) {
console.log('时钟停止');
});
3.5.3 Timeline 时间轴
// 时间轴控制
const timeline = viewer.timeline;
// 缩放时间轴
timeline.zoomTo(
Cesium.JulianDate.fromIso8601('2024-01-01T00:00:00Z'),
Cesium.JulianDate.fromIso8601('2024-01-07T00:00:00Z')
);
// 时间轴事件
timeline.addEventListener('settime', function(e) {
console.log('时间轴时间改变:', e.clock.currentTime);
}, false);
3.6 事件系统
3.6.1 事件类型
// ===== Scene 事件 =====
viewer.scene.preUpdate.addEventListener(function(scene, time) {
// 更新前
});
viewer.scene.postUpdate.addEventListener(function(scene, time) {
// 更新后
});
viewer.scene.preRender.addEventListener(function(scene, time) {
// 渲染前
});
viewer.scene.postRender.addEventListener(function(scene, time) {
// 渲染后
});
// ===== Camera 事件 =====
viewer.camera.moveStart.addEventListener(function() {
console.log('相机开始移动');
});
viewer.camera.moveEnd.addEventListener(function() {
console.log('相机停止移动');
});
viewer.camera.changed.addEventListener(function(percentage) {
console.log('相机变化:', percentage);
});
// ===== Entity 事件 =====
viewer.entities.collectionChanged.addEventListener(function(collection, added, removed, changed) {
console.log('实体集合变化');
});
3.6.2 鼠标/触摸事件
// 创建事件处理器
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
// ===== 鼠标左键点击 =====
handler.setInputAction(function(click) {
console.log('左键点击:', click.position);
// 拾取实体
const pickedObject = viewer.scene.pick(click.position);
if (Cesium.defined(pickedObject)) {
console.log('选中对象:', pickedObject);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// ===== 鼠标移动 =====
handler.setInputAction(function(movement) {
const position = movement.endPosition;
console.log('鼠标位置:', position);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// ===== 鼠标右键点击 =====
handler.setInputAction(function(click) {
console.log('右键点击:', click.position);
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
// ===== 鼠标双击 =====
handler.setInputAction(function(click) {
console.log('双击:', click.position);
}, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
// ===== 滚轮 =====
handler.setInputAction(function(delta) {
console.log('滚轮:', delta);
}, Cesium.ScreenSpaceEventType.WHEEL);
// ===== 组合键 =====
handler.setInputAction(function(click) {
console.log('Shift + 左键点击');
}, Cesium.ScreenSpaceEventType.LEFT_CLICK, Cesium.KeyboardEventModifier.SHIFT);
// ===== 移除事件 =====
handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
// ===== 销毁处理器 =====
handler.destroy();
3.6.3 拾取功能
// 拾取场景中的对象
function pickHandler(position) {
// 拾取第一个对象
const pickedObject = viewer.scene.pick(position);
if (Cesium.defined(pickedObject)) {
// Entity
if (pickedObject.id && pickedObject.id instanceof Cesium.Entity) {
console.log('选中 Entity:', pickedObject.id.name);
}
// 3D Tiles
if (pickedObject.primitive instanceof Cesium.Cesium3DTileset) {
console.log('选中 3D Tiles');
}
// Primitive
if (pickedObject.primitive instanceof Cesium.Primitive) {
console.log('选中 Primitive');
}
}
// 拾取所有对象
const pickedObjects = viewer.scene.drillPick(position);
console.log('所有拾取对象:', pickedObjects.length);
// 拾取位置(世界坐标)
const worldPosition = viewer.scene.pickPosition(position);
if (Cesium.defined(worldPosition)) {
const cartographic = Cesium.Cartographic.fromCartesian(worldPosition);
console.log('拾取位置:', {
lon: Cesium.Math.toDegrees(cartographic.longitude),
lat: Cesium.Math.toDegrees(cartographic.latitude),
height: cartographic.height
});
}
}
3.7 模块化结构
3.7.1 npm 包结构
CesiumJS 提供模块化的 npm 包:
cesium (完整包)
├── @cesium/engine (核心引擎)
│ ├── Core/ # 基础工具
│ ├── Renderer/ # 渲染器
│ ├── Scene/ # 场景
│ └── DataSources/ # 数据源
│
└── @cesium/widgets (UI 组件)
├── Animation/ # 动画控件
├── Timeline/ # 时间轴
├── Geocoder/ # 地理编码
├── HomeButton/ # 主页按钮
└── ...
3.7.2 按需导入
// 完整导入
import * as Cesium from 'cesium';
// 按需导入(减小包体积)
import {
Viewer,
Cartesian3,
Color,
Entity,
GeoJsonDataSource
} from 'cesium';
// 使用 @cesium/engine(更轻量)
import {
Scene,
Camera,
Primitive,
GeometryInstance,
RectangleGeometry
} from '@cesium/engine';
3.7.3 Tree Shaking 优化
// vite.config.js
import { defineConfig } from 'vite';
import cesium from 'vite-plugin-cesium';
export default defineConfig({
plugins: [cesium()],
build: {
rollupOptions: {
output: {
manualChunks: {
cesium: ['cesium']
}
}
}
}
});
3.8 扩展机制
3.8.1 Viewer 扩展
// 定义扩展
function myViewerExtension(viewer) {
// 添加自定义属性
viewer.myCustomProperty = 'value';
// 添加自定义方法
viewer.myCustomMethod = function() {
console.log('自定义方法被调用');
};
// 返回清理函数
return function() {
delete viewer.myCustomProperty;
delete viewer.myCustomMethod;
};
}
// 应用扩展
viewer.extend(myViewerExtension);
// 使用扩展
viewer.myCustomMethod();
3.8.2 自定义 DataSource
// 自定义 DataSource
class MyDataSource {
constructor(name) {
this._name = name;
this._entityCollection = new Cesium.EntityCollection();
this._clock = undefined;
this._changedEvent = new Cesium.Event();
this._errorEvent = new Cesium.Event();
this._loadingEvent = new Cesium.Event();
}
get name() { return this._name; }
get entities() { return this._entityCollection; }
get clock() { return this._clock; }
get changedEvent() { return this._changedEvent; }
get errorEvent() { return this._errorEvent; }
get loadingEvent() { return this._loadingEvent; }
get isLoading() { return false; }
async load(data) {
this._loadingEvent.raiseEvent(this, true);
try {
// 解析数据并创建实体
for (const item of data) {
this._entityCollection.add(new Cesium.Entity({
name: item.name,
position: Cesium.Cartesian3.fromDegrees(item.lon, item.lat)
}));
}
this._changedEvent.raiseEvent(this);
} catch (error) {
this._errorEvent.raiseEvent(this, error);
} finally {
this._loadingEvent.raiseEvent(this, false);
}
return this;
}
update(time) {
// 每帧更新逻辑
return true;
}
}
// 使用自定义 DataSource
const myDataSource = new MyDataSource('自定义数据源');
await myDataSource.load([
{ name: '点1', lon: 116.4, lat: 39.9 },
{ name: '点2', lon: 121.5, lat: 31.2 }
]);
viewer.dataSources.add(myDataSource);
3.9 本章小结
本章深入介绍了 CesiumJS 的核心架构:
- 分层架构:从基础层到应用层的清晰分离
- Viewer:应用入口,整合所有功能组件
- Scene:渲染引擎核心,管理所有可渲染内容
- 坐标系统:笛卡尔坐标、经纬度、屏幕坐标的转换
- 时间系统:JulianDate、Clock、Timeline
- 事件系统:场景事件、鼠标事件、拾取功能
- 模块化:npm 包结构、按需导入
- 扩展机制:Viewer 扩展、自定义 DataSource
在下一章中,我们将详细介绍 Viewer 与场景管理。
3.10 思考与练习
- 画出 CesiumJS 的分层架构图,标注各层的主要职责。
- 编写一个坐标转换工具类,支持多种坐标格式互转。
- 实现一个自定义事件处理器,响应键盘和鼠标组合操作。
- 研究
@cesium/engine和完整cesium包的区别。 - 尝试创建一个简单的自定义 DataSource。

浙公网安备 33010602011771号