EarthSDK3三维GIS开发实战:智慧园区可视化项目经验
2026/9/16 9:26:34 网站建设 项目流程

最近在做一个智慧园区可视化的项目,核心是把园区里的建筑楼栋、地下管廊、视频监控点位和门禁设备全部塞进一个三维地球里,方便管理人员在一张图上看全局。技术选型的时候对比了好几套方案,最后定下来用EarthSDK3做三维GIS开发。这个SDK最打动我的一点是它对国内GIS场景的适配度很高,不用自己拼凑一大堆底层库就能把三维地球跑起来,而且中文文档和示例都比较完整,遇到问题翻起来不费劲。整套开发做下来,从初始化地球、叠加影像地形、加载三维模型,到做点击交互和相机飞行,踩了不少坑也沉淀了不少能直接用的经验。这篇内容就是把这些实战过程梳理一遍,给后面想用EarthSDK3做三维GIS的团队或者个人开发者一个可供参考的完整路径。

如果你正打算在Web端做数字孪生、智慧城市大屏、自然资源可视化这类项目,又不想从WebGL底层开始造轮子,那么这篇文章很适合你。整个内容我会从选型思路、环境搭建、图层加载、交互功能到性能优化逐层展开,每一步都有对应的代码示例和参数说明,也会把我在实际项目中遇到的坑和排查思路一并写出来。

1. 项目整体设计与选型思路

1.1 需求拆解:三维GIS项目到底要解决什么问题

很多刚接触三维GIS的人容易陷入一个误区,以为三维GIS就是在页面上显示一个会转的地球,然后往上贴点模型就行。真正做过项目就会发现,业务方要的不是“炫酷”,而是“能用、能查、能分析”。我在这个智慧园区项目里拿到手的原始需求其实只有三条:第一,把园区所有空间数据统一放到一个三维场景中管理;第二,管理人员可以快速定位到某栋楼、某个摄像头,并查看详情;第三,大屏上要稳定运行,不能出现加载慢、浏览器崩溃这种问题。

把需求翻译成技术语言,就变成了四件事:需要支持多源空间数据接入(影像、地形、矢量、倾斜摄影、人工模型),需要提供友好的相机控制和拾取交互能力,需要有较高渲染性能来承载大体量模型,还需要一套清晰的数据组织方式来管理不同来源的图层。这时候选择什么样的SDK,直接决定了后续开发的节奏和交付质量。

1.2 为什么从Cesium、Three.js里选了EarthSDK3

前面先同步一个背景:团队里有人之前用Three.js做过模型展示项目,也有人用Cesium做过卫星轨迹可视化,所以选型时并不是没有基础。我们针对EarthSDK3、Cesium、Three.js分别做了快速原型验证,对比结果非常直白。

Cesium的优势在于太空级数据调度和成熟的3D Tiles生态,适合做全球尺度场景,但它默认的地球渲染风格偏冷,要调出国内GIS项目常见的视觉效果,往往得写不少样式覆盖代码。Three.js自由度最高,可以自己控制渲染管线,但几乎所有GIS能力都得自己造轮子,比如坐标系转换、瓦片加载、地形解析这些,开发周期会拉得很长。

EarthSDK3算是取了一个中间值。它底层的渲染能力不弱,封装出来的API又很贴近国内GIS开发的习惯,像“图层”“标注”“量测”这些概念基本拿到就能用,不用重新理解一套逻辑。还有一个很关键的因素是它的文档和示例代码本土化做得好,项目急的时候这个优势会被放大,遇到问题搜一下或者翻示例就能解决,不用满世界找翻译后的资料。

我整理了一张当时快速验证的对比表,简化后是下面这样的:

对比维度EarthSDK3CesiumThree.js
三维地球基础能力开箱即用,API语义清晰强大但偏底层需要自行集成
国内GIS数据适配对WGS84、CGCS2000、EPSG:4326支持完善支持但需手动配置需自行处理
业务功能封装测量、标注、可视域等有现成示例部分需从示例二次开发几乎全部自建
上手成本中低,文档中文友好较高
大屏项目视觉风格内置多套主题,改起来方便默认风格需较多调整完全自由,需要设计师配合

当然,这不是说EarthSDK3是万能的。在需要处理海量全球级卫星影像、或者需要深度定制渲染管线的场景下,Cesium和Three.js可能更合适。但就“Web端三维GIS业务系统”这个范围来说,EarthSDK3对团队的性价比确实更高。

1.3 整体技术架构和数据流设计

项目整体架构沿用经典的前后端分离模式。前端使用Vue 3构建业务界面,三维部分由EarthSDK3负责渲染,两者通过SDK对外暴露的实例通信。后端负责提供矢量和模型数据接口。数据链路大致是这样的:静态地理数据(建筑白模、影像瓦片、地形)预先处理并上传到对象存储或GIS数据服务中,前端通过图层方式加载;业务数据(摄像头信息、设备状态、工单记录)通过HTTP接口动态获取,再转成三维场景里的标注或模型绑定属性。

设计这套架构时我主要考虑了两点。第一是数据和视图分离,图层只负责“把数据显示出来”,业务属性通过数据关联去绑定,这样更换数据源时不用改动渲染层代码。第二是尽量复用SDK封装好的能力,避免在业务代码里直接操作底层渲染对象,降低后续维护成本。整个项目跑下来,这套架构经受住了考验,即使后期新增了好几类业务数据,前端代码也没有出现结构性的调整。

2. 环境准备与第一个三维场景

2.1 搭建开发环境需要注意的细节

EarthSDK3本质上是一个基于WebGL的JavaScript库,所以对前端工程化环境的要求不高。Node.js版本建议使用14以上,包管理器用npm或yarn都可以。项目用的Vue 3,但如果你用原生HTML开发,直接引入SDK的JS文件一样能跑起来。

安装方式选择上,如果用的是构建工具,建议直接走npm安装,方便做版本管理和按需引入。我在项目里使用的命令大致是npm install @earth-sdk/earth,然后通过import方式引入模块和样式。如果只是想快速验证功能,也可以把官方提供的SDK文件下载下来,在HTML里用script标签引入。两种方式我都试过,build工具模式下Tree Shaking效果更好,打包体积会小一些。

有一点要提前确认好,就是SDK的授权token。EarthSDK3通常需要注册开发者账号并创建应用获取token,初始化时传入才能正常使用。朋友团队第一次跑示例时没配置token,页面一直白屏,排查了半天才发现是授权问题。这个虽然不算技术难点,但很容易在最开始卡住,建议搭建工程时先检查这一步。

2.2 快速初始化一个三维地球

初始化三维地球的整个过程可以拆成三步:准备容器、创建实例、配置底图。

第一步,在页面里放一个撑满区域的div容器,并给它一个id。容器的宽高必须设置,不然SDK计算视口尺寸时会拿到0,导致地球不渲染。这是新手最容易忽略的问题。

第二步,创建地球实例。示例代码如下:

import * as Earth from '@earth-sdk/earth' import '@earth-sdk/earth/dist/style.css' const viewer = new Earth.Earth('earthContainer', { token: '你的应用token', scene: { backgroundColor: '#0b1120' }, // 相机初始位置,这里先放在北京上空 camera: { position: [116.391, 39.907, 15000], heading: 0, pitch: -60 } })

第三步,给地球添加一个影像底图。这里加载的是一个在线XYZ瓦片地址:

viewer.layers.addImageryLayer({ type: 'xyz', url: 'https://your-tile-server.com/tiles/{z}/{x}/{y}.png', minimumLevel: 3, maximumLevel: 18 })

这样做完,页面上就能看到一个带底图的三维地球了。

注意:不同小版本之间,API名称可能会有细微调整。如果发现初始化方法对不上,优先查阅对应版本的官方案例,这是最靠谱的排查方式。

2.3 初始化参数背后的一点经验

初始化Earth实例时,有几个参数值得花时间理解,而不是直接复制官方默认配置。

camera.position数组里的三个值分别是经度、纬度和相机距地高度。高度单位是米,15000就是15公里,这个高度下能看到整片城区,适合园区项目初始视角。pitch是俯仰角,-60表示相机斜向下看地面,接近人站在高处往下看的效果。如果设置成-90,就是正俯视的电子地图视角。

scene.backgroundColor是场景背景色。默认的深蓝色适合夜景风格大屏,如果你做的是日间业务系统,改成偏亮的颜色会更清晰。这个参数在初始化后也可以通过viewer.scene.backgroundColor动态修改。

另外还需要提醒一点:earth实例的创建要放在页面挂载完成之后。如果在Vue的created或者React的构造函数里初始化,容器DOM还没渲染好,同样会出现白屏问题。我习惯在mounteduseEffect里初始化,并加一个判断确保容器存在。

3. 核心图层加载:把数据放进三维球里

3.1 影像底图加载:在线地图服务与自建瓦片

影像底图是三维GIS项目最基础的图层,相当于传统GIS里的底图。EarthSDK3支持通过XYZ、WMTS、WMS等协议加载瓦片数据,我这次项目同时用了两种方式:在线公共瓦片服务和自建私有瓦片。

在线瓦片适合项目初期快速搭建演示环境,通常走XYZ协议。配置时核心是模板URL、最小层级和最大层级。模板URL里用{z}/{x}/{y}占位,SDK会自动替换成当前视口所需的瓦片行列号。层级范围要和瓦片服务实际提供的层级一致,设置过大或者过小都会导致瓦片请求失败或显示模糊。

自建瓦片场景下,使用的是离线影像。这种瓦片一般由地理处理工具提前切好,存放在内网存储服务或对象存储中。配置方式和在线瓦片几乎一样,只是URL改成内网地址。需要注意的坑是:切瓦片时选择的坐标系必须和SDK的地球坐标系一致,建议统一使用WGS84 Web墨卡托(EPSG:3857)。否则可能出现底图和矢量数据对不齐的情况,这个问题排查起来相当耗时间。

我总结了影像图层常用的几个参数:

参数作用我的经验值
minimumLevel最小可见层级3
maximumLevel最大可见层级18
opacity图层透明度1或0.8
contrast对比度调整1
visibility是否可见true

3.2 地形加载:让场景“凹凸有致”

纯影像底图渲染出来是平面的,加载地形之后地面才会有起伏,这在做山区、丘陵地带的项目时非常关键。园区的项目虽然基本是平地,但为了后续扩展到周边山体场景,地形能力还是提前接入了。

EarthSDK3加载地形,本质上是加载高程数据源,通过渲染图层方式叠加到地球上。配置时只需要指定地形数据服务地址即可:

viewer.terrain.add({ url: 'https://your-terrain-server.com/terrain', visibility: true })

大部分在线地形数据源都使用量化网格或者TIN格式组织。如果地形数据服务加载较慢,可以通过设置地形细节层级来限制最大细节范围,减少网络请求和三角面数量。

注意:地形数据和影像底图可能来自不同数据源,一旦叠加后出现高程明显的断层或错位,优先检查两个数据源的坐标系和范围是否一致,这通常能解决八成以上的问题。

3.3 矢量数据可视化:GeoJSON与业务数据上屏

三维GIS项目里,矢量数据通常用来表达行政边界、道路、用地范围、管线等要素。GeoJSON是最常用的交换格式。把GeoJSON加载成三维图层,并且给不同要素设置不同样式,是三维GIS开发的基础能力。

下面是我在项目中加载区域边界的方法:

viewer.layers.addGeoJsonLayer({ url: '/data/region.geojson', fill: { color: 'rgba(0, 150, 255, 0.25)', outline: true, outlineColor: '#00a8ff' }, clampToGround: true })

关键参数有三个。fill.color控制面的填充颜色,我习惯用带透明度的rgba值,这样不会遮挡底图细节。outline控制是否显示边界线,打开后面状区域的轮廓会非常清晰。clampToGround表示要素是否贴在地表上,如果地形有起伏,这个参数能让面贴合地形走,而不是像纸片一样悬在半空。

遇到业务数据不是GeoJSON的情况,比如后端返回的是普通的JSON数组,需要先转换成GeoJSON或者通过遍历数据方式手动添加标注。我一般使用一个转换函数把含经纬度字段的数据统一转成FeatureCollection,再交给SDK渲染。这样做的好处是渲染层代码统一,数据可以灵活切换。

3.4 三维模型加载:从单模型到倾斜摄影

园区建筑的三维模型是项目里最核心的数据。模型格式方面,我使用的有GLB单体模型和3D Tiles倾斜摄影数据两种。

加载GLB单体模型比较简单,关键是指定模型的放置位置、高度和缩放比例:

viewer.layers.addModelLayer({ url: '/models/building_a.glb', position: [116.391, 39.907, 43], scale: 1, heading: 0 })

position里的第三个值是模型离地高度。这个值要根据建筑底标高配置,如果建筑底部被地面淹没,就把高度值调高;如果模型悬空,就调低。现实中很多模型数据都缺乏准确的底标高,这时候往往要现场调整,我通常把调整步骤抽成可配置的参数,方便在做模型摆放时反复微调。

倾斜摄影模型一般用3D Tiles格式加载,这是三维GIS项目中承载大范围精细化实景数据的主流方案:

viewer.layers.addTilesetLayer({ url: 'https://your-data-server.com/tileset.json', maximumScreenSpaceError: 16 })

加载倾斜摄影数据时,有一个参数极易踩坑:maximumScreenSpaceError,它控制模型在屏幕上的简化程度。值越大,渲染性能越好,但模型细节越少;值越小,细节越清楚,但帧率会明显下降。我处理园区倾斜摄影数据时,最终调到了16,既保证了远看不断层,近看也有足够细节。

从整个图层体系来看,合理的图层管理顺序是影响场景渲染效果的重要因素。影像底图放在最底层,地形叠加在底图上,矢量和标注放在中间层,三维模型和倾斜摄影放在业务最上层。顺序错了虽然不一定报错,但视觉上会出现相互遮挡的混乱情况,调整起来比较痛苦。

4. 业务交互功能开发:让三维场景真正“能操作”

4.1 相机飞行定位的多种打开方式

三维GIS项目里,“从当前位置飞到目标位置”是最常被业务方提及的功能。比如在大屏上点击左侧“停车场”,右侧三维场景就要平滑地飞到停车场上方。EarthSDK3提供了一套相机控制API,但实际开发时依然有几个细节需要处理好。

基础用法是传一个目标视角:

viewer.camera.flyTo({ position: [116.391, 39.907, 800], heading: 0, pitch: -60, duration: 3 })

duration控制飞行持续秒数,我一般用3秒左右,太快用户在视觉上跟不上,太慢又显得反应迟钝。业务中我更常配合图层定位使用,先通过图层ID找到对应图层的范围,再根据范围动态计算相机落点,这样无论目标物是几层楼的大建筑还是几十米的小标志,都能准确框定在视野内。

如果飞行时希望相机自动围绕某个目标点旋转展示,可以在飞行结束后调用围绕旋转接口。这个效果在展示重点项目时很提气,但要注意旋转速度不能太快,否则容易让用户产生眩晕感。

4.2 点击拾取与弹窗展示:属性信息的三维呈现

把三维场景和业务数据连起来的关键环节,是点击模型或者标注时弹出对应的属性信息。比如点击一个摄像头点位,能弹出“摄像头编号、所属区域、在线状态、负责人”等信息。

实现这个功能,我封装了一个通用点击方法:

viewer.on('click', (event) => { const picked = viewer.pick(event.windowPosition) if (picked) { const properties = picked.attributes if (properties) { popup.show({ title: properties.name, content: buildContent(properties), position: picked.position }) } } })

viewer.pick会从当前鼠标点击位置检测场景中是否拾取到对象,返回的对象上带有之前绑定到模型或标注上的属性数据。这里最关键的开发习惯是:在加载模型或标注时,就要把业务属性通过统一字段绑定好,比如nametypecode等。如果等点击之后再去查接口,用户体验和代码逻辑都会大打折扣。

弹窗组件我直接用了一个覆盖在三维画布上的普通HTML浮层,通过position参数将浮层定位到模型在屏幕上的投影位置。场景旋转或缩放时,浮层位置需要跟着同步,可以在相机变化事件里更新浮层坐标。

4.3 测量与分析能力:让三维场景变得“专业”

三维GIS和普通三维展示产品拉开差距的地方,往往在测量和分析能力上。EarthSDK3内置的空间测量模块,开箱即可用:

viewer.measure.enable('distance')

这个接口会在场景中启用了测距模式,用户点击多个点,界面上就能实时显示每一段的长度和累计长度。除了测距,还可以启用面积测量、高度测量、三角测量等模式。在园区管网管理场景中,测距功能被用在了“估算两点间管线长度”上,几乎零代码接入,性价比极高。

更进阶一点,是做可视域分析,也就是从某个观察点看出去,能看见哪些区域:

viewer.analysis.startViewshed({ position: [116.391, 39.907, 120], direction: 0, angle: 60 })

这个功能在园区安防场景中非常有用:把摄像头位置作为观察点,模拟监控视野范围,辅助判断监控盲区。虽然三维空间分析有一定计算量,但在覆盖范围适中的园区场景下,运行时还是比较流畅的。

4.4 动态点位与轨迹:让业务数据动起来

大屏可视化项目中,“动起来”的数据往往比静态数据更有冲击力。我在项目里做了一个人员轨迹回放功能:后端返回一系列按时间排序的坐标点,前端将这些点转换成动态移动的标注或者线性轨迹。

具体思路是:先在地图上绘制一条轨迹线,然后创建一个模型,通过定时更新其position属性沿轨迹移动。移动过程中,模型朝向也可以根据相邻两点方向动态计算,让效果更加逼真。

const movingMarker = viewer.layers.createPointLayer({ image: '/images/marker.png', position: trackPoints[0] }) let index = 0 const timer = setInterval(() => { index += 1 if (index >= trackPoints.length) { clearInterval(timer) return } movingMarker.position = trackPoints[index] }, 1000)

这里要注意的是,轨迹数据量太大时,直接逐帧更新位置会轻微卡顿。优化方式是在数据端对轨迹点做抽稀处理,只保留必要的关键点位,同时缩短更新间隔。实测中,1000个点抽稀到200个点后,视觉效果几乎不受影响,但流畅度提升非常明显。

5. 常见问题排查与性能优化实录

5.1 白屏、授权失败与容器渲染问题

三维GIS开发遇到的第一类问题,往往是页面加载后一片空白或者地球不显示。从我接触到的提问来看,九成以上的白屏是由下面几个原因造成的。

第一是token未配置或配置错误。SDK初始化时会校验token,一旦校验失败,整个场景创建流程会中断。这类问题排查最容易,打开浏览器控制台看网络请求,如果初始化接口返回403或401,基本就是授权问题。

第二是容器没有宽高。这个问题前面也提过,三维画布渲染依赖容器尺寸,如果容器是隐藏状态或者初始宽高为0,SDK拿不到有效视口尺寸,地球自然不会显示。在Vue项目里,如果父组件有懒加载或者v-show切换,记得在切换完成后调用一次viewer.resize()

第三是数据源地址不可达。比如WS瓦片地址配置了一个不存在的域名,或者自建地形数据服务没有启动,场景虽然初始化成功了,但视觉上只是没有一个完整的底图,很容易让人误以为整个三维场景崩溃了。排查方法是用浏览器直接访问瓦片地址模板里的某一个具体瓦片URL,看看能不能正常返回图片。

5.2 坐标偏移:最隐蔽的“数据对不上”问题

坐标偏移是GIS开发中一个经典而又隐蔽的问题。表现在现象上就是底图在A位置、矢量在B位置、模型在C位置,三者完全不在同一个经纬度上。

发生坐标偏移的核心原因,通常是数据源之间的坐标系不一致。比如影像瓦片使用WGS84 Web墨卡托投影(EPSG:3857),而业务矢量数据使用CGCS2000坐标系或者WGS84地理坐标(EPSG:4326)。Web墨卡托坐标数值本身是以米为单位的平面坐标,和经纬度数值不是同一套系统,直接混用必然会产生偏移。

我的排查思路是按照“先确定数据坐标系,再做统一转换”的顺序。建议项目一启动就定下规范:所有进入场景的空间数据统一转成WGS84经纬度(EPSG:4326)或EPSG:3857,具体看SDK内部采用哪种坐标系。业务数据在后端导出时就完成转换,前端不做二次转换,避免重复计算和数据不一致。

注意:CGCS2000和WGS84在多数城市区域内差异很小,但这种小差异在大比例尺业务中会变成几米到几十米的偏差,所以在政企类GIS项目中不能忽视,必须作为专项问题处理。

5.3 场景卡顿:资源、数据和渲染的三层优化

三维GIS项目跑到中后期,性能优化是无法回避的话题。优化前我做的第一件事是用浏览器Performance面板记录场景在正常操作时的帧率和耗时。记录显示,场景初始状态下帧率只有25左右,切换到建筑密集区域时会掉到20以下,交互明显有迟滞感。从GPU和网络两个维度分析了数据后,我做了下面三项优化。

第一项是模型资源轻量化。园区倾斜摄影原始数据如果直接加载,浏览器几乎扛不住。我使用工具对模型做了纹理压缩和顶点抽稀的预处理,把纹理从2048尺寸压缩到1024,经过处理后加载包体减小约四成,初帧加载耗时从4秒下降到2秒左右。

第二项是渲染参数调整。通过调大maximumScreenSpaceError让远处模型使用更粗糙的表层渲染,降低了GPU压力。同时开启视口裁剪,让视野外的模型不再参与渲染计算。这两项调整对帧率提升立竿见影。

第三项是数据分级加载。在项目中,我把摄像头点位按区域分组,结合相机高度动态控制图层的可见性。当相机拉到城市尺度时,只显示所在城区的点位;当镜头放大到园区内部时,才显示园区全量点位。这样既保证了视觉效果,又大幅减少了单帧渲染的实体数量。

优化后再次记录数据,帧率稳定在55以上,倾角度操作也明显跟手了。整个过程验证了一个观点:三维GIS性能优化不是靠单一手段就能解决的,资源层、渲染层、数据层需要同时动手术。

5.4 常见问题速查表

把这一路踩过的坑整理成一张速查表,方便后面排查问题的时候快速定位:

现象可能原因处理建议
页面白屏token未授权或者容器无宽高检查控制台请求,确认容器尺寸
地球显示但无底图瓦片地址不可达或层级设置错误用浏览器直接访问瓦片,检查模板URL
地面平面化未加载地形数据源检查terrain.add的地址是否正确
矢量与底图位置对不上坐标系不一致统一数据源坐标系,推荐EPSG:4326
模型半截埋入地下模型底标高设置偏低在position第三个参数加大高度
点击模型无拾取结果未绑定业务属性确认加载时是否传入attributes字段
场景卡顿模型面数过多、渲染参数不合理做模型轻量化,调整LOD参数
纹理模糊纹理分辨率过小或mipmap未配置预处理阶段提升贴图分辨率

6. EarthSDK3开发中我总结的几条个人经验

项目从启动到交付,整个周期花了两个月左右,前期踩坑多,后期顺畅不少。聊聊我的体感。

EarthSDK3这类三维GIS框架,真正拉开开发效率差距的通常不是API掌握程度,而是是否理解三维场景里“数据组织”这件事。模型、标注、图层、属性、事件,这些元素之间的关系理清了,后续加需求、改样式都会非常顺。反过来,如果一开始只是对着官网示例做堆叠,代码很容易变成一坨无法维护的混合体。

另外一个心得是,三维开发调试离不开“分步验证”的习惯。加一个图层就检查一次位置和样式,不要所有东西一次性铺上去再去调,否则出了错根本不知道是哪一层引入的。我甚至会在加载层时临时给不同图层设置不同颜色,确认数据范围正确后再还原成正式样式,这个方法虽然土,但在排查问题的时候特别高效。

最后建议团队做三维GIS项目时,预留一个独立的阶段做模型处理。很多项目把时间全部排给了前端开发,忽略了对原始GIS数据的轻量化和格式转换,结果到了联调阶段不断被性能和兼容性问题拖住。数据处理好,前端工作量至少能减少三分之一。

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

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

立即咨询