
简介本资源是一套基于Vue框架集成Cesium三维地理信息可视化能力的完整训练项目源码面向计算机、地理信息、遥感、测绘等相关专业在校学生及初学者解决Web端三维GIS开发入门与实践难题。项目已通过实际运行测试支持底图加载含Cesium Ion、天地图、上海地形图、DEM高程数据渲染、地形切片处理及WGS84/笛卡尔/屏幕坐标系转换等核心功能适合作为毕业设计、课程设计或大作业参考模板。压缩包共488个文件含107个JS逻辑脚本、17个Vue组件、54个CSS样式文件、36个JSON配置及173个PNG/JPG图像资源整体大小6.01MB结构清晰便于模块化学习。已有156人下载学习配套详细使用说明文档涵盖API调用示例、坐标转换原理、角度弧度换算方法及常见问题排错提示可直接运行并在此基础上拓展三维分析、空间量测等进阶功能。1. Vue Cesium 不是简单拼接而是 WebGL 场景与响应式 UI 的协同调度很多开发者拿到“基于 Vue 的 Cesium 训练项目”压缩包后第一反应是解压、npm install、npm run serve——结果页面白屏、控制台报Cesium is not defined或Viewer is not a constructor。这不是环境没配好而是没理解这个组合的本质Vue 管 DOM 生命周期和状态响应Cesium 管 WebGL 渲染上下文和地球空间计算二者必须在挂载时机、容器绑定、资源释放三个关键节点上严格对齐。本项目不是把 Cesium 当成普通组件塞进template而是用 Vue 的ref和onMounted精确控制 Viewer 实例的创建与销毁它覆盖了从基础地图加载、3D 模型节点操作、动态矩形绘制到高程数据叠加、MVT 矢量瓦片解析等典型 WebGIS 场景所有源码均基于 CesiumJS 1.105 与 Vue 3 Composition API 编写无任何第三方封装库依赖。适合已掌握 Vue 基础能写 setup script、理解 ref/reactive且有地理信息或三维可视化需求的前端工程师尤其适用于需要快速验证空间分析逻辑、调试模型节点层级、或复用高程/热力图渲染模块的中高级开发场景。2. 用 Vue 3 Composition API 在本地跑通 Cesium Viewer 的最小命令2.1 为什么必须用onMounted而非created初始化 ViewerCesium 的Viewer构造函数要求目标 DOM 元素已真实存在于文档流中且具备明确宽高不能为0px。Vue 2 的mounted钩子尚可满足但在 Vue 3 的 Composition API 中若在setup()同步执行new Cesium.Viewer(...)此时模板尚未编译ref绑定的div还未挂载必然报错。常见误写如下// ❌ 错误setup 内直接 new Viewer export default { setup() { const viewer new Cesium.Viewer(cesiumContainer) // 此时 #cesiumContainer 不存在 return { viewer } } }正确做法是将 Viewer 创建延迟至onMounted生命周期钩子内并配合ref获取真实 DOM 节点template div refcesiumContainer classcesium-viewer/div /template script setup import { ref, onMounted } from vue import * as Cesium from cesium const cesiumContainer ref(null) let viewer null onMounted(() { // ✅ 确保容器已挂载且尺寸有效 if (cesiumContainer.value) { viewer new Cesium.Viewer(cesiumContainer.value, { terrainProvider: Cesium.createWorldTerrain(), // 启用全球地形 baseLayerPicker: false, // 关闭底图选择器减少干扰 geocoder: false, // 关闭搜索框聚焦训练逻辑 timeline: false, // 隐藏时间轴 animation: false // 关闭动画控件 }) } }) /script style scoped .cesium-viewer { width: 100vw; height: 100vh; margin: 0; padding: 0; } /style提示cesiumContainer.value是原生 DOM 元素不是 Vue 的响应式对象。Cesium Viewer 必须接收真实 DOM 节点传入ref对象本身会报类型错误。2.2 Cesium 模块按需导入以规避打包体积爆炸CesiumJS 默认打包体积超 20MBgzip 后约 5MB直接import * as Cesium from cesium会导致首屏加载极慢。本项目采用官方推荐的按需导入策略仅引入实际用到的类与函数// ✅ 正确只导入 Viewer、Entity、RectangleGraphics 等必需模块 import { Viewer, Entity, RectangleGraphics, Color, Cartesian3 } from cesium import { createWorldTerrain } from cesium/Source/Core/createWorldTerrain.js import { IonResource } from cesium/Source/Scene/IonResource.js // ❌ 错误全量导入即使使用 tree-shaking 也难彻底剔除 // import * as Cesium from cesium对应vite.config.js需配置 Cesium 资源路径别名避免运行时 404// vite.config.js import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], resolve: { alias: { cesium: cesium/Source } }, build: { rollupOptions: { external: [cesium] } } })注意Cesium 的IonResource用于加载在线 3D Tiles若项目需离线运行必须替换为本地Cesium3DTileset加载方式并预置.b3dm文件否则首次加载会触发跨域请求失败。2.3 Vue 路由参数驱动 Cesium 场景初始化训练项目常需根据 URL 参数切换不同地理范围或数据源。例如访问/scene?regionbeijinglayerterrain时自动定位北京并启用地形。利用 Vue Router 的useRoute可实现script setup import { onMounted, watch } from vue import { useRoute } from vue-router import { Viewer, Rectangle, Cartographic, Ellipsoid } from cesium const route useRoute() let viewer null onMounted(() { viewer new Viewer(cesiumContainer) initSceneByRoute() }) // 监听路由变化动态更新视角 watch(() route.query, (newQuery) { if (viewer newQuery.region) { initSceneByRoute() } }, { immediate: true }) function initSceneByRoute() { const { region, layer } route.query switch (region) { case beijing: // 北京经纬度范围东经115.7°–117.4°北纬39.4°–41.6° const rectangle Rectangle.fromDegrees(115.7, 39.4, 117.4, 41.6) viewer.camera.flyTo({ destination: rectangle, orientation: { heading: Cesium.Math.toRadians(0), pitch: Cesium.Math.toRadians(-30), roll: 0 } }) break case shanghai: viewer.camera.flyTo({ destination: Cartesian3.fromDegrees(121.47, 31.23, 500000), duration: 2 }) break } if (layer terrain) { viewer.terrainProvider createWorldTerrain() } } /script3. Cesium 模型节点操作与矩形绘制的 Vue 响应式封装3.1 将 3D 模型节点如 glTF作为 Vue 响应式实体管理Cesium 中的Entity是空间对象的抽象但其属性如位置、朝向、显隐默认不响应 Vue 的 reactivity。本项目通过ref包装 Entity 并监听属性变更实现双向同步template div input v-modelmodelPosition.lng placeholder经度 / input v-modelmodelPosition.lat placeholder纬度 / input v-modelmodelPosition.height placeholder高度(m) / button clickupdateModelPosition更新模型位置/button /div /template script setup import { ref, reactive, onMounted } from vue import { Viewer, Entity, Cartesian3, Transforms } from cesium const modelPosition reactive({ lng: 116.4, lat: 39.9, height: 100 }) let viewer null let modelEntity null onMounted(() { viewer new Viewer(cesiumContainer) // 创建模型实体初始位置 modelEntity viewer.entities.add( new Entity({ name: Beijing Tower, position: Cartesian3.fromDegrees(modelPosition.lng, modelPosition.lat, modelPosition.height), model: { uri: /models/tower.gltf, // 本地 glTF 模型路径 scale: 100, minimumPixelSize: 128 } }) ) }) function updateModelPosition() { // ✅ 手动触发 Cesium 属性更新Vue 无法自动代理 modelEntity.position.setValue( Cartesian3.fromDegrees(modelPosition.lng, modelPosition.lat, modelPosition.height) ) } /script提示Cartesian3.fromDegrees()返回的是不可变对象每次修改位置必须调用setValue()而非直接赋值entity.position ...否则 Cesium 渲染器不会感知变更。3.2 Vue 表单驱动 Cesium 绘制矩形RectangleGraphicsCesium 的RectangleGraphics用于绘制地理围栏、区域标注等其rectangle属性需为Rectangle实例。本项目将经纬度输入框与矩形边界绑定实现所见即所得编辑template div classrect-form label西经input v-model.numberrect.west //label label南纬input v-model.numberrect.south //label label东经input v-model.numberrect.east //label label北纬input v-model.numberrect.north //label button clickdrawRectangle绘制矩形/button button clickclearRectangle清除/button /div /template script setup import { ref, reactive } from vue import { Viewer, Rectangle, Color, RectangleGraphics } from cesium const rect reactive({ west: 115.7, south: 39.4, east: 117.4, north: 41.6 }) let viewer null let rectEntity null function drawRectangle() { if (!viewer) return // 清除旧矩形 if (rectEntity) { viewer.entities.remove(rectEntity) } // 创建新矩形实体 rectEntity viewer.entities.add({ name: Custom Rectangle, rectangle: { coordinates: Rectangle.fromDegrees( rect.west, rect.south, rect.east, rect.north ), material: Color.RED.withAlpha(0.5), // 半透明红色填充 outline: true, outlineColor: Color.RED, outlineWidth: 3 } }) } function clearRectangle() { if (rectEntity) { viewer.entities.remove(rectEntity) rectEntity null } } /script3.2.1 矩形坐标合法性校验表参数合法范围校验逻辑示例非法值west-180 到 180west east且west -180west 181,west eastsouth-90 到 90south north且south -90south 91,south northeast-180 到 180east west且east 180east -181,east westnorth-90 到 90north south且north 90north -91,north south校验代码可嵌入drawRectangle函数开头function drawRectangle() { if (rect.west rect.east || rect.south rect.north) { alert(矩形坐标范围错误西经必须小于东经南纬必须小于北纬) return } if (Math.abs(rect.west) 180 || Math.abs(rect.east) 180) { alert(经度必须在 -180 到 180 之间) return } if (Math.abs(rect.south) 90 || Math.abs(rect.north) 90) { alert(纬度必须在 -90 到 90 之间) return } // ... 继续绘制 }4. 高程数据叠加与 MVT 矢量瓦片加载的实战配置4.1 加载本地高程数据GeoTIFF实现地形起伏Cesium 原生不支持直接解析 GeoTIFF需借助Cesium.GeoTiffTerrainDataCesium 1.100 引入与Cesium.TerrainProvider结合。本项目提供terrain/目录存放预处理的.tif文件并通过Cesium.createTerrainFromUrl加载// 加载本地高程数据需确保 CORS 允许 async function loadLocalTerrain() { try { const terrainProvider await Cesium.createTerrainFromUrl( new Cesium.Resource({ url: /terrain/beijing_dem.tif, // 本地 GeoTIFF 路径 crossOrigin: anonymous // 关键启用跨域请求 }), { requestVertexNormals: true, // 启用法线计算提升光照效果 ellipsoid: Cesium.Ellipsoid.WGS84 } ) viewer.terrainProvider terrainProvider } catch (error) { console.error(高程数据加载失败:, error) // 回退到全球地形 viewer.terrainProvider Cesium.createWorldTerrain() } }注意浏览器对本地文件file://协议的fetch请求默认禁用 CORS必须通过vite preview或nginx启动 HTTP 服务否则crossOrigin: anonymous无效。4.2 解析 MVT 格式矢量瓦片并渲染为 Cesium EntityMVTMapbox Vector Tile是高效传输地理矢量数据的标准格式。Cesium 本身不原生支持 MVT需借助ol-mapbox-style或自定义解析器。本项目采用轻量级方案用vector-tile库解析二进制 MVT再转换为 CesiumPolygonGeometry# 安装依赖非 cesium 官方包 npm install vector-tile pbf// mvt-loader.js import { VectorTile } from vector-tile import { Pbf } from pbf import { PolygonGeometry, GeometryInstance, Primitive, Color, Ellipsoid } from cesium export async function loadMVT(url, layerName building) { const response await fetch(url) const arrayBuffer await response.arrayBuffer() const pbf new Pbf(arrayBuffer) const vt new VectorTile(pbf) const features [] if (vt.layers[layerName]) { vt.layers[layerName].features.forEach(feature { const geometry feature.toGeoJSON(0, 0, 4326) // WGS84 坐标系 if (geometry.type Polygon geometry.coordinates.length 0) { // 转换 GeoJSON 坐标为 Cesium Cartesian3 数组 const positions geometry.coordinates[0].map(([lon, lat]) Cesium.Cartesian3.fromDegrees(lon, lat, 0) ) features.push({ positions, color: Cesium.Color.BLUE.withAlpha(0.7) }) } }) } return features } // 在 Vue 组件中使用 async function loadBuildingLayer() { const features await loadMVT(/tiles/{z}/{x}/{y}.mvt, building) features.forEach(({ positions, color }) { viewer.entities.add({ polygon: { hierarchy: new Cesium.PolygonHierarchy( new Cesium.PolygonOutlineGeometry({ polygonHierarchy: new Cesium.PolygonHierarchy( Cesium.Cartesian3.fromDegreesArray(positions.map(p [p.x, p.y]))) }) ), material: Cesium.ColorMaterialProperty.fromColor(color) } }) }) }4.2.1 MVT 服务 URL 模板与 Cesium 坐标系映射规则服务类型URL 模板坐标系Cesium 转换要点Mapbox Style APIhttps://api.mapbox.com/v4/{id}/{z}/{x}/{y}.mvt?access_token{token}Web Mercator (EPSG:3857)需用Cesium.WebMercatorProjection转换为 WGS84自托管 TMS/tiles/{z}/{x}/{y}.mvtWGS84 (EPSG:4326)直接fromDegrees(lon, lat)GeoServer WMTS/geoserver/gwc/service/tms/1.0.0/{layer}EPSG%3A4326pbf/{z}/{x}/{-y}.pbfWGS84注意 y 轴翻转y (2^z - 1) - y5. Cesium 3D Tiles 单体化与动态光照调试技巧5.1 3DTiles 单体化Pickable的 Vue 控件联动3D Tiles 模型如建筑群默认为整体渲染无法单独点击某个建筑。启用单体化需在Cesium3DTileset加载时设置colorBlendMode: Cesium.Cesium3DTileColorBlendMode.REPLACE并绑定pick事件template div button clickenablePicking启用单体拾取/button div v-ifpickedFeature选中建筑{{ pickedFeature.name }}/div /div /template script setup import { ref, onMounted } from vue import { Viewer, Cesium3DTileset, SceneTransforms } from cesium const pickedFeature ref(null) onMounted(() { const tileset new Cesium3DTileset({ url: /tiles/buildings/tileset.json, colorBlendMode: Cesium.Cesium3DTileColorBlendMode.REPLACE, maximumScreenSpaceError: 1 }) viewer.scene.primitives.add(tileset) viewer.scene.globe.depthTestAgainstTerrain true // 绑定鼠标点击事件 viewer.screenSpaceEventHandler.setInputAction((movement) { const pickedObject viewer.scene.pick(movement.position) if (pickedObject pickedObject.id) { // 获取单体化属性需模型元数据支持 const properties pickedObject.id.properties if (properties properties.name) { pickedFeature.value { name: properties.name } } } }, Cesium.ScreenSpaceEventType.LEFT_CLICK) }) /script提示单体化依赖模型导出时嵌入batch table元数据。若pickedObject.id.properties为空说明原始.b3dm文件未包含属性表需用3d-tiles-tools重新生成。5.2 动态光照参数调试表Cesium 1.105参数类型默认值调试建议影响效果scene.globe.enableLightingBooleantrue设为false可关闭全局光照排查阴影干扰全球光照开关scene.sun.showBooleantrue设为false后手动控制scene.light太阳光源显隐scene.light.colorColorColor.WHITE改为Color.YELLOW模拟黄昏光源颜色scene.light.intensityNumber1.0调至0.3减弱光照突出地形纹理光源强度scene.fog.densityNumber0.0设为0.0001添加薄雾增强纵深感大气雾效调试代码示例// 动态调整光照可绑定 Vue slider function adjustSunIntensity(value) { viewer.scene.light.intensity value } function toggleFog(enabled) { viewer.scene.fog.enabled enabled viewer.scene.fog.density enabled ? 0.0001 : 0.0 }5.3 Cesium 3D 地球滚动崩溃的根因与规避方案当用户快速拖拽地球导致帧率骤降时Cesium 可能触发Maximum call stack size exceeded或WebGL context lost。根本原因是Camera.flyTo()连续调用未加节流或Entity创建/销毁过于频繁。本项目采用三重防护节流相机移动对viewer.camera.moveEndEvent添加防抖批量实体操作用viewer.entities.removeAll()替代逐个remove()资源懒加载3DTileset设置maximumScreenSpaceError: 2降低精度// 防抖移动结束事件避免高频触发 let moveEndTimer null viewer.camera.moveEnd.addEventListener(() { clearTimeout(moveEndTimer) moveEndTimer setTimeout(() { // 执行视图相关逻辑如更新比例尺、加载周边数据 updateScaleBar() }, 300) })最终所有训练项目源码均通过npm run build打包验证输出目录结构清晰dist/下含index.html、assets/含 Cesium 资源、models/glTF、tiles/3D Tiles、terrain/GeoTIFF可直接部署至 Nginx 或 GitHub Pages。本文还有配套的精品资源点击获取