简介:本资源是一个面向GIS开发工程师、数字孪生系统构建者及前端进阶学习者的三维数字城市可视化实践项目,聚焦企业级应用中Cesium与现代前端技术栈的工程化集成。项目基于Vue3.0 + TypeScript构建,深度整合Cesium开源GIS库,实现全球尺度三维地球渲染、主流地图服务接入(如OpenStreetMap)、WebGL加速的建筑模型/地形/点云可视化,并支持地图元素的交互式编辑与后台数据持久化保存。压缩包共525个文件,含93个TypeScript核心逻辑文件、105个JavaScript运行时脚本、143张UI与图层资源PNG、53张JPG场景示意图、39个Vue组件及配套CSS/JSON配置,整体10.06MB,结构清晰,涵盖源码、样式、静态资源与构建配置。目前已有1152人学习下载,开发者可直接运行调试,掌握Cesium场景初始化、图层管理、相机控制、编辑状态同步及前后端API对接等关键能力,是落地数字城市可视化平台的高参考性开源范例。 最近在做数字城市三维可视化项目,技术栈从选型到交付折腾了将近两个月。甲方要求很直接:WebGL效果要像样,场景里的模型、标注、特效得能通过后台编辑保存,而不是每次改动都重新发版。我在Three.js、Mapbox、Unity和Cesium之间来回比较,最后选了Cesium这个开源GIS库。这篇文章把整个选型、搭建、渲染效果、编辑器开发、后台保存的完整实现过程,以及上线前踩过的一堆坑,一次性捋清楚。想用开源路线做数字城市可视化的同学,这篇基本能覆盖你从0到1要面对的主要问题。
1. 为什么数字城市项目最终选了 Cesium:从备选方案到定型的逻辑
1.1 备选方案横向比较:为什么不是 Three.js / Mapbox / Unity
先说结论,数字城市这个场景和普通Web可视化不一样,它天然带地理坐标,需要加载倾斜摄影、地形、矢量底图,还要做场景漫游和标注。我一开始也纠结过Three.js,毕竟它的渲染生态最大,社区里能翻到大量炫酷的粒子、光影案例。但真往数字城市的方向一推,问题马上暴露:Three.js没有坐标系概念,没有投影转换,没有瓦片调度,更没有地形和3D Tiles的数据格式。你确实可以在Three.js里手动加载glTF、手动算经纬度投影,但相当于把所有GIS底层重新写一遍,这个工程量和后续维护成本我直接劝退了。
Mapbox我也考虑过,它的矢量底图渲染确实漂亮,表达式样式也很灵活,做二维地图编辑体验一流。但数字城市里最核心的三维数据源是倾斜摄影、BIM、白模,Mapbox对glTF的加载、对大体量瓦片流式调度的支持偏弱,更多还是围绕风格化地图在做2.5D场景。Unity和UE的渲染上限当然高,光影、反射、后期都能做得很棒,可它们面向的是客户端或游戏引擎工作流,要部署到浏览器里做一套B/S架构的后台编辑系统,WebGL打包体积、数据接口、跨域加载、权限管理都要额外折腾,开发周期压不住。
Cesium在这几个方向里是折中得最好的:它本质上就是一个GIS库,底层是三维地球的椭球模型,自带投影转换、摄像机控制、时间轴动画、地形服务、3D Tiles流式调度这些能力。数字城市要的东西它基本都有,而且是Apache 2.0协议,完全开源,商用没有授权费风险。我最终定它,说白了不是因为它某个特效做得最好,而是因为“地理空间底座”这个核心需求,它是唯一一个不用我从头造的。
1.2 Cesium 的“地球级”坐标体系和数据调度
Cesium的天生优势在于坐标体系。三维数字城市最大的坑就是坐标偏移和坐标系混乱,而Cesium底层直接使用WGS84椭球,经纬度、高度、弧度这些概念是原生支持的,不需要像Three.js那样自己定义世界坐标和地理坐标的映射关系。你用Entity加一个点,直接用经纬度就行,后台存的数据也是经纬度,前后端沟通成本大幅降低。
另一个核心是3D Tiles。倾斜摄影数据动辄几个GB,BIM模型面数更是惊人,如果直接丢给浏览器一次渲染,任何机器都扛不住。3D Tiles的机制是把模型切成层次细节的瓦片,根据相机视锥和距离动态加载卸载,所以地形、倾斜摄影、点云、BIM都可以像地图瓦片一样流式加载。这一点在数字城市项目里是刚需,因为用户不会只看一个静态视角,他要平移、旋转、放大缩小,必须有LOD调度兜底。
时间轴也是Cesium重要的隐藏优势。数字城市不只是展示静态模型,还需要模拟飞机飞行、车辆行驶、塔吊旋转、白天黑夜切换这些动态效果。Cesium的Clock和SampledPositionProperty天生就是为这类时间动态场景设计的,配合粒子系统能做很多业务联动效果。我在选型时把这个考虑得很重,因为可视化编辑保存里必然要存动画参数,这套时间轴机制后续帮了大忙。
1.3 整体架构:前端展示 + 可视化编辑 + 后端保存
项目落地时,我设计了一个比较清晰的分层架构。前端是Cesium + Vue3 + Vite,负责三维场景渲染和可视化编辑器操作。后端用Spring Boot + PostgreSQL,主要职责不是算渲染,而是保存场景配置、版本、权限这些业务数据。这里有一个关键思路:前端Cesium里创建的图层、实体、特效、相机视角,全部抽象成一套统一的JSON配置。用户在前端编辑器里拖一个模型、改一个颜色、调一个透明度,本质上都是在修改这颗JSON树,保存时通过接口扔给后端,回显时再把JSON取回来重新构建场景。
这个架构的好处是把“三维场景”和“业务系统”解耦了。后端完全不需要知道Cesium怎么渲染,只需要负责JSON的存取、校验、版本管理。前端也不需要考虑数据库怎么存,只负责把JSON翻译成Cesium对象。对于数字城市项目这种“内容和展示都会频繁变化”的交付形态,这个设计非常实用,后面我会专门展开讲这一块的工程细节。
2. 底图、坐标系与数据接入:数字城市的地基工作
2.1 坐标系先理清:WGS84、4326、3857 与国测局偏移
在数字城市项目里,坐标系混乱是导致“标注对不上底图”的最常见原因。Cesium默认使用WGS84,也就是EPSG:4326这个地理坐标系,经纬度单位是度。而常见的地图瓦片服务,比如Web Mercator投影的XYZ瓦片,对应的是EPSG:3857,它是一个投影坐标系,底层是通过墨卡托投影把经纬度换算成米。Cesium内部会处理这个投影转换,但前提是你得知道自己拿到的数据是什么坐标系。
国内还有一个特别容易踩的坑是国测局坐标偏移。高德、百度这类国内地图服务,公开展示的数据坐标不是原始WGS84,而是经过加密偏移的火星坐标系或百度坐标系。如果你用高德/百度的瓦片做底图,然后又用一份WGS84的经纬度数据往上叠标注,就会发现所有点和线都整体偏移了几十米甚至上百米。这个偏移在二维地图上肉眼可见,在三维场景里叠加了倾斜摄影后更明显。
我的处理经验是后端统一存储WGS84坐标,前端展示层根据底图类型做转换。如果底图用了高德或百度的瓦片,就在后端或前端做一个坐标纠偏模块,把WGS84转成对应坐标系后再渲染。这里最怕的是“半路统一”,比如一个项目里有人从高德复制了坐标,有人从GPS设备拿了WGS84,混着存,最后整个场景就是歪的。项目启动第一天就应当定死这个规范。
2.2 把主流地图接进来当底图
数字城市一般不会只用纯地球背景,通常会接影像底图或者矢量底图。Cesium加载底图是通过ImageryProvider系列组件完成的,常见的UrlTemplateImageryProvider可以直接按照{z}/{x}/{y}的瓦片规则加载任意XYZ服务的地址。我用天地图、高德影像、OSM都试过,效果比较稳定的组合是:影像底图用天地图或高德影像,矢量标注层用OSM或者自研服务,这样大场景有底图可看,放大之后又有足够清晰度。
接入代码不复杂,核心是创建一个ImageryLayer加到底图层。
const viewer = new Cesium.Viewer('cesiumContainer', { baseLayerPicker: false }); viewer.imageryLayers.addImageryProvider( new Cesium.UrlTemplateImageryProvider({ url: 'https://你的天地图服务地址/wmts?tilematrix={z}&tilerow={y}&tilecol={x}&style=default&format=tiles', maximumLevel: 18 }) );注意天地图服务一般需要申请token,高德的影像瓦片对referer有校验,本地开发时建议走一个代理服务,避免跨域问题。图层顺序也要留意,Cesium的imageryLayers数组是按顺序叠加的,越往上层越靠前。业务数据面、标注面应该放在底图之上,而不是把业务图层塞到底图下面,否则倾斜摄影和标注会被影像盖住。
另外我建议不要把底图和3D Tiles混在一个图层里管理。底图是“地理背景”,3D Tiles是“业务建筑”,两者在Cesium里是完全不同的对象类型。编辑器操作时,底图一般只允许切换和透明度调整,不参与增删改,这样数据模型更干净。
2.3 倾斜摄影、白模与 BIM 数据做成 3D Tiles
数字城市的建筑数据源主要有三类:倾斜摄影、白模、BIM模型。这三类数据格式不同,但最终在Cesium里都建议统一转成3D Tiles,才能享受到LOD调度和流式加载。
倾斜摄影是实景三维最常见的数据来源,用无人机拍摄后通过建模软件生成,常见的有OSGB、OBJ格式。我以前的做法是先用CC或大疆智图这类软件把照片跑出倾斜模型,再通过转换工具发布成3D Tiles。生产环境里也可以直接用CesiumLab、py3dtiles这类工具做转换,生成一个tileset.json和一堆b3dm文件,Cesium直接加载这个tileset地址就行。
白模一般是从规自部门拿到的建筑轮廓和高度数据,或者是根据楼层数快速拉伸出来的体块。它的数据量小,适合做整个城市的快速三维概览。加载进来以后如果想做效果提升,可以用CustomShader给白模加一些简单的假光照和AO效果,让立面看起来更有立体感。
BIM模型麻烦一些,Revit、Navisworks这类软件导出到Web本身就是个重活。我的经验是先在建模端做减面处理,把精细的构件从几百万面降到几万面以内,再导出glTF或3D Tiles,否则浏览器加载以后画面会卡成幻灯片。如果项目要求精细BIM,最好用流式渲染方案,而不是一次性全量加载。
2.4 矢量数据:MVT 与 GeoJSON 的加载姿势
数字城市不止有三维模型,还有大量矢量业务数据:地图标注、行政边界、道路线、楼栋面、摄像头点位。我在项目里遇到过“cesium加载mvt格式”的搜索需求,坦率说Cesium原生并不直接支持MVT格式的矢量瓦片,它更擅长的是GeoJSON和3D Tiles。
我的做法是分场景处理:实时变化的业务数据,比如设备点位、告警点、标注,用GeoJSON加载最方便,Cesium直接提供了GeoJsonDataSource,样式也可以通过配置直接控制。如果数据量大到GeoJSON一次性解析会卡顿,就做分级加载,或者把矢量面数据转成3D Tiles瓦片,用Cesium3DTileset加载。
MVT格式更适合在传统二维地图引擎里使用,如果非要在Cesium里用,可行的链路是先在后端或中间层把MVT解码成GeoJSON,再交给Cesium渲染。还有一个相对轻量的方案是直接把矢量数据发布成WMS或WMTS服务,在Cesium中作为影像图层加载。这本质上是用栅格化的方式呈现矢量,交互能力会弱一些,但胜在数据量和加载速度可控。
3. WebGL 渲染与视觉特效:夜景、动态光照和让城市“活”起来
3.1 先解决“WebGL isn't supported or disabled”
这个报错几乎是每个Cesium项目都会遇到的现场问题,尤其是客户用非主流浏览器、缩略版浏览器或者远程桌面环境时。Cesium基于WebGL渲染,如果浏览器环境不支持WebGL,Viewer初始化时会直接白屏,控制台里大概率能看到“WebGL context could not be created”或者“WebGL isn't supported, or is disabled”之类的提示。
这类问题大多数不是代码bug,而是运行环境问题。排查流程我一般这么走:先在浏览器的GPU诊断页面里看硬件加速状态,确认WebGL是否被禁用;然后检查浏览器的“设置-高级-硬件加速”是否开启,开启后要重启浏览器才生效;再确认显卡驱动是否正常,远程桌面环境很坑,虚拟显卡的WebGL支持不全,经常导致初始化失败;最后检查浏览器是否有插件或企业策略禁用了WebGL2。
还有一个代码层面的兜底:初始化Viewer时设置WebGL参数,降低对高端特性依赖。
const viewer = new Cesium.Viewer('cesiumContainer', { webgl: { failIfMajorPerformanceCaveat: false, powerPreference: 'high-performance' } });failIfMajorPerformanceCaveat: false的意思是即使系统判断显卡性能不高,也允许尝试创建WebGL上下文,这在老机器和虚拟机里很管用。如果Canvas被隐藏或显示时上下文丢失,还需要监听webglcontextlost事件,在适当时机重建Viewer,否则页面会一直黑屏。
3.2 夜景模式:建筑发光与灯光模拟
数字城市大屏项目里夜景模式是标配,甲方基本都会提“切换夜景模式,模拟真实夜晚灯光、光线等场景”。Cesium默认的昼夜光照可以模拟太阳光方向,但整体偏物理真实,城市夜景需要的“建筑轮廓发光、窗户亮灯、道路路灯点缀”这些效果,要靠叠加手段来完成。
我实现的夜景方案分三层。第一层是给3D Tiles的模型风格调暗:通过Cesium3DTileStyle把建筑基础颜色调成深灰蓝色,模拟夜晚建筑本身的暗部。第二层是叠加夜景贴图或灯光贴图,用半透明纹理覆盖在城市区域,让窗户位置出现规律的亮斑。第三层是重点建筑用CustomShader做一个自发光效果,让地标建筑看起来是本身在发光,而不是被外部光照亮。
CustomShader是Cesium对3D Tiles做自定义渲染的重要入口,可以对每个片元做发光融合。以我的经验,城市级夜景不用追求每个建筑都物理正确,用贴图规律模拟窗户灯光,再加上重点建筑自发光点缀,观感已经很接近真实夜景。真光源最好少用,几十个点光源同时开,性能会直接崩掉,后面第6章会具体说这个坑。
3.3 动态特效集合:雷达、风场、洪水、箭头线、动态墙体
数字城市交付时,领导喜欢看动态特效。Cesium里能实现的特效组合非常多,我总结几个高频需求。
雷达扫描在Cesium里比较常规,本质是一个圆形几何体叠加一个动态变化的扫描材质,可以用Entity的ellipse加上自定义Material,每帧旋转贴图UV即可。动态墙体对应的是WallGeometry,更适合做工程范围线、保护区、水位上涨这种效果,材质可以用Cesium的PolylineArrowMaterialProperty之类的现成材质,也可以写一个Fresnel效果的Shader,让墙体从下往上循环发光。洪水淹没这个场景我做过,方法是创建一个多边形水面,用动态材质控制水面透明度与波纹,再通过修改顶点高度模拟水位上涨,代码层面主要是动态更新PolygonHierarchy的高度值。
动态风场的实现方式比较多,数据量不大时用ParticleSystem让粒子沿风向流动,视觉效果好;如果风场数据量巨大,比如覆盖整个城市,粒子数量会爆炸,建议改用图片纹理做UV滚动来模拟风向。类似高德的箭头线也可以做,箭头本质是Polyline加上动态纹理材质,把箭头贴图沿着线方向循环滚动,就能形成流动方向感。做这些特效时一定要有个全局开关,因为所有特效全开的情况下,DrawCall数量会迅速拉高,低端机器立刻掉帧,后续优化很麻烦。
3.4 大模型与海量标注性能
数字城市项目做到后面,性能瓶颈一般出现在两个地方:一是模型面数太大,二是标注点太多。Cesium里的性能优化核心是区分Entity和Primitive两种API的使用场景。
Entity是高层API,使用方便,适合数量少、交互多的业务对象,比如点击弹窗的设备点、飞行中的飞机。但每个Entity内部会创建独立的DrawCommand,几百个Entity还好,上千个就会明显卡顿。大量同类标注点位、建筑面、路灯模型,应该用Primitive或者Instanced3DModel来批量绘制,本质上是一次性把多个实例合并到同一个绘制批次里,减少状态切换。
标注避让是另一个大坑。很多项目把大量标注点直接摆在地图上,结果文字互相遮挡,根本没法看。Cesium的LabelCollection可以做简单的距离缩放和锚点偏移,但真正的避让需要自己做碰撞计算。我一般会先用空间索引把密集区域的标注抽稀,再给标签设置PixelOffset,配合视野缩放控制标签显隐,效果比一窝蜂全显示要好得多。
4. 可交互与可视化编辑:从展示到“可操作的场景”
4.1 绘制几何:矩形、多边形、箭头线、切割面
数字城市后台编辑器里最基础的能力是绘制图形。用户要在地图上画一个范围框、圈一个禁飞区、标注一个围栏。我用Cesium的ScreenSpaceEventHandler监听鼠标事件,自己封装了一套绘制工具。画矩形比较简单,鼠标按下就是矩形的起点,拖动过程实时更新Rectangle,松开鼠标确认结束。画多边形稍微复杂一点,需要记录每个点击点,用PolygonHierarchy构建面,并在最后双击闭合。
箭头线这种带方向的线是很多业务的核心展示,Cesium没有现成的箭头线绘制工具。我用的是路径生成算法:由用户点出折线,前端根据折线的转折角度生成带箭头的线条几何体。业务数据上,箭头线保存的是折线坐标序列,渲染时再生成箭头样式,这样后端库里的数据不会因为样式改变而失效。
切割面的场景比较特殊,比如用一条已知线把某个图斑切开。这个算法在Cesium里直接做不高效,我建议交给后端处理。前端只负责把折线坐标传给后台,后台用turf.js这类几何库做Polygon的difference或intersect运算,把切割结果返回GeoJSON,前端再重新渲染。前端核心是交互体验,几何运算尽量下沉到后端,性能更好,逻辑也更清晰。
4.2 模型节点控制:塔吊、车辆、船舶动起来
数字城市里除了静态建筑,还要让设备动起来。Cesium加载glTF/GLB模型后,可以控制模型的内部节点,也就是模型骨骼树里的某个部件做旋转、位移、缩放。这个能力在做塔吊、机械臂、雷达天线、舱门动画时特别有用。
Cesium里控制节点主要通过model.nodeTransformations属性实现,传入一个节点名称和Matrix4变换矩阵。比如塔吊的吊臂节点需要旋转,只需拿到塔吊所在的Entity,然后对该节点设置旋转矩阵即可。
entity.model.nodeTransformations['Crane_Arm'] = Cesium.Matrix4.fromRotationTranslation( Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(angle)), Cesium.Cartesian3.ZERO, new Cesium.Matrix4() );需要注意,nodeTransformations只有在glTF模型内部确实存在这个节点名称时才生效,所以模型建模时最好规范命名节点。后端如果下发设备状态数据,比如塔吊当前角度,前端可以订阅推送并实时更新矩阵,这样就实现了业务数据联动驱动的可视化。
4.3 可视化编辑器:图层树 + 属性面板 + 画布操作
可视化编辑器是我这个项目里投入最大的部分,它决定了用户能不能真正“编辑并保存”。我的编辑器由三个核心别面组成:左侧图层树、右侧属性面板、中间三维画布。
图层树负责管理场景中的所有图层和实体。每个图层有显隐开关、透明度滑条、图层类型标识,底层对应的是一个图层对象数组。右侧属性面板负责展示选中实体的所有可编辑属性,包括位置、旋转、缩放、颜色、线宽、透明度、动画速度、弹窗标题、数据字段等。这是可视化编辑的核心,因为用户改的就是这些JSON字段,面板只是把这些字段映射成表单控件。
中间画布负责几何操作。选中一个实体后,可以拖拽一个位置手柄来移动它,或者通过旋转手柄调整方向。这里不应该直接对Cesium对象做随机修改,而是统一把每次操作写入一个“场景变更对象”,等用户点保存或发布时,一次性把变更后的场景JSON提交到后端。这样做可以有效避免用户在编辑过程中误操作产生的大量临时状态。
编辑器整体设计好比一个“三维PPT编辑器”,底层工作就是把PPT里每个元素的样式和位置转化为数据。数据模型越规范,编辑器越稳定,这一点在第5章会细说。
4.4 飞行漫游与路线规划
飞行漫游是每次汇报展示的保留节目,Cesium在这块有天然优势。让模型沿轨迹飞行的关键API是SampledPositionProperty,它可以按时间戳采样一组坐标,然后通过Clock驱动实体的位置插值。飞机飞行的业务逻辑是:从后台拿一条包含经纬度、高度、时间戳的航线数组,前端创建Entity后设置model,再把SampledPositionProperty挂到position上,设置viewer.clock的起止时间,启动动画即可。
路线规划更偏业务侧。如果路线是驾车导航类的路网路径,通常由后端路网服务计算,前端拿到路径点序列后,用样条曲线做平滑处理,让相机或者模型沿平滑路径移动。如果只是人工指定的巡检路线,前端绘制工具就可以完成。做这类交互时,我认为一定要区分“相机漫游”和“模型漫游”两种模式。相机漫游是让视角像无人机一样沿路径飞行,适合展示城市全貌;模型漫游是让一个车辆或人物模型沿道路移动,适合模拟业务过程。两种模式的实现路径完全不同,但都可以把路径保存成统一的坐标点数组,后端不关心具体是哪种漫游。
5. 配合后台的可视化编辑保存:场景数据模型与工程实现
5.1 场景数据模型设计
能保存的前提是有一个清晰的数据模型。我最终把整个三维场景抽象成一个场景配置树,顶层是场景描述,包括场景名称、相机初始视角、图层列表。图层区分类型,比如tiles3d图层、entity图层、粒子特效图层、路线图层。每个图层内部再挂具体的实体数组,每个实体有通用属性和类型专属属性。
大致的数据结构可以这样定义:一个JSON对象包含camera和layers两个核心字段,layers数组里每个元素要么是3D Tiles源,要么是Entity列表,要么是特效参数。所有可编辑属性都被显式列出。这个结构的核心价值在于,前端可以通过遍历JSON来重建整个场景,后端也可以通过校验JSON来防止脏数据写入。
{ "sceneId": "scene_001", "name": "滨江新区数字城市", "camera": { "longitude": 116.397, "latitude": 39.908, "height": 800, "heading": 0, "pitch": -45 }, "layers": [ { "id": "layer_buildings", "name": "建筑白模", "type": "tiles3d", "url": "/data/buildings/tileset.json", "visible": true, "opacity": 0.9, "style": { "color": "#2d6cdf" } }, { "id": "layer_markers", "name": "设备点位", "type": "entity", "visible": true, "entities": [ { "id": "marker_001", "type": "point", "position": [116.398, 39.907, 30], "label": "1号摄像头", "color": "#ff6600" } ] } ] }我刚在项目里用JSON直接存整个场景,没有拆成几十张关联表,因为场景树本质上是嵌套结构,JSONB存储最适合。前端编辑器、后端保存、接口回显都基于这棵JSON树,逻辑简单、排查问题也容易。
5.2 后台表结构与接口
后台存储我用的PostgreSQL,场景配置字段用JSONB类型。表结构很简单,核心是scene表:id、name、config_json、version、created_at、updated_at。加version字段是为了处理多人同时编辑时的版本冲突,每次保存version加1,保存时带上旧版本号,后端比对版本不同就拒绝覆盖。
接口设计上,我主要提供三个接口:保存场景、发布场景、获取最新场景。保存场景是把编辑器的草稿JSON提交上来,不一定要立即对外生效;发布场景是把这个JSON标记为正式版本,对外展示用最新发布版本;获取场景是返回某个场景的当前配置。
如果只是做一个场景,一张表就够了。但数字城市项目一般会有多个场景,比如“白天模式场景”、“夜间模式场景”、“汇报模式场景”,所以表设计时要留一个scene_type字段。版本管理的做法是每次保存都生成一条新记录,发布时只发布指定版本,避免误改配置导致线上展示出错。
5.3 前端场景回显
场景回显是整个编辑保存闭环的最后一环,也是最容易出问题的环节。保存后的JSON要能完整还原成三维场景。我的前端解析流程是:进入页面后先请求场景JSON,清空当前viewer的entities和imageryLayers,但保留底图;然后遍历layers数组,根据每个图层的type字段调用对应的加载逻辑;所有图层加载完毕后,根据camera字段设置相机初始视角。
需要注意的坑是异步加载。3D Tiles大场景加载需要时间,如果用户在场景还没加载完时就点了保存,保存的JSON里可能处于一个半完成状态。我建议在回显过程中加一个“场景初始化中”的遮罩,等关键图层加载完成后再隐藏。如果加载失败了,不要直接白屏,给出错误提示和重试入口。这样就算3D Tiles源临时不可用,编辑后台也不至于崩溃。
对于模型和贴图资源,回显时要处理相对路径和绝对路径。我的做法是前端把所有资源地址统一存储为后端返回的相对路径,通过一个资源前缀统一拼接,避免因为IP端口变化导致资源加载失败。
5.4 多人协同编辑与权限
多人同时编辑一个数字城市场景,在交付阶段是很常见的需求,因为开发人员、运管人员和甲方领导可能同时在编辑器里操作。最开始我直接让每个人保存后覆盖写,结果出现过A把B修改的夜景灯光覆盖掉的惨案。后来改成乐观锁方案:场景配置带version字段,保存时前端带旧版本号,后端发现版本不匹配就返回冲突,前端提示用户刷新或合并。
实际操作上,我不建议在三维编辑器里做太复杂的多人实时协同,成本极高。简单场景用乐观锁加操作日志就够。操作日志表记录用户、时间、操作内容、涉及图层,管理员可以回看是谁改坏了场景。权限控制上,后端接口要校验用户可编辑的图层范围,前端只是做隐藏展示,真正的权限校验必须在后端完成,否则随便调接口就能改数据。
6. 上线前的一堆坑:从开发到交付的实测记录
6.1 “identifier 'cesium' has already been declared”
这个报错我在项目集成阶段碰到过一次,页面加载Cesium时控制台直接报变量重复声明。原因是我在index.html里用script标签引入了Cesium的全局版本,组件里又通过ESModule的方式import了Cesium,两个Cesium在全局作用域里产生了命名冲突。排查时一度以为是Cesium库坏了,后来才发现是引入方式重复。
解决方式很简单:统一用模块化引入,npm安装Cesium后在业务组件里import使用,不再挂script标签。如果老项目必须要加载全局版,也要确保整个站点只挂一个Cesium实例,不要在部分页面用全局、部分页面用模块。这个坑在Cesium各版本迁移时容易出现,升级时最好做一次全局搜索,看有没有残留的script引用。
6.2 “
本文还有配套的精品资源,点击获取