☰
三维智慧城市系统实战:Cesium与SuperMap融合开发指南
2026/10/25 2:59:35 网站建设 项目流程

简介:这是一套面向三维WebGIS开发者与智慧城市项目实践者的Cesium三维可视化工程,兼容超图SuperMap三维服务,适合具备一定前端与WebGL基础、希望快速搭建三维城市场景的读者参考。工程内含智慧城市、警情模拟、水积警情模拟三个独立场景,每个HTML对应一个可运行示例,可直接在VSCode或HBuilder中打开运行,后台三维服务调用超图在线范例场景,所有3D模型均已内置,无需额外下载素材。压缩包共约2000个文件,涵盖png、jpg等贴图资源,js脚本、css样式与json配置,以及s3m、gltf、glb等三维模型数据,另有kml、geojson、terrain、wasm等地理与渲染相关文件,整体约202.4MB。目录中Build、examples与cesium-d3kit.js、city系列脚本构成核心框架,便于按模块查阅与二次开发。目前已有2355人学习下载,适合作为三维智慧城市可视化项目的起步模板与场景搭建参考。

1. 三维智慧城市系统:从需求到选型,为什么是 Cesium 加 SuperMap

做三维智慧城市项目,绕不开一个核心矛盾:前端要好看、要流畅,后端要能接住 GIS 平台沉淀的空间数据。很多团队一开始用游戏引擎做可视化,效果确实惊艳,但一碰到真实地理坐标系、倾斜摄影模型、地下管线、实时视频融合,立刻翻车。另一条路是直接用 GIS 平台自带的三维客户端,数据接入稳了,但定制交互和视觉表现又受限于平台能力。这套方案要解决的,就是在这两者之间找一个平衡点——用 Cesium 做三维 WebGL 渲染底座,兼容超图 SuperMap 的三维服务,让前端既有自由度,又能直接消费 GIS 平台已经发布好的三维缓存、地形、影像和空间查询能力。适合正在做三维智慧城市毕设、课程设计,或者需要交付一套可演示、可扩展的三维 GIS 方案的读者。

2. 总体架构设计:前后端分离与三维服务分层

2.1 四层架构的职责划分

这套系统我一般会拆成四层:数据层、服务层、渲染层、应用层。数据层负责存放倾斜摄影 OSGB 数据、地形瓦片、影像瓦片、矢量面数据以及业务属性数据。服务层由 SuperMap iServer 承担,把三维瓦片、地形、影像、数据查询、空间分析统一发布成 REST 或 WebSocket 服务。渲染层就是 Cesium 的 Viewer 加上自定义的图层管理、实体管理、交互工具。应用层则是具体的智慧城市业务面板,比如楼宇分层、管线开挖、摄像头调阅、人口热力。

这样分的好处是,三维渲染和 GIS 服务解耦。Cesium 只负责“画”,SuperMap 负责“算”和“管”。前端不需要直接解析 OSGB 或 S3M 格式,只需要按约定的 URL 去请求瓦片或查询接口。后期如果换渲染引擎,服务层不用动;如果换 GIS 平台,渲染层也不用大改。

2.2 三维服务接入方式与坐标系约定

Cesium 原生支持 3D Tiles,而 SuperMap 的三维服务通常输出 S3M 或 3D Tiles 格式。实际对接时,优先让 iServer 把三维缓存发布成 3D Tiles,这样 Cesium 可以直接用Cesium3DTileset加载,兼容性最好。如果平台版本只支持 S3M,那就需要引入 SuperMap 提供的 Cesium 扩展包,或者通过 iServer 的 REST 接口做一层转换。

坐标系是血泪经验最多的地方。Cesium 内部用 WGS84 地理坐标,SuperMap 数据可能是 CGCS2000 或地方坐标系。我的做法是:所有服务发布前,在 iServer 里统一转成 EPSG:4326 或 EPSG:4490,前端不再做动态投影。如果确实需要地方坐标系,就在 Cesium 里用Cesium.CustomDataSource配合Cesium.Transforms做局部偏移,但偏移参数必须由后端下发,不能写死在前端。

// Cesium 加载 SuperMap iServer 发布的 3D Tiles 服务 const viewer = new Cesium.Viewer('cesiumContainer', { terrainProvider: await Cesium.CesiumTerrainProvider.fromUrl( 'http://localhost:8090/iserver/services/3D-terrain/rest/realspace/terrain' // 地形服务地址 ), imageryProvider: new Cesium.WebMapTileServiceImageryProvider({ url: 'http://localhost:8090/iserver/services/map-img/rest/maps/img/wmts', // 影像瓦片服务 layer: 'img', style: 'default', format: 'image/png', tileMatrixSetID: 'c' }) }); // 加载三维模型瓦片 const tileset = await Cesium.Cesium3DTileset.fromUrl( 'http://localhost:8090/iserver/services/3D-building/rest/realspace/datas/building/tileset.json' ); viewer.scene.primitives.add(tileset); viewer.zoomTo(tileset);

这段代码里,CesiumTerrainProvider负责地形,WebMapTileServiceImageryProvider负责影像底图,Cesium3DTileset负责建筑白模或倾斜摄影。三个 URL 都指向 iServer 的不同服务。参数上,tileMatrixSetID要和 iServer 里 WMTS 能力文档中的一致,通常是c或EPSG:4326。如果地形服务加载失败,先检查 iServer 是否开启了地形缓存,以及 Cesium 的terrainProvider是否在Viewer初始化时异步等待完成。

2.3 模块划分与数据流

前端模块我通常分成五块:场景初始化、图层控制、实体交互、空间查询、业务面板。场景初始化负责创建 Viewer、加载地形影像和三维瓦片。图层控制用树形结构管理不同数据源,支持显隐、透明度、叠加顺序。实体交互处理鼠标点击、悬停、绘制点线面。空间查询调用 iServer 的 REST API,做缓冲区分析、通视分析、淹没分析。业务面板则根据查询结果渲染图表和属性表。

数据流是单向的:用户操作触发查询请求,请求发到 iServer,返回 GeoJSON 或 JSON 结果,前端解析后生成 Cesium 实体或更新面板。反向的数据流只有一种:用户在面板上修改属性,通过 POST 请求写回数据库,然后刷新对应图层。这种单向数据流让调试变得简单,出问题先看请求有没有发出去,再看返回结果对不对,最后看渲染有没有生效。

3. 核心功能实现:三维场景、空间查询与业务联动

3.1 三维场景初始化与性能调优

场景初始化不只是 new 一个 Viewer。智慧城市项目动辄几十平方公里的倾斜摄影模型,不加控制直接加载,浏览器内存直接爆掉。我一般会做三件事:第一,开启 Cesium 的requestRenderMode,只在需要重绘时渲染,降低 GPU 占用;第二,设置maximumScreenSpaceError,控制瓦片加载精度,通常设 16 到 32 之间,太大模型粗糙,太小加载卡顿;第三,用Cesium3DTileset的maximumMemoryUsage限制内存,默认 512MB,可以根据机器配置调到 1024MB。

viewer.scene.requestRenderMode = true; // 按需渲染 viewer.scene.maximumScreenSpaceError = 16; // 屏幕空间误差 tileset.maximumMemoryUsage = 1024; // 最大内存占用 MB tileset.style = new Cesium.Cesium3DTileStyle({ color: { conditions: [ ['${height} > 100', 'color("red")'], ['${height} > 50', 'color("orange")'], ['true', 'color("white")'] ] } });

样式这段是按建筑高度着色,${height}是三维瓦片里自带的属性字段。如果模型没有这个字段,需要先在 iServer 里做属性挂接,或者在前端用tileset.getProperty动态计算。性能调优没有银弹,通常的做法是先用 Chrome Performance 录一段,看是瓦片请求慢还是渲染帧率低,再针对性调整。

3.2 空间查询与 SuperMap REST API 对接

空间查询是智慧城市系统的核心交互。用户在地图上画一个多边形,系统要返回范围内的建筑、管线、人口等信息。SuperMap iServer 提供了 REST 空间查询接口,前端用fetch或axios发 POST 请求,请求体里带几何对象和查询参数。

// 多边形缓冲区查询示例 const queryParam = { queryMode: 'BufferQuery', queryParameters: { name: 'building', bufferDistance: 100, // 缓冲距离 100 米 spatialQueryMode: 'INTERSECT' // 相交查询 }, geometry: { type: 'Polygon', points: [ { x: 116.397, y: 39.908 }, { x: 116.407, y: 39.908 }, { x: 116.407, y: 39.918 }, { x: 116.397, y: 39.918 } ] } }; const response = await fetch( 'http://localhost:8090/iserver/services/data-building/rest/data/featureResults.json', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(queryParam) } ); const result = await response.json(); // result.features 是 GeoJSON 格式的要素集合

参数说明:queryMode选BufferQuery表示缓冲区查询,spatialQueryMode可以是INTERSECT、CONTAIN、WITHIN等。bufferDistance单位是米,和坐标系有关,如果是地理坐标系,iServer 会自动按米做近似计算。返回的features可以直接用Cesium.GeoJsonDataSource.load加载到场景里,也可以手动遍历生成实体。如果查询结果为空,先检查几何对象的坐标顺序是不是经度在前、纬度在后,这是最常见的踩坑点。

3.3 业务面板与三维场景的联动

业务面板和三维场景的联动,我一般用事件总线做解耦。Cesium 的ScreenSpaceEventHandler监听鼠标点击,拿到拾取的实体 ID,然后通过事件总线通知面板组件加载对应属性。面板上的操作,比如切换楼层、显示管线,也通过事件总线反向通知场景更新样式。

// 点击拾取实体 const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); handler.setInputAction((movement) => { const picked = viewer.scene.pick(movement.position); if (Cesium.defined(picked) && picked.id) { const entityId = picked.id.id; eventBus.emit('entity-selected', entityId); // 通知面板 } }, Cesium.ScreenSpaceEventType.LEFT_CLICK); // 面板控制楼层显隐 eventBus.on('floor-change', (floor) => { tileset.style = new Cesium.Cesium3DTileStyle({ color: { conditions: [ [`${floor} === ${floor}`, 'color("cyan")'], ['true', 'color("white", 0.3)'] ] } }); });

这种模式的好处是,面板组件不需要知道 Cesium 的存在,场景也不需要知道面板怎么渲染。后期换 UI 框架,或者把面板拆成独立微前端,都不会影响三维核心逻辑。唯一要注意的是事件命名要统一,我习惯用模块-动作的格式,比如entity-selected、floor-change,避免不同开发者起冲突。

4. 避坑与常见问题:坐标系、瓦片加载与内存泄漏

4.1 模型位置偏移或漂浮

现象:三维建筑模型加载后,整体偏移几百米,或者悬浮在地形上方。原因通常是数据坐标系和 Cesium 场景坐标系不一致,或者地形服务和模型服务的高程基准不同。解决:先在 iServer 里确认模型数据的坐标系,统一转成 EPSG:4490。如果地形有高程偏移,用tileset.modelMatrix做平移修正,但修正值必须从后端配置读取,不能硬编码。

4.2 瓦片加载慢或浏览器卡死

现象:场景旋转时瓦片一直加载,帧率掉到个位数,严重时浏览器无响应。原因一般是maximumScreenSpaceError设得太小,或者同时加载了多个高精度图层。解决:把maximumScreenSpaceError调到 16 到 32,关闭暂时不需要的图层,用viewer.scene.globe.show控制底图显隐。另外,iServer 的瓦片服务要开启缓存,第一次访问慢是正常的,后续应该走缓存。

4.3 空间查询返回空结果

现象:前端画了多边形,调用查询接口返回features: []。原因可能是几何坐标顺序反了,或者查询图层名和 iServer 里发布的数据源名称不一致。解决:先检查 GeoJSON 的坐标是不是[经度, 纬度],再检查queryParameters.name是否和 iServer 服务列表里的数据集名称完全一致。如果还不行,用 iServer 自带的查询页面测试同一组参数,排除前端传参问题。

4.4 内存泄漏与页面崩溃

现象:长时间运行后,浏览器内存持续上涨,最终页面崩溃。原因通常是 Cesium 实体只增不减,或者事件监听没有销毁。解决:在组件卸载时调用viewer.entities.removeAll()、viewer.scene.primitives.removeAll(),并移除所有ScreenSpaceEventHandler。如果用了 Vue 或 React,在beforeDestroy或useEffect的清理函数里做这些事。我一般会封装一个destroyViewer方法,统一处理资源释放。

4.5 服务跨域与 HTTPS 混合内容

现象:本地开发正常,部署到服务器后三维服务请求被浏览器拦截。原因通常是 iServer 没有开启 CORS,或者 HTTPS 页面请求了 HTTP 服务。解决:在 iServer 的web.xml里配置 CORS 过滤器,允许前端域名。如果页面是 HTTPS,所有服务地址也必须升级到 HTTPS,否则浏览器会阻止混合内容。这个坑在答辩演示时特别致命,建议提前在目标环境完整跑一遍。

5. 验收与答辩:怎么证明这套三维智慧城市系统真的能跑

验收三维智慧城市系统,不能只看截图。我一般会准备一套演示脚本,按顺序走一遍:先展示地形和影像底图加载,证明基础场景可用;再加载倾斜摄影模型,旋转缩放,证明三维瓦片服务正常;然后画多边形做缓冲区查询,展示返回的建筑列表和属性面板;最后演示一个业务联动,比如点击楼宇显示楼层信息,切换楼层高亮。每一步都要有明确的成功标准,比如“模型加载后无偏移”“查询返回要素数量大于零”“面板数据与三维实体一致”。

答辩时,评委最容易问三个问题:坐标系怎么统一的、性能怎么优化的、数据怎么更新的。坐标系问题,回答“服务端统一转 EPSG:4490,前端不做动态投影”。性能问题,回答“按需渲染加屏幕空间误差控制,瓦片走 iServer 缓存”。数据更新问题,回答“通过 iServer 的数据服务接口做增量更新,前端定时刷新或手动触发”。如果评委追问 SuperMap 和 Cesium 的版本兼容性,就如实说“以 iServer 发布 3D Tiles 为准,S3M 需要额外扩展包”,不要编造版本号。

验收表格可以这样设计:

验收项操作步骤预期结果实际结果
地形加载打开页面,观察地形起伏地形显示正常,无裂缝通过
模型加载加载建筑瓦片,旋转缩放模型位置正确,无漂浮通过
空间查询画多边形,执行缓冲区查询返回要素列表,面板显示通过
业务联动点击楼宇,切换楼层三维高亮,面板同步通过
性能表现连续旋转 1 分钟帧率不低于 30fps通过

从那以后我每次做三维项目,都强制走一遍“坐标系确认、服务连通性测试、内存释放检查”这三步,不管工期多紧都不跳过。希望这套方案能帮到你,需要完整工程文件和配置说明的,可以按文末方式获取。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询