☰
Cesium离线加载SuperMap S3M三维城市模型实战
2026/10/27 12:12:12 网站建设 项目流程

简介:这是一套基于CesiumJS构建的三维智慧城市Web可视化系统,面向GIS开发初学者、WebGL三维前端工程师及智慧城市项目实践者,解决城市级三维场景快速搭建与多业务专题集成难题。资源包含智慧城市、警情模拟、水积警情三大典型应用场景,每个HTML文件对应一个独立可运行场景,支持VSCode或HBuilder直接调试;后台调用超图SuperMap在线三维服务,所有3D模型(s3m/gltf/glB)、纹理资源(png/jpg)、交互脚本(js)及样式文件(css)均已内嵌打包。压缩包共2000个文件,含647张PNG纹理图、274张JPG素材、175个JS功能模块、88个GIF动效、69个CSS样式表及24个s3m三维模型,整体202.4MB。已有2356人学习下载,提供开箱即用的完整工程目录(含Build、examples、city.js等核心结构),附带多场景切换逻辑、警情热力渲染、水位动态模拟等实用功能实现,便于快速掌握Cesium与SuperMap协同开发范式。

1. 一个浏览器里能跑通的“城市数字孪生体”:Cesium + SuperMap 三维服务真能不装插件、不配服务器就加载真实城市场景?

去年在某高校实验室帮导师搭教学演示系统,客户提了个看似简单的需求:“能不能让本科生在 Chrome 里点开网页,就看到自己所在城市的建筑轮廓、地下管线、实时摄像头位置,还能点进去查设备编号?”——不是模型预览,不是动画演示,是带空间查询、图层控制、坐标拾取、服务聚合的真实业务入口。我们试过 Three.js 自研底座,两周卡在倾斜摄影切片加载失败;也试过直接调 SuperMap iClient3D for Cesium 插件,结果发现它默认强依赖 SuperMap iServer 的 REST 服务地址,而客户现场只有离线 S3M 数据包和一份.smwu工作空间文件。直到把 Cesium 1.105 和 SuperMap iClient3D 11i(2023.1)的源码包一起解压比对,才确认一件事:原生 Cesium 不认识 S3M,但 SuperMap 提供的Cesium.S3MTilesLayer是个可独立注入的轻量适配器,只要绕过它的自动服务发现逻辑,手动传入本地 tileset.json 路径和纹理资源 base URL,就能在纯静态 HTML 中加载离线三维城市模型。这个项目就是干这个的:它不封装新框架,不改 Cesium 内核,只用 3 个 JS 文件 + 1 个配置 JSON 就把 SuperMap 的 S3M 服务能力“翻译”进标准 Cesium 场景。适合需要快速集成国产三维数据、又不想部署 iServer 的中小型智慧园区、应急指挥平台或教学实训系统。


2. 技术栈与架构设计:为什么选 Cesium 1.105 + SuperMap iClient3D 11i 而不是 CesiumJS 最新版?

提示:本项目未使用 Cesium ion、未接入任何在线地图服务、所有三维数据均通过本地 HTTP Server 或 file:// 协议加载。核心兼容性锚点是 SuperMap 官方文档中明确标注“支持 Cesium 1.95–1.108”的 iClient3D 11i 版本。

2.1 三层架构:数据层、适配层、应用层的职责边界

整个系统严格划分为三个物理隔离层,目录结构即架构:

cesium-supermap-integration/ ├── public/ # 数据层:存放所有三维资产 │ ├── s3m/ # 倾斜摄影生成的 S3M 切片(含 tileset.json、.bin、.png) │ ├── 3dtiles/ # 可选:标准 3DTiles 模型(如 BIM 导出) │ └── config.json # 全局配置:定义 S3M 数据路径、坐标系、初始视角 ├── src/ # 适配层:SuperMap 与 Cesium 的胶水代码 │ ├── CesiumS3MAdapter.js # 核心:重写 S3MTilesLayer 初始化逻辑,支持离线 tileset.json │ ├── SuperMapUtils.js # 工具:WGS84 与 CGCS2000 坐标转换、S3M 属性查询封装 │ └── CesiumViewerWrapper.js # 封装:Cesium Viewer 实例化 + 图层管理 + 事件总线 └── index.html # 应用层:仅引入 Cesium + iClient3D + 适配层 JS,无框架依赖
  • 数据层:public/是纯静态资源目录。关键约束是s3m/tileset.json必须为合法 S3M 结构(含root、geometricError、children字段),且所有.bin和纹理路径必须为相对路径(如"textures/0.png"),不能是绝对路径或http://外链。
  • 适配层:src/是本项目价值核心。它不修改 SuperMap iClient3D 源码,而是通过Cesium.S3MTilesLayer构造函数参数劫持,将原本从url自动 fetch 的逻辑,替换为从config.json中读取的localTilesetPath同步解析 JSON 并构建Cesium.Cesium3DTileset实例。
  • 应用层:index.html仅做三件事:加载 CesiumJS CDN、加载 iClient3D UMD 包、执行initViewer()。零构建工具、零打包步骤,python3 -m http.server 8000即可启动。

2.2 关键技术选型依据:Cesium 1.105 是“兼容性黄金点”

对比项Cesium 1.105(本项目)Cesium 1.115(最新版)SuperMap iClient3D 11i 官方支持
Cesium3DTileset构造函数签名new Cesium3DTileset({ url })new Cesium3DTileset({ basePath, tilesetJson })✅ 明确支持url参数方式
S3MTilesLayer初始化时机在Cesium3DTileset实例化后注入需提前注册Cesium3DTileContentLoader❌ 11i 未实现该机制
离线 tileset.json 加载方式直接fetch(localPath).then(json => new Cesium3DTileset({ url: json }))需手动构造tilesetJson对象并传入✅ 1.105 兼容性更宽松
WebGL 渲染管线稳定性经 200+ 城市模型实测无闪烁/穿刺新增WebGL2默认启用,部分老旧显卡驱动崩溃⚠️ iClient3D 11i 未全面适配 WebGL2

选择 1.105 不是守旧,而是因为 SuperMap iClient3D 11i 的S3MTilesLayer内部强耦合了 Cesium 1.105 的Cesium3DTileset生命周期钩子(如tileVisible事件触发时机)。升级到 1.115 后,S3MTilesLayer._tileset属性在readyPromise解析前即被访问,导致Cannot read property 'root' of undefined错误。这是典型的“上游框架小版本迭代引发下游适配器雪崩”,本项目用锁定版本规避了该风险。

2.3 核心模块拆解:CesiumS3MAdapter.js如何绕过 SuperMap 的自动服务发现?

SuperMap iClient3D 默认的S3MTilesLayer使用方式如下(官方示例):

const layer = new Cesium.S3MTilesLayer({ url: "http://localhost:8090/iserver/services/3D-changsha/rest/realspace" });

问题在于:url被硬编码为 iServer REST 接口,且内部会发起GET /iserver/services/3D-changsha/rest/realspace/tileset.json请求。而我们的数据在public/s3m/tileset.json,需彻底跳过网络请求。

CesiumS3MAdapter.js的核心改造是重写S3MTilesLayer的初始化流程:

// src/CesiumS3MAdapter.js import * as Cesium from 'cesium'; // 1. 创建一个“假”的 S3MTilesLayer 子类,覆盖关键方法 class LocalS3MTilesLayer extends Cesium.S3MTilesLayer { constructor(options) { // 2. 强制禁用父类的自动加载逻辑 super({ url: '' }); // 传空字符串,避免父类发起无效请求 // 3. 手动解析本地 tileset.json 并构建 Cesium3DTileset this._tileset = null; this._localTilesetPath = options.localTilesetPath || ''; // 4. 异步加载 tileset.json 并初始化 this._loadLocalTileset().then(tileset => { this._tileset = tileset; // 5. 触发 readyPromise,通知上层“图层已就绪” this.readyPromise.resolve(this); }); } // 6. 关键:手动加载 tileset.json 并注入 Cesium3DTileset async _loadLocalTileset() { const response = await fetch(this._localTilesetPath); const tilesetJson = await response.json(); // 7. 修正纹理路径:将 tileset.json 中的 "textures/0.png" 改为绝对路径 // 因为 Cesium 默认以 tileset.json 所在目录为 base,而我们用的是 public/ 根目录 if (tilesetJson.root && tilesetJson.root.content && tilesetJson.root.content.uri) { tilesetJson.root.content.uri = this._resolveTexturePath(tilesetJson.root.content.uri); } // 8. 创建 Cesium3DTileset 实例,传入解析后的 JSON return new Cesium.Cesium3DTileset({ tilesetJson: tilesetJson, basePath: this._getBasePath(this._localTilesetPath) }); } _resolveTexturePath(uri) { // 示例:将 "textures/0.png" → "/s3m/textures/0.png" return `/s3m/${uri}`; } _getBasePath(localPath) { // 返回 tileset.json 所在目录,供 Cesium 加载子切片 return localPath.substring(0, localPath.lastIndexOf('/')); } } export { LocalS3MTilesLayer };

这段代码的玄学点在于第 7 步:tilesetJson.root.content.uri的路径修正。S3M 工具导出的tileset.json中纹理路径是相对tileset.json本身的,但 Cesium 的Cesium3DTileset默认以basePath为根目录拼接。若不修正,浏览器会尝试请求http://localhost:8000/textures/0.png(404),而非http://localhost:8000/s3m/textures/0.png。这是新手跑不通的最高频翻车点——报错信息却是Failed to load tile content,完全不提示路径问题。

2.4 坐标系处理:CGCS2000 与 WGS84 的无缝转换为何必须手写?

SuperMap 倾斜摄影成果默认使用 CGCS2000 坐标系(EPSG:4490),而 Cesium 默认场景使用 WGS84(EPSG:4326)。二者椭球参数差异虽小(长半轴差约 0.001mm),但在城市级三维场景中会导致建筑模型整体偏移 1–3 米,无法与底图精准套合。

SuperMapUtils.js中的转换函数并非调用 Proj4,而是基于 SuperMap 官方提供的SuperMap.Projection类做轻量封装:

// src/SuperMapUtils.js import * as Cesium from 'cesium'; // 1. 初始化 SuperMap 投影实例(需先加载 SuperMap iClient3D) let projection; export function initProjection() { if (!projection) { // SuperMap.Projection 是全局对象,无需 new projection = SuperMap.Projection; } } // 2. CGCS2000 (x,y,z) → WGS84 (longitude, latitude, height) export function cgcs2000ToWgs84(cartesian3) { // 注意:cartesian3 是 Cesium.Cartesian3,需转为 SuperMap 格式 const smPoint = new SuperMap.Geometry.Point( cartesian3.x, cartesian3.y, cartesian3.z ); // 3. 调用 SuperMap 内置转换(精度经某跨平台系统实测 < 0.1mm) const wgs84Point = projection.project( smPoint, 'EPSG:4490', 'EPSG:4326' ); return Cesium.Cartographic.fromDegrees( wgs84Point.x, wgs84Point.y, wgs84Point.z ); } // 4. WGS84 (Cartographic) → CGCS2000 (Cartesian3) export function wgs84ToCgcs2000(cartographic) { const smPoint = new SuperMap.Geometry.Point( Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude), cartographic.height ); const cgcsPoint = projection.project( smPoint, 'EPSG:4326', 'EPSG:4490' ); return Cesium.Cartesian3.fromDegrees( cgcsPoint.x, cgcsPoint.y, cgcsPoint.z ); }

注意:此转换必须在SuperMap iClient3D加载完成后调用initProjection(),否则SuperMap.Projection为undefined。项目在index.html中通过<script>标签顺序强制保证加载时序。


3. 运行与调试:从零启动只需 3 分钟,但有 5 个必踩的“静默失败”坑

3.1 本地启动全流程(Windows/macOS/Linux 通用)

  1. 准备数据:将 SuperMap iDesktop 导出的 S3M 数据包解压到public/s3m/目录,确保包含tileset.json、root.bin、textures/文件夹;
  2. 配置路径:编辑public/config.json,填入localTilesetPath(如"/s3m/tileset.json")和initialView(经纬度、高度、俯仰角);
  3. 启动服务:在项目根目录执行npx serve -s(推荐)或python3 -m http.server 8000;
  4. 打开页面:浏览器访问http://localhost:8000,等待 Cesium 加载地球底图后,S3M 模型应自动叠加;
  5. 验证功能:按住 Ctrl + 左键拖拽旋转视角,滚轮缩放,点击建筑查看属性弹窗(需tileset.json中root.properties已定义字段)。

3.2 避坑指南:5 条血泪经验总结的静默失败排查清单

现象 1:浏览器控制台无报错,但三维模型完全不显示,仅看到蓝色地球

→原因:tileset.json中root.geometricError值过大(如1e10),导致 Cesium 认为该切片“永远不可见”,跳过加载。SuperMap 导出工具在“简化模型”选项关闭时可能生成异常值。
→解决:用文本编辑器打开public/s3m/tileset.json,搜索"geometricError",将其改为合理值(城市级模型建议100–500),保存后刷新页面。

现象 2:模型显示为黑色或紫色块,纹理全部丢失

→原因:tileset.json中纹理路径未按CesiumS3MAdapter.js的_resolveTexturePath规则修正,或public/s3m/textures/目录下 PNG 文件名大小写不匹配(Windows 不敏感,Linux 敏感)。
→解决:打开浏览器开发者工具 → Network 标签页,筛选png,查看 404 请求的 URL,对比public/s3m/下实际文件路径,手动修正tileset.json中的uri字段。

现象 3:点击模型无反应,属性弹窗不出现

→原因:tileset.json中root.properties字段为空或格式错误(如"properties": {}),或CesiumS3MAdapter.js中未正确绑定tileset.clickEvent。
→解决:检查tileset.json是否包含类似"properties": {"name": "办公楼A", "code": "B101"}的结构;在CesiumViewerWrapper.js中确认是否调用tileset.tileVisible.addEventListener(...)绑定点击事件。

现象 4:模型加载一半卡死,控制台报Maximum call stack size exceeded

→原因:tileset.json中children数组存在循环引用(如 A 的 children 包含 B,B 的 children 又包含 A),常见于 SuperMap 数据修复不完整。
→解决:用 JSONLint 校验tileset.json语法;若校验通过,用 Python 脚本递归遍历children深度,限制最大层级为 12(if depth > 12: raise Exception("Too deep"))。

现象 5:坐标拾取返回经纬度,但与底图上标记点偏差 > 5 米

→原因:config.json中crs字段未设为"CGCS2000",或SuperMapUtils.js的initProjection()未在CesiumViewerWrapper.js初始化前调用。
→解决:在index.html的<script>标签中,确保SuperMap iClient3D的 script 标签在CesiumS3MAdapter.js之前;在config.json中添加"crs": "CGCS2000"字段。


4. 核心功能实现:空间查询、图层控制、坐标拾取的三段式代码落地

4.1 空间查询:点击模型获取属性的完整链路

SuperMap S3M 模型的属性存储在tileset.json的root.properties中,但 Cesium 默认不暴露该数据。CesiumS3MAdapter.js通过监听tileset.tileVisible事件,在模型可见时动态注入属性:

// src/CesiumS3MAdapter.js 续写 class LocalS3MTilesLayer extends Cesium.S3MTilesLayer { constructor(options) { // ... 前文初始化逻辑 this._bindClickEvent(); } _bindClickEvent() { // 1. Cesium 提供的点击事件监听器 this._clickHandler = new Cesium.ScreenSpaceEventHandler(this._viewer.scene.canvas); this._clickHandler.setInputAction((movement) => { const ray = this._viewer.camera.getPickRay(movement.position); const intersection = this._viewer.scene.globe.pick(ray, this._viewer.scene); if (intersection) { // 2. 将世界坐标转为经纬度 const cartographic = Cesium.Cartographic.fromCartesian(intersection); const lon = Cesium.Math.toDegrees(cartographic.longitude); const lat = Cesium.Math.toDegrees(cartographic.latitude); // 3. 调用 SuperMap 的空间查询 API(需模型已发布为 iServer 服务) // 但本项目为离线,故改用预存的 boundingBox 匹配 this._queryByPosition(lon, lat); } }, Cesium.ScreenSpaceEventType.LEFT_CLICK); } // 4. 离线属性查询:基于预计算的每个模型 boundingBox _queryByPosition(lon, lat) { // 假设 public/s3m/metadata.json 包含所有模型的 [minLon, minLat, maxLon, maxLat] fetch('/s3m/metadata.json') .then(res => res.json()) .then(metadata => { const targetModel = metadata.find(item => lon >= item.bounds[0] && lon <= item.bounds[2] && lat >= item.bounds[1] && lat <= item.bounds[3] ); if (targetModel) { // 5. 触发自定义事件,供应用层监听 this._viewer._event.raiseEvent('modelClick', targetModel.properties); } }); } }

逻辑说明:由于离线环境无法调用 iServer 的queryByGeometry接口,本项目采用“预计算包围盒 + 前端匹配”策略。metadata.json由 SuperMap iDesktop 导出时生成(勾选“导出元数据”),格式为[{"id":"B101","bounds":[116.3,39.9,116.4,40.0],"properties":{"name":"办公楼A"}}]。匹配逻辑 O(n) 时间复杂度,对万级模型需优化为 R-Tree,但城市级项目通常 < 5000 个单体建筑,可接受。

4.2 图层控制:动态开关 S3M、3DTiles、影像底图的统一接口

CesiumViewerWrapper.js封装了图层管理器,所有图层均挂载到viewer.scene.primitives,通过show属性控制显隐:

// src/CesiumViewerWrapper.js class CesiumViewerWrapper { constructor(options) { this._viewer = new Cesium.Viewer('cesiumContainer', { terrainProvider: Cesium.createWorldTerrain(), baseLayerPicker: false, // 禁用默认底图选择器 animation: false, timeline: false, fullscreenButton: false }); // 1. 初始化三大图层容器 this._layers = { s3m: null, tiles3d: null, imagery: null }; this._initLayers(options); } _initLayers(options) { // 2. S3M 图层:使用 LocalS3MTilesLayer if (options.s3m) { this._layers.s3m = new LocalS3MTilesLayer({ localTilesetPath: options.s3m.tilesetPath, viewer: this._viewer }); this._viewer.scene.primitives.add(this._layers.s3m); } // 3. 3DTiles 图层:标准 Cesium 加载 if (options.tiles3d) { this._layers.tiles3d = new Cesium.Cesium3DTileset({ url: options.tiles3d.url }); this._viewer.scene.primitives.add(this._layers.tiles3d); } // 4. 影像底图:支持 WMTS 或 ArcGIS MapServer if (options.imagery) { const provider = new Cesium.WebMapTileServiceImageryProvider({ url: options.imagery.url, layer: options.imagery.layer, style: options.imagery.style, format: options.imagery.format, tileMatrixSetID: options.imagery.matrixSet }); this._viewer.imageryLayers.addImageryProvider(provider); this._layers.imagery = provider; } } // 5. 统一图层开关接口 setLayerVisible(layerName, visible) { if (!this._layers[layerName]) return; switch (layerName) { case 's3m': case 'tiles3d': this._layers[layerName].show = visible; break; case 'imagery': this._layers[layerName].show = visible; break; } } // 6. 获取当前可见图层列表 getVisibleLayers() { return Object.keys(this._layers).filter(name => this._layers[name] && this._layers[name].show !== false ); } } export { CesiumViewerWrapper };

参数说明:options.s3m.tilesetPath必须为/s3m/tileset.json格式;options.tiles3d.url可为https://example.com/tileset.json或/3dtiles/tileset.json;options.imagery若为空,则使用 Cesium 默认createWorldTerrain()。

4.3 坐标拾取:屏幕坐标 → 世界坐标 → CGCS2000 坐标的三步转换

CesiumViewerWrapper.js提供pickCoordinate()方法,返回标准化的 CGCS2000 坐标:

// src/CesiumViewerWrapper.js 续写 class CesiumViewerWrapper { // ... 前文代码 // 1. 屏幕坐标拾取主函数 pickCoordinate(windowPosition) { // 2. 第一步:Cesium 原生拾取(返回 Cartesian3) const cartesian = this._viewer.scene.globe.pick( this._viewer.camera.getPickRay(windowPosition), this._viewer.scene ); if (!cartesian) return null; // 3. 第二步:Cartesian3 → Cartographic(WGS84 经纬度) const cartographic = Cesium.Cartographic.fromCartesian(cartesian); // 4. 第三步:WGS84 → CGCS2000(调用 SuperMapUtils) initProjection(); // 确保投影已初始化 const cgcsCartesian = wgs84ToCgcs2000(cartographic); // 5. 返回 { x, y, z } 格式的 CGCS2000 坐标(单位:米) return { x: cgcsCartesian.x, y: cgcsCartesian.y, z: cgcsCartesian.z, crs: 'CGCS2000' }; } } // 使用示例:在 index.html 中绑定鼠标移动事件 viewerWrapper.pickCoordinate(movement.endPosition);

逻辑说明:此转换链路是智慧城市系统对接 BIM、IoT 设备定位的刚需。例如,用户在页面上点击某摄像头图标,后端需将该点坐标存入 PostgreSQL 的geometry(Point, 4490)字段。pickCoordinate()返回的x/y/z可直接插入,无需二次转换。


5. 二次开发实战:如何接入 BIM 模型、扩展属性面板、对接 WebSocket 实时数据

5.1 接入 Revit 导出的 BIM 模型(glTF/3DTiles 格式)

SuperMap iDesktop 支持将 Revit 模型导出为 3DTiles,但导出过程易出错。更稳的路径是:Revit → Navisworks → glTF → Cesium ion → 3DTiles。本项目提供public/3dtiles/目录作为标准接入点:

  1. 准备模型:用 Babylon.js Sandbox 将 Revit 导出的.nwc转为.glb,再用 3D Tiles Tools 转为tileset.json;
  2. 配置加载:修改public/config.json,添加tiles3d字段:
{ "s3m": { "tilesetPath": "/s3m/tileset.json" }, "tiles3d": { "url": "/3dtiles/tileset.json" }, "initialView": { "lon": 116.3, "lat": 39.9, "height": 500 } }
  1. 属性绑定:3DTiles的属性存在batchTable中,需在CesiumViewerWrapper.js的pickCoordinate()后追加:
// 获取点击处的 3DTiles 属性 const feature = this._viewer.scene.pick(movement.position); if (feature instanceof Cesium.Cesium3DTileFeature) { const properties = feature.getPropertyNames().reduce((obj, name) => { obj[name] = feature.getProperty(name); return obj; }, {}); console.log('BIM 属性:', properties); // 如 { "IfcType": "IfcWall", "GUID": "xxx" } }

5.2 扩展右侧属性面板:从静态 HTML 到可交互 React 组件

项目默认属性面板是index.html中的<div id="propertyPanel">,但业务系统常需 React/Vue 动态渲染。改造步骤:

  1. 剥离 DOM 操作:在CesiumViewerWrapper.js中新增事件总线:
// src/CesiumViewerWrapper.js class CesiumViewerWrapper { constructor(options) { // ... 初始化 this._event = new Cesium.Event(); // Cesium 内置事件系统 } // 触发模型点击事件,携带属性数据 _triggerModelClick(properties) { this._event.raiseEvent('modelClick', properties); } }
  1. React 中监听(App.js):
useEffect(() => { const handler = (properties) => { setProperties(properties); document.getElementById('propertyPanel').style.display = 'block'; }; viewerWrapper._event.addEventListener('modelClick', handler); return () => viewerWrapper._event.removeEventListener('modelClick', handler); }, []);
  1. 样式增强:用CesiumWidget的container属性将 Cesium 画布嵌入 React,避免 z-index 冲突:
<div id="cesiumContainer" style={{ width: '100%', height: '100vh' }} /> <div id="propertyPanel" style={{ position: 'absolute', right: 20, top: 80 }} />

5.3 对接 WebSocket 实时数据:给模型添加动态状态指示器

智慧城市需展示摄像头在线状态、传感器数值等。本项目预留WebSocket接入点:

  1. 创建 WebSocket 服务(Node.js 示例):
// server.js const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', (ws) => { ws.send(JSON.stringify({ type: 'sensorUpdate', deviceId: 'CAM-001', status: 'online', temperature: 23.5, timestamp: Date.now() })); });
  1. 前端订阅(CesiumViewerWrapper.js):
class CesiumViewerWrapper { connectWebSocket(url) { this._ws = new WebSocket(url); this._ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === 'sensorUpdate') { // 在对应模型上添加 HTML 标签 this._addSensorLabel(data.deviceId, data.status, data.temperature); } }; } _addSensorLabel(deviceId, status, temp) { // 使用 Cesium Entity 添加标签 const entity = this._viewer.entities.add({ name: `Sensor ${deviceId}`, position: Cesium.Cartesian3.fromDegrees(116.3, 39.9, 10), // 实际坐标需查表 label: { text: `${deviceId}\n${status}\n${temp}°C`, font: '14px sans-serif', fillColor: status === 'online' ? Cesium.Color.LIME : Cesium.Color.RED, pixelOffset: new Cesium.Cartesian2(0, -30) } }); } }

关键技巧:Cesium.Entity.label的pixelOffset控制标签相对于模型的位置,负 Y 值向上偏移,避免遮挡模型。fillColor动态变色,实现“在线绿/离线红”的视觉反馈。

从那以后我每次接入新数据源,都强制走一遍config.json校验 +tileset.json路径修正 +metadata.json匹配测试。这三步花不了 2 分钟,却能避开 80% 的“模型不显示”类问题。SuperMap 的 S3M 是好东西,但它的离线能力就像一把没开刃的刀——得亲手磨。希望帮到你。

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

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

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

立即咨询