
简介一套基于Vue.js的SuperMap与Cesium集成项目面向GIS开发工程师、前端工程师以及三维可视化学习者围绕“倾斜摄影数据的三维地图展示”这一场景解决SuperMap空间数据服务、Cesium渲染引擎与前端框架混合使用时的工程配置、依赖管理和场景搭建问题。压缩包为RAR格式共560个文件整体约25.84MB文件类型包括176个PNG图片、146个JS脚本、56个JPG图片、33个JSON配置、32个CSS样式、3个Vue组件等前端工程文件以及s3m与terrain倾斜摄影模型/地形数据、mp4演示视频和markdown说明文档目录结构清晰便于按模块查阅。目前已有721人学习浏览适合希望快速掌握SuperMap-Cesium三维地图开发或需接入倾斜摄影数据的开发者参考。项目内附完整工程配置覆盖vue.config.js、babel.config.js、browserslistrc等关键文件可以学习Vue CLI的构建与代理设置、Babel的语法转换规则、浏览器兼容范围等内容同时配有示例代码与运行演示可在本地快速启动一个可交互的3D地图场景并了解package.json与package-lock.json在npm依赖管理中的具体作用。 前阵子帮朋友调试一个三维可视化项目对方给的倾斜摄影数据是无人机飞出来的原始OSGB要求用SuperMap产品和Cesium技术栈在网页端展示出来。我一开始觉得这活儿不难毕竟SuperMap的iClient3D for Cesium就是干这个的结果真正上手才发现从数据格式到坐标体系再到加载调优每一步都有暗坑。这篇就从头到尾梳理一遍把我踩过的坑和验证过的做法都写出来给正好在做类似事情的同行做个参考。1. 选型逻辑为什么用SuperMap iClient3D for Cesium而不是原生Cesium1.1 原生Cesium的局限与iClient3D的定位差异先明确一个概念Cesium本身是一个开源三维地球引擎它的社区版和官方版都能加载3D Tiles、地形、影像等数据。那为什么还要用SuperMap封装的版本最核心的原因是数据格式的兼容性。无人机倾斜摄影的常见产出格式是OSGBOpenSceneGraph Binary这是一套分块存储的二进制格式包含模型几何、纹理、LOD层级关系。原生Cesium不直接支持OSGB需要先转换成3D Tiles。而SuperMap iClient3D for Cesium是在Cesium基础上扩展了数据解析能力可以直接加载SuperMap iServer发布的S3MSuperMap 3D Model格式切片也支持3D Tiles。如果你们的倾斜摄影数据已经通过SuperMap iServer处理成了S3M服务那用iClient3D就是最顺的路。另一个实际考虑是团队的技术积累。很多GIS团队之前用SuperMap桌面端做数据处理和制图服务端用iServer发布如果再引入一套纯Cesium的加载链路意味着要维护两套数据管线。用iClient3D延续SuperMap的生态开发语言还是JavaScriptAPI风格和Cesium接近学习成本低很多。1.2 版本选择里的隐藏细节SuperMap iClient3D for Cesium的版本迭代很快不同版本对应的Cesium版本不同API也有细微差异。我建议优先选择SuperMap官网最新的正式版同时注意区分开发版和稳定版。之前遇到过一个奇怪的问题在某个版本里正常使用的scene.addS3MTilesLayerByScp方法升级后就提示销毁了后来查文档发现新版本推荐用new Cesium.SuperMapS3MTilesLayer方式创建图层。如果你负责的项目要求长期稳定运行建议锁定一个经过验证的版本不要频繁跟着小版本更新走。提示iClient3D for Cesium本质是Cesium的超集它继承了Cesium原生的Viewer、Entity、Primitive等API所以你完全可以混合着用——用SuperMap的图层加载倾斜摄影用原生Cesium的接口做标注和交互。2. 数据从哪里来倾斜摄影的格式转换与发布链路2.1 OSGB原始数据需要先处理倾斜摄影的原始成果通常是一个工程目录里面有tile分块文件夹、metadata.xml等文件。这种格式不能直接扔给Web前端加载必须经过切片处理。SuperMap的路线是用桌面端SuperMap iDesktop把OSGB数据生成S3M缓存然后发布到iServer上或者直接把S3M缓存目录作为一个数据源接入iServer。这一步最容易忽略的是坐标系设置。倾斜摄影原始数据的坐标系可能是地方坐标系、WGS84经纬度、CGCS2000高斯投影等不同来源的数据差异很大。在桌面端生成S3M缓存时必须明确设置正确的坐标系否则后面在Web端加载会出现位置偏移或者模型不显示的问题。2.2 两种数据路线的对比我把实际用过的两条路线整理成表格方便你根据自身情况判断路线处理工具前端加载方式优势劣势S3M切片服务SuperMap iDesktop iServerscene.addS3MTilesLayerByScp 或 SuperMapS3MTilesLayer与SuperMap体系无缝衔接分析功能丰富属性查询方便需要完整的iServer授权部署重量级3D Tiles转换第三方转换工具或Cesium实验室Cesium.Cesium3DTileset轻量不依赖SuperMap服务端格式通用需要处理纹理压缩、LOD生成与SuperMap属性联动较弱需要说明的是如果公司已经采购了SuperMap的GIS平台走S3M路线是省心的选择iServer会对S3M做流式传输优化前端加载性能有保障。如果只是临时展示几个倾斜模型不想引入重型的服务端那把OSGB转成3D Tiles用原生Cesium加载配合一个静态文件服务就能跑起来也足够应对中小场景。2.3 投影信息不一致是我遇到的第一道坎我遇到过最典型的情况是iServer里发布的服务显示正常但是前端地图上完全找不到模型浏览器Network面板里能看到3D Tiles的请求在返回结果就是画面上什么都没有。后来排查发现iServer服务的坐标系是CGCS2000高斯投影而前端Cesium场景的底图是Web MercatorEPSG:3857或WGS84EPSG:4326两者坐标系底子不一样三维场景里模型处在一个无法被相机捕捉到的位置。解决办法是在桌面端生成缓存时把坐标系统一转成WGS84的经纬度坐标EPSG:4326或者在iServer发布时选择动态投影。这里强调一下Cesium的地形和影像服务用的是EPSG:4326或者EPSG:3857倾斜摄影服务的坐标系最好和底图保持一致否则后续相机定位、坐标换算都会变得混乱。3. 核心实操加载S3M倾斜摄影图层的最小可用案例3.1 初始化场景时的参数选择拿到一个SuperMap iServer发布的S3M三维切片服务后前端代码其实不复杂。首先创建一个包含底图的Viewer建议把自带控件做裁剪const viewer new Cesium.Viewer(mapDiv, { baseLayerPicker: false, // 隐藏底图切换按钮 geocoder: false, // 隐藏搜索框 timeline: false, // 隐藏时间轴 animation: false, // 隐藏动画控件 infoBox: false, // 隐藏属性弹窗 selectionIndicator: false, // 隐藏选中指示器 shouldAnimate: true });这些默认控件在实际项目中基本用不上隐藏掉可以让界面更干净。注意shouldAnimate: true这个参数S3M图层中如果包含动态效果比如水面波动、扫光特效需要开启动画循环。3.2 添加S3M图层的两种写法旧版API的写法是scene.addS3MTilesLayerByScpconst promise scene.addS3MTilesLayerByScp( http://localhost:8090/iserver/services/3D-terrain/rest/realspace/datas/0/data/Model, { name: oblique-photography } ); promise.then(layer { // 图层加载成功后的回调 console.log(layer); });新版API改成了构造式const layer new Cesium.SuperMapS3MTilesLayer({ url: http://localhost:8090/iserver/services/3D-terrain/rest/realspace/datas/0/data/Model, name: oblique-photography }); scene.layers.addLayer(layer);两种写法我都用过建议以你当前引入的iClient版本对应的文档为准。加载成功之后通常还要调整一下图层的显示参数比如layer.style.fillForeColor可以修改整体填充色layer.maximumScreenSpaceError控制显示精度。3.3 把相机飞到模型位置模型加载了但相机还停留在默认位置怎么办用viewer.flyTo来定位这里的坐标必须是经纬度viewer.flyTo({ destination: Cesium.Cartesian3.fromDegrees(116.391, 39.907, 800), orientation: { heading: Cesium.Math.toRadians(0), pitch: Cesium.Math.toRadians(-45), roll: 0 }, duration: 2 });里面fromDegrees的坐标顺序是经度、纬度、高度我第一次写的时候反过来了结果相机飞到了海面上折腾了半天才发现是坐标顺序问题。这个细节后面细说。3.4 如果数据转成了3D Tiles的加载方式当你的倾斜摄影已经处理成3D Tiles格式时不依赖SuperMap服务也可以加载用原生的Cesium.Cesium3DTileset即可const tileset await Cesium.Cesium3DTileset.fromUrl( http://localhost:8080/tileset.json ); viewer.scene.primitives.add(tileset); viewer.flyTo(tileset);Cesium3DTileset同样支持设置最大屏幕空间误差、动态屏幕空间误差、可见性判断等参数。这种方式更适合轻量化部署的场景不需要启动iServer直接把静态切片文件放到Nginx或对象存储里就能被加载。4. 排坑实录倾斜摄影加载中的常见异常和处理链路4.1 模型加载不出来但请求正常返回这个问题的排查链路可以这样走打开浏览器Network面板确认3D Tiles或S3M的请求是否在持续加载如果请求一直有但场景里没有模型优先检查坐标系是否一致如果请求直接报404检查服务URL和数据目录的发布配置。我那次排查到最后发现是iServer发布的S3M坐标系是CGCS2000 3-degree GK而我在桌面端生成缓存时没有正确配置转换导致模型坐标在WGS84地球上是不存在的位置。解决方案是在SuperMap iDesktop重新生成S3M缓存明确指定为EPSG:4326。4.2 模型位置偏移出现在另一个城市的空中这种情况很多时候不是坐标系错误而是经纬度互换。Cesium中fromDegrees(longitude, latitude, height)依次是经度、纬度、高度而部分GIS平台或者JSON属性表里习惯写成latitude/longitude。如果从数据源中读取经纬度后直接作为两个参数传入很可能交换了顺序模型就跑到千里之外了。比较隐蔽的是X、Y属性也有不同定义。倾斜摄影元数据里X对应经度或东西方向Y对应纬度或南北方向但在高斯投影坐标系下X、Y的含义和经纬度有区别。建议在转换到Cesium的坐标之前先在控制台log一下数值范围常识性判断一下经度是否在76°到134°之间中国范围纬度是否在18°到54°之间超过这个范围的大概率是坐标顺序或投影出了问题。4.3 模型倾斜了90度站立的建筑躺了下来有时候模型能显示但整体旋转了90度看起来建模场景横躺在球面上。这个问题一般出在数据转换时的坐标轴方向处理上。SuperMap的S3M和Cesium的3D Tiles都有约定的坐标轴方向正常情况下Z轴向上。但不同采集设备生成的原始OSGB可能存在坐标系旋转差异需要在数据预处理时通过配置旋转矩阵修正。遇到这种情况我建议先回到桌面端在SuperMap iDesktop中打开原始倾斜摄影数据检查模型在地面是否正常。如果桌面端正常而Web端倾斜那就是切片工具或发布环节的坐标轴转换问题优先在生成缓存时调整坐标旋转参数。如果桌面端也不正常就得回到原始数据源去校正坐标系。4.4 模型加载后直接被地形或者底图淹没倾斜摄影的位置正确但高度被地形遮挡这通常是因为场景中加载了地形数据而地形的高程比模型底部高。解决办法是关闭地形遮挡效果或者调整模型的位置// 关闭地形深度检测 viewer.scene.globe.depthTestAgainstTerrain false; // 或者给模型一个相对地面的高度偏移 tileset.modelMatrix Cesium.Matrix4.fromTranslation( Cesium.Cartesian3.fromDegrees(116.391, 39.907, 50) );有一点要说明一下如果底图是平面坐标系例如某些自定义服务fromDegrees会不适用于其坐标体系这种情况下需要切换到经纬度底图或者先使用Cesium.Cartesian3.fromArray等方式。4.5 场景卡顿、GPU占用过高倾斜摄影的模型精度高、三角面片多加载到前端很容易造成卡顿。遇到这种情况先把视口拉远看模型整体的顶点数和纹理贴图大小是不是超出了一般WebGL的承受范围。在SuperMap iDesktop生成S3M时可以配置LOD分层比例因子、纹理压缩格式比如将RGB纹理转为DXT或WebP这部分配置对性能影响非常大。其次检查代码中是否开启了不必要的高级效果。比如viewer.scene.globe.enableLighting开启后会动态计算日照阴影如果场景里模型数量巨大这个效果会显著拉低帧率。我在实际项目中习惯默认关闭动态光照只在需要炫酷展示时临时开启。5. 进阶玩法动态光照、雷达扫描与水面效果的实践5.1 动态光照的开启与参数调优倾斜摄影展现建筑群轮廓时加上动态光照会让画面立体感强很多。Cesium里开启光照的入口很简单viewer.scene.globe.enableLighting true;但开启后你就得关注阳光方向的时间匹配。Cesium默认的光照是根据场景时钟的时间来计算太阳位置的如果你的viewer.clock时间停在某个时刻光照方向和强度就固定了。想调整成更理想的视觉效果可以设置viewer.clock.currentTime为某个具体时间点再配合viewer.scene.sun和viewer.scene.moon等参数。动态光照对性能确实有影响尤其是低端显卡。如果你的目标用户是大屏展示场景建议先在内网一台弱GPU机器上做压力测试确认帧率满足要求再上线。5.2 雷达扫描效果可视域分析的轻量实现网上看到很多人搜Cesium雷达效果这里说一个常见做法。可视域分析在SuperMap iServer中有对应的分析服务可以在服务端计算完再返回结果。如果不想依赖服务端分析也可以用Cesium的Primitive或Entity画一个扇形扫掠const radarEntity viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(116.391, 39.907, 10), ellipse: { semiMajorAxis: 500, semiMinorAxis: 500, material: Cesium.Color.RED.withAlpha(0.3), classificationType: Cesium.ClassificationType.TERRAIN } });这只是个简单的椭圆示意真正的雷达扫描效果需要动态更新材质参数可以用CallbackProperty返回随时间变化的透明度或扫描角度。要做复杂一点的比如多个扇形区域、扫描动画建议封装成独立的Cesium自定义Material相关文档在Cesium博客里其实有示例。5.3 动态水面的显示效果调整SuperMap的S3M图层中水面效果通常会以模型的一部分出现比如河流面片。前端可以给模型面片赋予动态材质也可以通过Cesium.Material中的Water类型来生成动态水面const waterMaterial new Cesium.Material({ fabric: { type: Water, uniforms: { normalMap: waterNormals.jpg, frequency: 100.0, animationSpeed: 0.01, amplitude: 1000.0, specularIntensity: 50.0, baseWaterColor: Cesium.Color.fromCssColorString(#00a8ff) } } });使用Water材质时normalMap需要一张法线贴图这张图通常是一张蓝紫色的法线纹理Cesium官方示例里会自带一张但实际项目里建议用自己生成的避免版权问题。水面的性能开销取决于波纹频率和贴图大小默认参数在中等画质下问题不大如果和大范围倾斜摄影叠加在一起还是要留意帧率。5.4 用雷达、可视域分析丰富场景的决策参考如果你有SuperMap iServer的分析服务建议优先用服务端的可视域、天际线、阴影分析接口因为服务端分析是GPU并行计算返回的是矢量结果前端只要把结果以Entity或Primitive的方式叠加显示即可稳定性比前端实时计算好很多。前端实时计算的优点是响应快没有网络往返适合交互频繁的演示场景。两者结合的方式是默认用前端做粗粒度交互关键时刻调用服务端做精细分析。6. 环境部署与常见坑位从本地联调到线上发布6.1 本地联调时的跨域配置本地做开发时前后端分离是常态。iServer服务如果是独立部署的前端页面跑在webpack-dev-server里默认端口不同就会产生跨域问题。Cesium请求跨域的资源会直接被浏览器拦截表现是Network里看不到请求或者大量请求报CORS error。解决办法有几个一是在iServer的web.xml里配置CORS过滤器允许指定源二是开发阶段用Nginx做反向代理把/iserver路径代理到iServer真实地址前端代码里用相对路径请求三是iServer本身的配置文件里开启跨域支持。我实际开发中更倾向用Nginx代理的方式因为可以顺便做缓存和HTTPS终结对后续线上部署也有参考价值。6.2 线上部署时的数据体积控制倾斜摄影数据动辄几个GB甚至几十个GB如果直接放到普通Web服务器上首次加载会让用户等到怀疑人生。建议在发布之前做数据的三维缓存压缩同时配置iServer的缓存策略。如果走的是3D Tiles路线要关注tileset.json里的geometricError和子节点content的URL可以通过减小最大屏幕空间误差值来优化加载速度。一个容易忽略的问题是纹理贴图的格式。无人机照片经过建模后贴图通常是几千像素的JPEG或PNG直接在Web端加载会占用大量显存。建议把贴图批量压缩到适中的分辨率比如1024或2048并统一转换成WebGL友好的格式能大幅提升加载和渲染性能。6.3 半路接手的项目如何快速排查前几天有朋友接手一个Cesium项目问我是先看代码还是先看数据。我的建议是先梳理数据链路倾斜摄影原始数据在哪里 → 经过哪些工具处理 → 发布到什么服务 → 前端从哪里请求 → 坐标系分别是多少。这个链路只要梳理清楚80%的显示异常问题都能定位到具体环节。如果链路检查完还没发现问题就在浏览器Console里执行一下viewer.scene.primitives看看当前场景中有哪些Primitive对象再逐个隐藏来定位问题图层。这个方法看起来很原始但非常管用。我在实际项目中最后总结出来的心得是用SuperMap iClient3D for Cesium加载倾斜摄影90%的精力其实不是花在写代码上而是花在数据预处理和服务发布上。坐标系对不对、切片的LOD合不合理、纹理压缩有没有做这些环节直接决定了前端体验。代码层面反而简单上面给的案例基本覆盖了主流程。如果你正在做类似的功能先把数据和服务的链路跑通再回头优化前端展示往往是最省时间的路径。本文还有配套的精品资源点击获取