简介:面向 GIS 开发者的 SuperMap iClient for 3D 二三维一体化浏览示例包,将二维平面地图与三维立体场景集成在同一交互界面,支持按需自由切换,避免传统 GIS 工作中多窗口、多软件来回切换的繁琐。压缩包内共有 18 个文件,总大小 18.9MB,主要类型包括 JavaScript 脚本(SuperMap.Include.js)、可直接打开的 HTML 演示页面、udb/udd 格式的空间数据库、sxwu 三维场景文件、dat 数据文本以及 docx 说明文档,覆盖从数据加载到场景展示的常用环节,目录结构清晰,适合直接参考。目前已有 445 人学习下载。通过这份示例,开发者可以快速看到二三维切换的落地写法,理解三维建模、图层管理、交互操作与性能优化等关键环节;基于其中的演示页面和空间数据,也能改造出适用于城市规划、环境分析、交通管理等场景的 GIS 应用,节省从零摸索的时间,适合作为入门学习或二次开发的基础模板。 SuperMap iClient for 3D这套东西,我最早接触是在一个智慧园区项目里。需求本身不算复杂:园区一期的几十栋楼要做数字孪生,客户提了一个很实际的要求——既想保留二维地图上那种全局视野和快速标绘的爽快感,又希望在三维场景里能直接看楼宇的体量关系、外立面细节甚至内部管线走向。当时团队里最常见的做法就是两套系统拼在一起,二维一套WebGIS,三维一套WebGL,中间靠点击事件互跳。但实际用下来发现这是个伪需求:用户跳来跳去,视角一乱就不知道自己在哪,标注数据还得维护双份。后来我切换到SuperMap iClient for 3D的平面场景方案,把二三维一体化浏览做进了同一个应用里,整个交互逻辑一下顺了。这篇文章就把我在这个项目里摸出来的经验完整地摊开讲。
1. 先说清楚:平面场景不是"伪三维"
很多刚接触SuperMap iClient for 3D的人会对"平面场景"这个词有误解,以为它是用三维引擎模拟出来的二维效果,或者干脆就是个带高度值的平面图。实际上,平面场景是一个真正运行在WebGL三维渲染管线里的场景,它和传统球面场景的唯一本质区别,就是没有地球曲率参与计算。渲染、光照、相机、裁剪全部都是三维的,只不过地面基准被处理成了一个平面,而不是一个椭球面。
这么设计的好处非常明显。在球面场景里,所有三维数据最终都要被投影到WGS84或者CGCS2000的经纬度坐标上,再叠加上地球曲率进行变换。当你处理的区域只有几平方公里时,这个曲率带来的视觉差异小到可以忽略,但它会引入一堆不必要的计算开销,还会让平面坐标系的业务数据(比如园区CAD总图、施工坐标系下的管线数据)在接入时经历一次投影转换,转换参数稍微对不上,地上建筑和地下管线就整体错位。
平面场景则干脆绕开了这个问题。它允许你直接在平面坐标系下承载三维数据,不用先转成经纬度。这意味着什么?意味着你手里那套用了很多年的二维地图瓦片、CAD总图、正射影像,可以直接作为底图叠加到三维场景里。它和二维地图之间的坐标关系几乎是一一对应的,不需要做重投影。在SuperMap iClient for 3D落地二三维一体化浏览的时候,这一步省掉了大量数据处理的脏活累活。
一个直观的对比表格:
| 对比维度 | 传统球面三维场景 | 平面场景 | 纯二维地图 |
|---|---|---|---|
| 坐标系 | 经纬度坐标,涉及椭球变换 | 平面坐标,可直用投影坐标系 | 平面坐标 |
| 三维模型承载能力 | 完整支持 | 完整支持 | 不支持 |
| 局部区域精度 | 受投影转换影响,可能产生微小偏移 | 与原数据坐标系一致,无转换损失 | 与原数据坐标系一致 |
| 底图叠加 | 需影像重投影 | 可直接叠加二维瓦片/影像 | 原生支持 |
| 适合场景 | 宏观地理信息展示 | 园区、城区、厂区等局部范围 | 传统GIS业务的日常浏览 |
从这个表格就能看出来,平面场景在局部范围内做三维浏览,比球面场景更有优势——它既保留了三维的直观表达力,又不需要承担球面坐标变换带来的额外负担。二三维一体化浏览这个需求,在平面场景下做起来,技术摩擦是最小的。
2. 二三维一体化的核心机制:数据和相机状态的解耦与联动
要把二维地图和三维平面场景在一套界面里无缝衔接,最关键的不是渲染,而是两个视图之间数据和视角的同步机制。我最初以为这是个纯前端UI问题,后来发现真正难的是底层的坐标统一逻辑和相机状态映射。SuperMap iClient for 3D做了一件事很聪明:它把二维视图和三维场景内部的渲染循环当成两个相对独立的对象来管理,同时提供了一套事件系统来同步两者的状态。所以你需要理解的不是某个单一API,而是这套"解耦+联动"的运行机制。
2.1 数据层的统一:同一份数据,两套渲染
在二三维一体化浏览里,最常见的数据类型无非就三种:底图影像、矢量要素、三维模型。传统做法是二维地图请求一套二维服务,三维场景再请求一套三维服务,两套数据源格式不同、坐标基准可能也不同,维护成本极高。
SuperMap iClient for 3D的平面场景在数据层上的处理思路是:底图和矢量数据都走同一套SuperMap iServer数据服务,渲染层再分别适配二维和三维视图。换句话说,你在二维地图上加载了一个矢量面图层,这个图层的数据源和三维场景里加载的矢量面图层可以是同一个。二维视图用二维符号渲染它,三维平面场景用三维挤出或多边形填充方式渲染它。数据只存一份,两边的图层各自消费。这样带来的直接好处是:你在二维视图里做了一个属性查询或者选中操作,三维场景里的对应要素能立刻同步高亮,因为底层数据源里的要素ID是相同的。
2.2 视角同步的数学逻辑
二三维联动浏览,最核心的交互体验就是:我在二维地图上框选一个区域,三维场景的相机就得移动到这个区域上方;我在三维场景里旋转缩放,二维地图上的视野范围矩形也得跟着变。这个过程涉及到一套变换逻辑。
我实际项目里的做法是监听两个视图各自的核心视口变更事件。二维地图视口变化时,取出当前视野的平面坐标范围(左上角坐标和右下角坐标),把这组坐标作为相机目标范围,反算三维场景相机应该所在的高度、朝向和位置。三维场景相机变化时,把相机视野朝向地面投影形成的梯形或矩形范围转换成平面坐标范围,然后设置二维地图的显示范围。
这里有一个容易踩坑的地方:相机俯仰角过大的时候,三维场景的视野投射到地面上是一个不规则的梯形,直接拿这个梯形框做二维地图的视野范围,会让二维地图频繁跳动。我后来加了一个平滑策略——只有当相机俯仰角接近垂直(比如大于60度)时,才把视野投射范围同步给二维地图;俯仰角过小时,只同步中心点和方向,不同步范围。这个策略让两边的联动交互稳定了很多。
2.3 控件与操作模式的天然互补
SuperMap iClient for 3D的二维地图和三维场景控件各自保留了原生交互习惯,这一点反而让一体化浏览更自然。二维地图用鼠标拖拽、滚轮缩放、拉框放大;三维场景用鼠标左键旋转、右键平移、滚轮缩放。用户不需要刻意区分两套操作逻辑,因为系统内部做了一件事:默认给三维场景绑定的是"平面视图操作模式",在这种模式下,相机始终默认保持垂直俯视,拖拽时执行的是二维式的平移而不是三维式的旋转。只有用户主动切换到漫游模式时,三维场景的相机才会被释放成自由视角。
这个设计非常贴合实际需求。园区管理平台里的用户大部分是物业人员和运维人员,他们习惯二维地图的简单操作方式。如果三维场景一进来就是自由视角,很多人会被绕晕。平面场景提供了"二维操作习惯直接过渡到三维浏览"的可能性,让用户没有学习成本地完成从二维到三维的视角切换。
3. 落地方案:一个可复用的平面场景二三维一体化浏览架构
讲完原理,直接上实操。这里我以SuperMap iClient for 3D在Vue前端项目里的集成方式为例,给出一个可以直接套用的架构设计。
3.1 场景初始化的分层思路
我把整个一体化浏览界面分成三层:底层的三维场景容器、覆盖在其上的二维地图容器、最上层的联动状态控制层。三维场景作为背景层承载立体数据表现,二维地图作为一个透明浮层挂载在界面的左下角,用来保持全局视野。这种布局比左右分栏更符合业务直觉——用户始终盯着大的三维主视图,需要定位全局时扫一眼小地图,而不是在两个等宽视图之间来回切换。
初始化的顺序也很关键。SuperMap iClient for 3D的三维场景初始化会创建WebGL上下文,这个过程如果和二维地图的初始化并行发起,在某些浏览器版本上会出现资源竞争。稳妥的做法是:先初始化三维平面场景,等它的核心视图对象加载完成之后,再初始化二维地图浮层。
3.2 联动状态管理的核心逻辑
联动状态层,我用的是一个独立的状态管理器,维护以下核心字段:
- 当前同步开关状态(是否开启双向联动)
- 三维场景相机当前的位置、朝向、俯仰角
- 二维地图当前视野范围(左上角坐标、右下角坐标)
- 当前生效的数据图层的可见性状态
- 当前选中的要素ID
联动逻辑按下面的流程执行:
- 用户在三维场景里进行拖拽、缩放、旋转操作,三维场景的相机状态触发变更事件。
- 状态管理器在事件响应函数里读取最新的相机参数,计算地面投影范围。
- 将投影范围转换为平面坐标矩形,调用二维地图的视野设置方法,同时更新浮层上显示的视野范围矩形框。
- 用户操作二维地图浮层时,反向执行:读取二维地图的视野范围,计算相机应该所在的高度和中心点,调用三维场景的相机飞行或直接定位方法。
这一步的代码核心是用一个状态管理器做中转,而不是让两个视图对象直接互相调用。好处是以后如果要在移动端或者大屏上增加第三个视图(比如鹰眼图),只需要对接状态管理器,而不用改动两套视图的联动代码。这个设计让项目后期扩展维护轻松很多。
3.3 数据联动:从选中到高亮的完整链路
一体化浏览不能只同步视角,数据和业务操作的联动同样重要。我在园区项目中做了三个典型的业务联动场景:园区楼栋选中高亮、管线定位巡航、设备状态查询。每个场景的联动链路是一致的:
- 二维地图点击楼栋面要素,触发要素选中事件,拿到要素的唯一标识(比如楼栋编号)。
- 状态管理器记录当前选中ID。
- 三维场景侧监听选中ID变化,在对应的三维模型图层中查找同一个ID对应的实体,切换其材质颜色或开启高亮描边。
- 同时,三维场景的相机平滑移动到被选中楼栋的斜上方。
这里有个务必注意的细节:二维地图的矢量面要素和三维场景里的模型实体必须是同一个空间数据源,而且属性字段中得有一个共同的唯一标识字段。如果你让三维模型单独维护一套ID映射表,初始开发可能感觉不到问题,但后续数据更新时ID很容易对不上,高亮就会失灵。所以我在项目的开始阶段就坚持把二维矢量面和三维模型库的ID统一,宁可前期多花点时间做数据清洗,也不在后期维护双份映射。
4. 平面场景的踩坑实录:投影、层级与事件冲突
工具链在理论跑通之后,真正的挑战往往来自实际操作中的各种"意外"。这部分我挑几个最具代表性的问题,还原排查过程和最终的解决方式,帮后来的人少走弯路。
4.1 瓦片底图在三维平面场景里的偏移和拉伸
项目进行到第三周,我遇到一个诡异现象:二维地图上底图影像和矢量路网贴合得很准,但切换到三维场景之后,底图影像的某些区域出现明显的偏移,越靠近边缘越严重。一开始我怀疑是数据和投影问题,反复核对了数据源坐标系,发现二维和三维加载的明明是同一个影像服务。
排查到最后,问题出在底图影像的金字塔层级设置上。三维平面场景渲染瓦片底图时,需要根据场景相机的高度自动计算应该加载的瓦片层级。如果最大可加载层级设置得比原始影像的层级小,三维场景就会把低层级瓦片强行拉伸显示,导致看起来像"偏移"的扭曲。尤其当相机俯仰角比较小、视线接近水平方向扫过地面时,低层级瓦片的拉伸感会被进一步放大。
解决方法是:启动场景时根据业务需要的最大显示精度,手动设置三维场景底图图层的最大可见层级,并开启瓦片预加载。同时,把二维地图所需层级和三维场景所需层级分开配置。因为二维地图拉近距离时的精度要求和三维场景并不完全一致,共用一套层级配置容易让某一方显示效果被拖累。
4.2 联动过程中的事件死循环
双向联动刚做完的时候,我发现每次拖动三维场景,二维地图也跟着挪位置,然后三维场景又收到一次相机变化事件,又触发一次定位,整个画面轻微抖动。偶尔还会出现"你明明在拖三维,三维却突然自己飞回某个位置"的失控现象。这是一个典型的事件循环问题:A视图状态变化导致B视图状态变化,B的变化反过来又触发A的视图状态更新,如果更新后的值不一致,就会互相触发。
排查链路很清楚:我在两个视图的状态变更事件监听入口分别加了日志,记录触发时间和关键参数。日志显示,三维场景每次收到联动定位指令时,相机事件至少被触发了两次,一次是用户操作引起的,一次是定位程序写入引起的。而程序写入的相机参数和用户操作之后的相机参数存在细微的数值误差,这微小的差异就让联动逻辑误以为状态发生了新变化。
解决办法是在状态管理器里增加一个静默更新标记:当前方触发的是联动更新(非用户交互)时,置一个标志位,后续视图状态变更事件里检测到这个标志位后,直接跳过触发下一步联动。等一轮联动结束,标志位自动清零。同时在程序写入相机参数时,对坐标值做一次四舍五入的归一化处理,消除浮点数误差带来的连锁反应。这个方法虽然简单,但实测稳定可靠,再也没出现过抖动。
4.3 三维场景相机定位精度和二维视野范围的换算误差
联动过程中,相机定位和视野范围换算之间存在一个固定的精度损耗。因为三维场景的内部相机参数计算是基于浮点的,而二维地图视野范围用的是平面坐标的整数或定点数。当场景范围跨越数公里、比例尺超过一定程度时,几米的换算偏差在屏幕上就只有几个像素,常规浏览无感。但一旦你要在二维地图上做精确的标绘或者依据视野范围进行空间查询,这个偏差就会导致查询结果出现边缘丢漏。
我最后采用的方案是:联动同步时使用同一套坐标数据类型,禁止两边的坐标值发生类型转换。三维场景的相机投影范围输出后,先统一格式化为和二维地图相同的数据类型,再传入二维视野设置逻辑。另外搭配一个50至100毫秒的节流处理,避免高频率联动时坐标值反复截断产生的累积误差。经过这一轮调整,边缘丢漏的查询类问题基本消失。
5. 性能优化与发布前检查
一体化的双视图意味着两套渲染体系同时工作,如果数据量再叠加上去,性能压力是单视图方案的好几倍。这块优化如果没有做好,用户打开页面前五分钟就会流失。我分享几个实测后确认有效的优化点。
5.1 合理规划三维场景的显示层级和体量
平面场景和球面场景相比,少了一层地球曲率的约束,但模型加载的体量控制依然是个大问题。园区项目初期,我把所有楼栋的精细模型全部加载进场景,结果加载耗时接近半分钟,而且交互掉帧严重。后来采用了明确的分级显示策略:
- 相机高度在500米以上时,只加载简模和楼栋外轮廓体块。
- 相机高度在100至500米之间时,加载中等细节模型(带主要窗户贴图)。
- 相机高度在100米以下时,才加载完整精细模型和内部管线。
这个策略的实现方式是在场景相机高度变化事件中动态控制模型图层的可见范围。SuperMap iClient for 3D的图层系统支持设置可见距离范围,我把三层模型分别放进三个图层,给每个图层配好显示高度区间,渲染时引擎会自动根据相机高度裁剪不可见范围的图层。实际效果是加载时间从30秒降到了8秒,交互流畅度提升非常明显。三维场景最忌讳的就是"把所有细节一口气展示给用户",细节应该随着用户的靠近逐步浮现,这才符合人的认知习惯。
5.2 二维浮层地图的渲染策略
共享同一套数据源之后,二维浮层地图的每一次视野变化都会重新请求瓦片数据。如果用户的实时联动频率很高,二维浮层的瓦片请求就会非常密集,不仅拖慢自身显示,还会挤占三维场景模型加载的带宽。我在项目里给二维浮层的瓦片加载加了两层保护:
- 视野变化停止后300毫秒才发起瓦片加载请求,联动过程中只显示当前已加载的瓦片缓存。
- 设置二维浮层的最大显示层级,一般比独立二维地图低两个层级即可,因为浮层地图的角色是提供全局定位,而不是精细浏览。
这两层保护让网络请求量下降了大概一半,三维场景的模型加载速度也得到提升。很多开发者容易忽略这一点:一体化浏览里,二维地图是辅助视角,它不应该抢走主场景的资源。
5.3 发布前的浏览器兼容与功能自查
SuperMap iClient for 3D底层依赖WebGL,浏览器的兼容性问题主要集中在显卡驱动、WebGL版本支持和GPU内存占用上。发布前我习惯做一轮全流程自查,按这个清单走一遍:
- 用两台不同显卡性能档位的台式机、一台集成显卡笔记本,分别打开应用,检查三维场景是否正常渲染,是否出现黑屏或贴图闪烁。
- 在无GPU硬件加速的虚拟机里启动应用,确认能降级为软件渲染模式,不会白屏崩溃。
- 长时间挂机30分钟,观察GPU内存是否持续上升不释放,这通常指向纹理或实体资源未正确销毁。
- 在窗口缩放和浏览器标签页切换后,确认三维场景画布大小自适应正常,不出现拉伸变形或事件坐标偏移。
- 检查控制台,确认没有第三方库版本冲突导致的WebGL上下文丢失报错。
最后一项尤其值得注意。SuperMap iClient for 3D对WebGL上下文的创建有较高要求,如果页面里同时加载了其他动态3D库(比如ECharts GL、Three.js),且它们也创建了WebGL上下文,部分浏览器会因为上下文数量超限导致后创建的场景渲染失败。我处理过不止一次这样的问题。管理好页面里三维资源的创建顺序和销毁时机,是稳定运行的前提。
6. 平面场景二三维一体化还能扩展什么
完成园区项目之后,我又在后续工程里把同样的架构复用到其他需求上,发现这套"平面场景+二维浮层+状态管理器"的组合,可以承载很多超出初始预期的能力。比如建筑群级的专题分析,可以在三维平面场景里做楼栋阴影遮蔽模拟、视线通视分析和室内外一体漫游;又比如接入IoT设备实时数据后,设备状态的定位和告警联动可以完全复用之前做好的选中高亮链路,只是数据源从空间数据换成了实时数据流。
如果你也是第一次接触SuperMap iClient for 3D的平面场景,我的建议是:不要一开始就扎进细节API里去抠每个参数,先把"数据层统一、视角层联动、状态层管理"这三层架构想清楚。绝大多数一体化浏览卡壳的问题,追到根上都是这三层之间的职责没有分干净。把这个骨架搭稳了,后面增加功能只是往上挂新的业务模块而已。二三维一体化浏览,真正难的地方不在于做不做得出一个三维场景,在于二维和三维之间的那层逻辑连接,是否牢固可靠。
本文还有配套的精品资源,点击获取