原因很简单——模型太精细了
发布时间:2025-06-24 20:20:13 作者:北方职教升学中心 阅读量:879
zp_community_id
:社区 ID,外键,关联到 zp_community
表。zp_room_name
:房屋名称,例如“101”。为了解决这个问题,我们对模型进行了抽稀处理,但结果还是不理想,依然卡顿得让人抓狂。
zp_room
表建立多对一关系,一个房屋可以包含多个窗户。zp_build_name
:楼栋名称,例如“A5_10”。如果你也在进行类似的3维GIS项目,或者遇到类似的技术挑战,欢迎与我分享你的经验与困惑。正常)等描述住户的其他信息。zp_room_id
:房屋 ID,主键,用于唯一标识每间房屋。- 字段:
zp_community_id
:社区 ID,主键,用于唯一标识每个社区。添加 3D 模型到 Cesium 场景函数:
addModel(models, entities) { if (models && models.length > 0) { for (let i = 0; i < models.length; i++) { let heading = Cesium.Math.toRadians(65); var pitch = 0; var roll = 0; var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll); var orientation = Cesium.Transforms.headingPitchRollQuaternion( models[i].position, hpr ); this.locatEntity = entities.add({ name: models[i].name, id: models[i].id, position: models[i].position, orientation: orientation, model: { color: null, silhouetteColor: null, silhouetteAlpha: 0, silhouetteSize: 0, show: true, uri: models[i].url, scale: 1.0, // 缩放比例 minimumPixelSize: 1, // 最小像素大小 maximumScale: 1, // 模型的最大比例尺大小。我实在不想绑这些东西,只好辗转用了些“特殊手段”搞了一个 Mapbox账号。
- 字段:
zp_build_id
:楼栋 ID,主键,用于唯一标识每个楼栋。好在,经过一番折腾,我发现了解决之道:nvm!(https://github.com/nvm-sh/nvm)真是个好东西,堪比穿越时空的工具箱,直接把 Node.js环境切换到项目需要的版本,问题就迎刃而解了。存储楼栋信息的zp_build、然而,第一版模型导入 Cesium后,直接把界面“卡成了PPT”。而这个3维GIS系统用的是 Vue3,导致前端依赖始终拉不下来,急得我头都大了。
4.
zp_window
窗户表- 功能:存储窗户的单体化信息。状态(如危险、
- 关系:
- 与
zp_build
表建立多对一关系。以下是每张表的具体设计与作用:
1.
zp_community
社区表- 功能:存储社区的基础信息。整个流程是:配置数据 -> 模型对象 -> 场景渲染的转换过程。
通过主外键关联,整个数据表的结构从社区到楼栋再到房屋、虽然过程有点折腾,但也算是用汗水换来了经验。随着建筑数量的增加,前端的加载压力成倍增长,直接把界面“按在地上摩擦”。
- 与
zp_window
表通过zp_room_id
建立一对多关系。
3.
zp_room
房屋表- 功能:存储房屋的具体信息。
- 字段:
zp_window_id
:窗户 ID,主键,用于唯一标识每个窗户。其流程为:- 遍历 buildingName 数组,获取每个建筑物的配置信息
- 调用 initHouse() 函数,根据建筑物数据创建模型对象,包含:
- 模型路径(gltf文件)
- 位置信息(经纬度坐标)
- 模型数组引用
- 建筑物ID
- 将创建的模型对象存入 models 数组
- 最后调用 addModel() 函数,将所有模型一次性添加到 Cesium 场景的 entities 中进行渲染
这是场景初始化时的重要函数,负责将所有建筑物的 3D 模型加载并显示到地图上。窗户、
zp_household_name
:住户姓名。“租客”、其流程为:- 开启加载状态,设置 2 秒的加载动画
- 清空场景中现有的实体(viewer.entities.removeAll())和模型数组(models = [])
- 通过 axios 请求 /zz/redisWindow/windowUrls/{buildingId} 获取该建筑的窗户数据
- 请求成功后:
- 先加载建筑主体模型(initHouse())
- 遍历窗户数据数组,逐个加载窗户模型(initClickWindow())
- 将所有模型添加到场景(addModel())
- 添加建筑标签和按钮标签
- 最终实现建筑物从整体模型向可交互的分户模型的转换
这是实现建筑物点击交互的核心函数,将整体建筑拆分为可独立操作的窗户单元,为后续的窗户选择和信息绑定提供基础。客户要求每个窗户都必须单体化,也就是说每扇窗户都是一个独立的要素(这就需要把建筑与窗户进行分离,如下图)。原因很简单——模型太精细了。我们为此构建了一套基于 Cesium、本文系统初衷是设计一套数据管理系统,以实现对社区特殊人群的精细化管理。
后端代码:
在后端我把窗户模型丢到了redis中进行存储,对应的接口为:
public Map<String,Set> getWindowUrls(String buildBH) { Map<String,Set>windowUrls=new HashMap<>(); Set<Object> windowBH=redisUtils.sGet(buildBH); windowUrls.put(buildBH,windowBH); return windowUrls; }
redis中存的是建筑下对用的窗户编号以及窗户编号对应的gltf模型信息:
A5_10为建筑编号, A5_10_w_1为窗户编号
4.2.3. 住户信息绑定
4.2.3.1. 绑定初始化
前端代码:
bindWindow(viewer, entities, bindWindows) { // 移除其他事件处理器 this.removeAllHandler(); // 创建绑定事件处理器 this.handlerBind = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); // 处理 Ctrl + 点击事件 this.handlerBind.setInputAction( function (movement) { if (this.isInit == 2) { // 绑定模式 var pick = viewer.scene.pick(movement.position); if (pick && pick.id && pick.id._id.indexOf("_w_") != -1) { // 处理窗户选择 windowsList.push(pick.id._id); // 高亮显示 this.lightwindow(entities, windowsList, 0); } } }, Cesium.ScreenSpaceEventType.LEFT_CLICK, Cesium.KeyboardEventModifier.CTRL );}
项目使用 isInit 变量管理不同的交互状态:
- isInit = 0: 初始化状态,可点击建筑
- isInit = 1: 建筑分解状态,可查看窗户信息
- isInit = 2: 绑定模式,可进行窗户绑定
4.2.3.2. 绑定窗户
前端代码:
// 获取窗户信息this.$axios.getHouseholdInfo({ windowId: windowId }) .then(res => { if (res.code == 200) { // 处理返回数据 } });// 提交绑定axios.post("/room/bindWindow", { roomId: val.name, windowId: [...this.windowsData]})
这段代码实现了窗户信息的查询和绑定功能。这种按需加载的策略有效缓解了性能问题,同时也让客户的需求得到了妥善实现。去年我写了一个工作流系统,用的 Node.js是 10 的版本,Vue也还是Vue2,老得都快发灰了。行吧,登录 Mapbox官网去获取新的 token。住户,形成了一套完整的层次化管理体系:
- 社区 -> 楼栋 -> 房屋 -> 窗户:实现从宏观到微观的空间数据管理。存储房屋信息的zp_room、
- 与