geopro/docs/superpowers/specs/2026-06-24-vtk-category-vie...

19 KiB
Raw Permalink Blame History

VTK 三维分析视图重构设计(按数据类型分组 + 对象树联动)

日期 2026-06-24 · 分支 feat/vtk-3d-view · 状态:设计待评审 配套后端契约见 HANDOFF-vtk-3d-backend-api.md;持久化定论见记忆 vtk-3d-persistence-structure

1. 背景与目标

现状 VTK 视图左侧 ColumnDrawer 是三个固定 tab三维数据集 / 二维数据集 / 三维分析),splitByDimension 仅按 ddCode 把数据集分到 3 个维度桶。

新需求:把「三维数据集」并入「三维分析」,改成按数据类型大类分组的视图(电阻率 / 视电阻率 / 瞬变电磁 / 三维体 / 切片),每类有各自的筛选条与操作;对象树勾选与 VTK 列表联动,且支持 GS / 项目层级直挂的数据集;全局视图控制移到 VTK 画布工具条。

目标:在保持现有渲染/交互能力的前提下,完成上述信息架构与交互重构,分类与筛选数据全部走已查实的真实字段,不引入臆测。

2. 范围

  • 「三维数据集 + 三维分析」合并为按大类分组的「三维分析」tab。
  • 分类层由 ddCode3 维)改为 dsTypeCode(大类)映射表驱动。
  • 对象树联动改造:非根 GS 三态复选框 + 右键 ds/tm项目根直挂数据固定显示structParentConfType 分流拉取。
  • 装置类型 / 采集时间筛选落地(客户端可做,不依赖后端补字段)。
  • 全局视图控制(坐标轴 / 比例 / 快捷视图 / 缩放)移到中央 VTK 画布工具条。
  • 三维体/切片生成(createVolume/createSlice)按契约 DTO 组装出真实 VoxelGenerateRequest/SliceGenerateRequest 请求体结构(仍走 mock 存储、假 id供后端联调、并为将来切真实端点铺序列化路径。

不含

  • 「二维分析」tabColumn2DDataset)本次不改。
  • 三维体 / 切片 / 异常的持久化切真实(仍走 Api3dRepository mock后端就绪后另行切换见配套 handoff
  • 后端接口改动(装置类型值→中文字典源待坐实,见 §11

3. 实测依据(关键事实,已通过真实接口核实)

后端基址 http://tenant.geomative.cn/pop-api,样本项目「演示项目(高密度+瞬变)」1438889436225536

  1. 大类必须按 dsTypeCode,不能按 ddCode——电阻率 / 视电阻率 / 瞬变电磁反演剖面三者 ddCode 同为 dd_inversion_data,仅 dsTypeCode/name 不同:

    大类 ds 行 name dsTypeCode
    电阻率数据 电阻率数据 ERT platform inversion data
    视电阻率数据 视电阻率数据 visual resistivity data
    瞬变电磁数据 瞬变电磁反演剖面 DD TRANSIENT ELECTROMAGNETIC INVERSION
  2. 装置类型与采集时间是 ds 的结构化属性dsObject/dynamicForm/{id}formList 定义字段 arrayType(装置类型,下拉)、collectTime(采集时间)ds 行 properties 按 confFieldId 携带其值。装置类型只 ERT 类(电阻率/视电阻率)有,瞬变电磁没有。

  3. 装置类型枚举GET /business/script/arrayTypeList[{itemValue,name}](温纳排列(α)…施伦贝谢尔…共 15 项)。

  4. 层级拉取dsObject/data/pagestructParentId + structParentConfType1=GS/项目2=TM客户端 loadRowsAsync 第 3 参即 parentConfType,现状写死 2。

  5. 遗留arrayType 值是 id1429468249448449),实测不在该字段 optionsObject(另一套 id 体系value→中文字典源待坐实§11

4. 整体架构

ColumnDrawer 改为两 tab「三维分析」「二维分析」。二维分析不动。

「三维分析」tab = QScrollArea 纵向堆叠 5 个类型段:

┌─ 三维分析 ──────────── 二维分析 ─┐
│ ▼ 电阻率数据      [日期▾][装置类型▾]│  段头:类型级筛选
│    ▸ 演示项目(根·直挂ds固定·无复选框) │
│    ▾ ☑ ERT1 (GS·三态)   [右键:生成体]│
│         ☑ ERT1-WN  (数据行·勾选=渲染)│
│         ☑ ERT1-WS                   │
│    ▾ ☐ ERT2 (GS)        [右键:生成体]│
│ ▼ 视电阻率数据    [日期▾][装置类型▾]│
│ ▼ 瞬变电磁数据    [日期▾]           │  无装置类型筛选
│ ▼ 三维体          [日期▾]           │  体→[切片们 + 直接挂体的异常]三级树
│    ▾ 体A                            │
│       ▾ 切片S1(挂体A)               │
│          异常a1(挂S1)               │
│       异常a2(挂体A,临时切片上画)    │
│ ▼ 切片            [日期▾]           │  已保存切片(挂父体下,与三维体段切片同源)
└──────────────────────────────────────┘

全局视图控制移到中央 VTK 画布竖排工具条: 设置(⚙→坐标轴设置对话框) / 快捷视图(前后上下左右) / 放大·缩小·复位(=现"适配")。

5. 数据模型与分类层

DsRow 扩展RepoTypes.hpp):现解析 id/dsName/typeName(=name)/ddCode/createTime/parentId/file*,新增:

  • dsTypeCode —— 大类分类主键。
  • arrayType —— 装置类型值(从 ds 行 properties[] 按 confFieldId 取)。
  • collectTime —— 采集时间(同上)。

NavDto::parseDsRows 补这三个字段的解析。两接口 properties 形态不同(实测)dsObject/data/page 的 ds 行 properties[{confFieldId,value}] 数组(按 confFieldId 取值);dsObject/dynamicForm/{id}properties{fieldCode:value} map(现 parseDynamicForm 用 fieldCode 取值正确、不冲突。DsRow 解析的是前者,故需配合 §10 的 confFieldId↔fieldCode 映射定位 arrayType / collectTime。

分类配置表 CategoryConfig(新文件,集中一处,开闭原则)——每段一条,带元数据:

段序 段名 识别键 可生成三维体 装置类型筛选
1 电阻率数据 dsTypeCode = ERT platform inversion data
2 视电阻率数据 dsTypeCode = visual resistivity data
3 瞬变电磁数据 dsTypeCode = DD TRANSIENT ELECTROMAGNETIC INVERSION
4 三维体 ddCode = dd_voxel
5 切片 ddCode = dd_slice

splitByCategory(rows) -> CategoryBuckets:替代 splitByDimension,按配置表把 DsRow 分入有序大类桶;不在表内的 dsTypeCode接地电阻/原始数据/白化/坐标等)丢弃。

6. 对象树联动(ObjectTreePanel 改造)

节点交互模型:

节点 复选框 右键新增项
项目根 无(不可勾,直挂 ds 固定显示) —(生成三维体改在段头,见 §7
非根 GS 三态 选择 ▸ ds / tm带对号
TM 叶子 普通二态

GS 三态语义(标准 tristate 聚合GS 复选框 = [GS 自身 ds] + [所有子 TM 勾选] 的聚合;都有=Checked都无=Unchecked部分=PartiallyChecked。

  • 右键「选择 ▸ ds」= 切换「GS 自身 ds」开关GS 自身 ds 在树中无独立复选框载体,故必须由此控制)。
  • 右键「选择 ▸ tm」= 一键全选/全不选所有子 TM子 TM 仍可单独勾,此项是批量便捷)。
  • 点 GS 复选框:任一开 → 全关;全关 → 全开。
  • 菜单项按有无动态禁用(无直挂 ds 禁 ds 项,无 TM 禁 tm 项)。
  • 实现约束(必须)停用现有 Qt::ItemIsAutoTristateObjectTreePanel.cpp:123)——它只聚合子项 checkState、看不到「GS 自身 ds 开关」这第二维度,直接套用会产出错误聚合态。改为:用一个 UserRole 在 GS 节点存「自身 ds 开关」布尔,itemChanged 里手动按「ds 开关 子 TM 勾选」计算父三态并 setCheckState(复用现有 0ms 合并防重入 pattern避免级联多次触发

信号扩展checkedTmsChanged(QStringList)checkedSourcesChanged(QList<DataSource>),每个 DataSource = {id, confType}

  • 勾选 TM → {tmId, 2}GS 自身 ds 开关开 → {gsId, 1};项目根直挂(固定)→ {rootId, 1}
  • 集合为{id,confType} 去重的并集:一条 TM 既被 GS 聚合又被单独勾时只算一次;confType=1(GS/项目)拉该节点直挂 dsconfType=2(TM)拉 TM 下 ds二者物理数据不重叠不会重复进桶。

数据流main.cpp 接线):

checkedSourcesChanged
  → 对每源 loadRowsAsync(projId, src.id, src.confType, classify=3, …)   // 第3参按源传(1/2)
  → 汇总 DsRow[] → splitByCategory → 各 CategorySection.setDatasets

7. 类型段组件 CategorySection

一个可参数化的类型段(单一职责,高内聚),由 CategoryConfig 一条配置驱动:

  • 段头:标题 + 折叠开关 + 日期范围筛选 + 装置类型下拉(仅 装置类型筛选=✓ 的段显示)。
  • 段体:项目根 / GS / TM 树 + 数据行,复用 DatasetListPanel::populateDatasetList(按 parentId 建树)与卡片委托;数据行可勾选 = 渲染。
  • 渲染勾选链承接(必须)CategorySection 暴露 checkedDatasetsChanged接管退役的 Column3DDataset 原有「剖面勾选→帘面渲染」主链——main.cpp 把 5 段(电阻率/视电阻率/瞬变=帘面,三维体/切片=体素/切片)的勾选并集后下发 pushChecked(沿用现有 checkedProfiles/checkedAnalysis 并集模型),否则帘面渲染整体失联。
  • 生成三维体入口:仅 可生成三维体=✓ 的段(电阻率/视电阻率/瞬变电磁)段头有「+新增三维体」按钮。源数据集 = 三维分析中当前勾选的同类型 ds(本段类型,天然按段隔离、可跨 GS。点击 → VolumeParamsDialog(页面中心弹窗):左侧·数据列表 树状(按 GS 分组)展示这些已勾选源,每项可勾选/取消供确认或二次修改右侧·插值参数:名称、生成位置(下拉)、插值模型、水平/竖向间距、IDW 幂次、最大影响距离。生成位置(归属)规则:默认 = 源同属单 GS→该 GS、源跨 GS→项目根用户可改为项目内任意 GS / TM(故归属 structParentConfType 可为 1 或 2与源数据解耦。提交 → 组装 VoxelGenerateRequestcreateVolume
  • 筛选:复用并扩展 applyDatasetFilter,日期比较字段由 createTime 改为 collectTime§10

「三维分析」tab 容器(替代原 Column3DDataset/Column3DAnalysis 在 tab 中的位置):QScrollArea + 垂直布局,按配置表实例化 5 个 CategorySection

8. 三维体 / 切片 / 异常段

复用现有 Api3dRepositorymockrefreshAnalysis 合并注入机制,仅重新组织到段:

三维体归属由「生成位置」选择决定:默认单 GS→该 GS、跨 GS→项目根用户可改为项目内任意 GS / TMstructParentConfType 可 1 或 2。后端契约 docs/api/vtk-3d-openapi.json 已同步至 v0.6-draftstructParentConfType 放开 1/2 + 默认规则);客户端 createVolume 接真实端点时需补 structParentId/structParentConfType,并新增按归属实体 id 查异常的 queryException/{remarkSourceId} 调用。

三维体段是「体 → 切片 / 异常」三级树取消独立异常区——异常不再单列,而是作为叶子挂在它归属的实体节点下):

  • 三维体段:列已生成的体(客户端 mock + 后端 dd_voxel),按归属(项目/GS/TM分组。「正在生成…」状态:现 createVolume 同步登记、首次 loadVolume 惰性插值,本期不引入异步生成态机、体即时出行。)体节点下挂:① 基于该体生成的切片子节点;② 直接挂体的异常(见归属规则)。多体可同时勾选渲染(dsProps_ 按 dsId 各存 actor切片/异常操作针对「当前激活体」volumeOwnerDs_=切片源体 currentVolumeImage_)。
  • 异常归属(核心规则):异常必基于切片(在某切片平面上画),切片必基于体SliceSpec.volumeDsId)。查找链 异常 → 所在切片 → 切片所属体。挂载目标按该切片是否已保存成 dd_slice 决定:
    • 切片已保存(是 dd_slice 实体)→ 异常挂该切片remarkSourceId=切片dsId)。
    • 切片未保存(临时圈定平面)→ 异常挂切片所属体remarkSourceId=体dsId=volumeOwnerDs_)。
    • 数据模型:AnomalyvolumeDsId 改名为 remarkSourceId= 挂载实体 dsId体 or 切片;对齐后端 remarkSourceId=dsObjectId)。挂体/挂切片由 remarkSourceId 指向的实体类型区分(查 isVolumeDataset/isSliceDataset),展示树按 parentId=remarkSourceId 自动挂到对应节点——不引入新 type 字段⚠️ 后端 remarkSourceType 已是标注几何形态(1点/2线/3面/4文字 = Anomaly.markType),勿与"挂体/挂切片"混淆。仍 mock 存储。
  • 切片段:列已保存切片(dd_slice),按父体分组(parentId = 所属体)。与三维体段内的切片子节点同源(同一批 sliceRows)。

体素 / 切片 / 异常的渲染、生成、保存路径不变(VtkSceneController / InteractionManager / Api3dRepository),只改列表的承载位置。

请求体组装(本次新增)createVolume 扩参为 (projectId, structParentId, structParentConfType, params, name)——归属来自对话框「生成位置」选择GS/项目根/TM默认单GS→该GS、跨GS→项目根createSliceprojectId。两者内部按新增客户端 DTO VoxelGenerateRequest/SliceGenerateRequest(对齐 docs/api/vtk-3d-openapi.json schema组装出完整请求体并提供 toJson 序列化mock 路径存内存 +debug打印请求体将来切真实端点只把「存」改「发」、组装逻辑原样复用。如此重构一落地即可从 UI 真实产出请求体(值为 mock、结构与字段为真createSliceprojectId 会波及 main.cpp 切片保存调用点(切片右键/列表保存路径),须一并传 nav.currentProjectId()

9. VTK 画布工具条 VtkViewToolbar

Column3DDataset 抽出全局视图控制,做成中央 VTK 画布上的竖排工具条(新组件):

  • 设置(⚙) → 弹「坐标轴设置」对话框 AxesSettingsDialogX 轴 / Y 轴 / 深度(m) 各带「显示」开关 + 最小值 / 最大值,取消 / 应用。
  • 快捷视图:前、后、上、下、左、右(按钮文字即此六字,沿用现有 ViewDir 与功能)。
  • 缩放:放大 / 缩小 / 复位(复位 = 现「适配」)。

信号沿用现有 Column3DDataset 已接的控制器槽(axesModeChanged/verticalExaggerationChanged/viewRequested/zoom*/fitRequested 等),仅迁移承载控件。水平/垂直比例的承载位置随工具条一并迁移(或并入坐标轴设置对话框,实现时取最贴合者);setVerticalExaggeration 默认值回灌main.cpp:902也要迁到新工具条避免默认夸张值不同步。

10. 装置类型 / 采集时间筛选落地

字段映射服务 DatasetFieldDictionary(新组件,按 dsType 缓存)——为何必要ds 列表行 properties 只给 [{confFieldId,value}](数字 confFieldId、无字段名要判定哪个 confFieldId 是 arrayType/collectTime必须从 dynamicFormformList(含 confFieldId↔fieldCode 对应)取映射。

  • 对每个 dsType 拉一次 dsObject/dynamicForm,缓存:① arrayType / collectTime 字段的 confFieldId(用于从 ds 行 properties 取值);② 装置类型选项字典value→中文
  • 列表行装置类型 = ds 行 propertiesconfFieldId == arrayType 的 confFieldId 那项的 value经字典翻中文展示与分组。
  • 段头装置类型下拉 = 该段当前数据出现过的装置类型集合;选中按值过滤。

采集时间:日期筛选按 collectTime;三维体 / 切片段无此字段 → 回退 createTime

11. 待坐实 / 风险

  • 装置类型 value→中文字典源:实测 arrayType 值不在该字段 optionsObject 里(另一套 idparseDynamicForm 也不翻译;但客户端属性面板据称显中文。需在实现前坐实正确字典源(候选:fieldConfigJsonObject.fieldDataRadius 指向的全局字典 / script/arrayTypeList)——请提供客户端数据集属性面板截图以最快定位现有翻译路径。不阻塞其余设计。
  • 三维体 / 切片 / 异常仍 mock:后端就绪后切真实(接口 I3dSceneRepository 留缝),见配套 handoff。
  • 跨 GS 生成体:本次以「单容器(项目根/GS范围」为主跨多 GS 拼大体暂不做。

12. 组件 / 文件边界

类别 组件 说明
新增 CategoryConfig dsTypeCode/ddCode→大类映射表 + 段元数据
新增 splitByCategory 替代 splitByDimension
新增 CategorySection 单个类型段(段头筛选/操作 + 段体树)
新增 三维分析 tab 容器 QScrollArea 堆叠 5 段
新增 VtkViewToolbar + AxesSettingsDialog VTK 画布工具条 + 坐标轴设置
新增 DatasetFieldDictionary 按 dsType 缓存 arrayType/collectTime 映射 + 装置字典
改造 ObjectTreePanel GS 三态 + 右键 ds/tm + 信号扩展为带 confType 源集合
改造 DsRow + NavDto::parseDsRows 补 dsTypeCode/arrayType/collectTime
改造 ColumnDrawer 三 tab → 两 tab
改造 main.cpp 数据流按 confType 分流拉取 + 段接线 + 生成入口传容器归属
新增 VoxelGenerateRequest/SliceGenerateRequest DTO + toJson 对齐 openapi schema 的客户端请求体结构 + 序列化
改造 Api3dRepository::createVolume/createSlice 扩参带归属/projectId内部组装请求体 DTOmock 存 / 将来发)
复用 DatasetListPanel populateDatasetList / 卡片委托 / applyDatasetFilter
复用 Api3dRepository / refreshAnalysis 三维体/切片/异常 mock 与合并注入
复用 Column2DDataset 二维分析 tab不动
退役 Column3DDataset / Column3DAnalysis 功能拆分到 CategorySection / VtkViewToolbar / 三维体段

13. 非目标

  • 不改二维分析。
  • 不改后端接口、不新增后端字段(装置类型走客户端已有数据)。
  • 不切换三维体/切片/异常持久化为真实后端。
  • 不做跨 GS 拼体、不做装置类型以外的新筛选维度。