geopro/docs/superpowers/specs/2026-06-30-vtk-merged-datas...

257 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# VTK 视图重构:合并数据集单栏 + 动态段 + 图标工具条 + 2D 平面底图 — Spec2026-06-30
> 分支起点:`main`PR #9 已合并。职责范围VTK 视图左侧数据集栏(面板结构 + 段交互 + 2D/3D 共存渲染 + 底图)。
> 本 spec 替代 2026-06-26「二维分析锁定俯视」模型的相机/显隐部分(见 §10 迁移说明)。
## 0. 一句话目标
把「三维分析 + 二维分析」两个 tab 合并成**一个无标题的单列数据集栏**2D 与 3D 数据集用**一致的分段组织**同列呈现;段按数据有无**动态显隐**;段操作改**响应式图标工具条**2D 数据以**按类型一块平面**的方式与 3D 体/帘面在**同一个自由透视场景**里共存。
---
## 1. 背景与现状(接手必读)
- **面板**`ColumnDrawer` = 左侧抽屉,`QTabWidget` 两 tab
- 三维分析 `CategoryAnalysisTab``QScrollArea` 竖堆 `CategorySection`×4电阻率/视电阻率/瞬变/三维体)。
- 二维分析 `Column2DDataset`:平铺树 + 底图下拉 + 2D视图模式下拉 + 自定义Z 滑块。
- 切 tab 发 `analysisModeChanged(bool is2D)`
- **段** `CategorySection`段头chevron+标题 新增三维体[反演类] 导入雷达[voxel]+ 段体(**固定显示**的筛选行[日期范围+装置类型] + 可勾选数据树)。
- **分类**3D 用 `splitByCategory()``categoryConfigs()` 4 段2D 用 `splitByDimension().dim2D`(当前仅 `dd_trajectory_data`)。
- **渲染**
- 2D 轨迹 → `MapLineActor`(橙色 `vtkPolyLine`),经 `VtkSceneController::set2DPlacement(mode,z)` 摆到**单一全局 Z**5 模式0关/1 Z=0/2顶+50/3底-50/4自定义。已有逐 ds 的 Z 拖动偏移 `mapLineZOffset_`
- `VtkSceneView::setAnalysisMode2D(is2D)`:切 tab 时**按维度翻 actor 可见标志** + 相机锁定近俯视 + `VtkViewToolbar` 禁 6 向视图。
- 底图 `TileBasemap`(单例):天地图 WMTS卫星 `img_w`→`buildWarped` 带高程地形 / 矢量 `vec_w`→`buildFlat` 纯平面),透明度**固定 0.55**,瓦片范围 = `dataHorizontalRadius()×10``[2000,30000]m`,置 Z=0。`buildFlat` 已能纯平贴矢量瓦片。
- **顶部工具条** `TopBar`:视图/项目管理/业务工具/**设备** 四菜单。
- **渲染区竖排工具栏** `VtkViewToolbar`段1 `Gear`(坐标轴设置) → 分隔线 → 6向视图 → 缩放。
---
## 2. 已确认的关键决策(与用户逐条确认)
1. **单一自由场景共存**:合并删 tab 后,**取消**「锁定近俯视相机 + 按维度自动显隐」。场景恒为自由透视,勾选的 2D 平面与 3D 体/帘面**同时可见**。
2. **底图 = 1 个 3D 底图 + N 个 2D 底图**:所有三维数据**共用一个** 3D 底图(现状 `TileBasemap`);每个 **2D 类型(段)** 一块**独立平面底图**N = 2D 段数,非每条 ds 一张)。
3. **默认勾选**:沿用现有「直接挂项目下的 ds 默认进 VTK」逻辑本次只保证动态显隐段时不破坏它。
4. **2D z 值按类型一块平面**:每个 2D 类型一块平面 + 一个 z 滑块;平面初始 z = 该类型**第一个被勾选 ds** 的 z同类型其余 ds 投影到此平面。
5. **3D 底图控件移到 `VtkViewToolbar`**Gear 之后),不在 3D 段上。
6. **「导入雷达」移到 `TopBar` 设备菜单**(临时测试功能,后续整体移除)。
7. **`view2DMode` 5 模式下拉废弃**2D 高度完全由「z值」滑块替代。
---
## 3. 架构方案
**采用方案 A统一单列 `DatasetColumn` + 类型抽象§5。**
- 用**一份类目描述符目录 `categoryCatalog()`**§5每类型一份 `CategoryDescriptor`:分类/筛选/操作/渲染策略)同时驱动 3D/2D 段;`CategorySection` 按描述符建筛选器与图标条;`VtkSceneController` 按描述符的 `renderStrategyId` 查可插拔渲染策略§5.4)渲染。**消费方不再 `if dimension/ddCode` 散判**。
- 删除 `Column2DDataset``QTabWidget``ColumnDrawer` 承载单个 `DatasetColumn`,保留折叠开关。
- 取舍:复用已验证的勾选保留/折叠/spinner/结构树逻辑改动集中在描述符目录、段头工具条OpKind 映射)、渲染策略注册表。
(备选 B「两 widget 去 tab 竖堆」因 2D/3D 段不一致、重复逻辑被否C「全重写」工作量过大被否。
---
## 4. 面板结构§1 重构)
- `ColumnDrawer`:去 `QTabWidget` + `Column2DDataset`;改持 `DatasetColumn`**移除** `analysisModeChanged` 信号链。折叠开关保留。
- `DatasetColumn`(原 `CategoryAnalysisTab` 改名/改造):`QScrollArea` 竖堆 N 个 `CategorySection`**无栏目标题**。
- **动态显隐**:段 bucket 为空 → 段 `hide()`;非空 → `show()`。三维体段同理(默认空→默认不显示)。`relayoutSections()`/stretch 逻辑只对可见段生效。
- **空面板占位**:所有段均空时,滚动区中央显示提示语占位(文案:「请在左侧对象树勾选测线 / 数据集」);任一段非空则隐藏占位。
---
## 5. 类型抽象(扩展契约 —— 本 spec 的架构基石)
> 目标:**接入一个新 ds 类型 = 实现一份描述符(必要时再补一个渲染策略 / 一个操作 / 一个筛选器)**无论它「按什么规则接入、有什么操作、怎么渲染」UI 层与渲染层都只消费抽象、不再 `if dimension/ddCode` 散判。这是「数据集栏目」的统一规范,所有现存 5 类与未来新类都走它。
### 5.1 类目描述符 `CategoryDescriptor`data 层,纯 C++,无 Qt/VTK
```cpp
namespace geopro::data {
enum class SceneKind { Volume3D, Curtain3D, Plane2D }; // 渲染语义 / 共存规则
enum class FilterKind { DateRange, ArrayType }; // 筛选器契约(可扩展)
enum class OpKind { GenerateVolume, Filter, PlaneZ, Basemap }; // 段操作契约(可扩展)
struct CategoryDescriptor {
std::string id; // "resistivity"/"apparent"/"transient"/"voxel"/"trajectory" ...
std::string title; // 段标题
SceneKind sceneKind; // 渲染语义
std::function<bool(const DsRow&)> classify; // 轴1 数据来源/分类("无论按什么规则接入"
std::vector<FilterKind> filters; // 轴2 本段筛选器(顺序=显示顺序)
std::vector<OpKind> operations; // 轴3 段头图标操作(顺序=显示顺序)
std::string renderStrategyId; // 轴4 渲染策略键(解析到注册表,见 §5.4
};
// classify 便捷构造器(覆盖现有按 ddCode / dsTypeCode 接入的常见情形;任意复杂规则可直接写 lambda
std::function<bool(const DsRow&)> byDdCode(std::initializer_list<std::string> codes);
std::function<bool(const DsRow&)> byDsTypeCode(std::initializer_list<std::string> codes);
const std::vector<CategoryDescriptor>& categoryCatalog(); // 有序目录,取代 categoryConfigs()
} // namespace geopro::data
```
### 5.2 目录定义catalog取代 `categoryConfigs()`
| id | title | sceneKind | classify | filters | operations | renderStrategyId |
|---|---|---|---|---|---|---|
| resistivity | 电阻率数据 | Curtain3D | `byDsTypeCode({"ERT platform inversion data"})` | DateRange,ArrayType | GenerateVolume,Filter | `"curtain"` |
| apparent | 视电阻率数据 | Curtain3D | `byDsTypeCode({"visual resistivity data"})` | DateRange,ArrayType | GenerateVolume,Filter | `"curtain"` |
| transient | 瞬变电磁数据 | Curtain3D | `byDsTypeCode({"DD TRANSIENT ELECTROMAGNETIC INVERSION"})` | DateRange | GenerateVolume,Filter | `"curtain"` |
| voxel | 三维体 | Volume3D | `byDdCode({"dd_voxel"})`mock 注入,见 §10 | DateRange | Filter | `"volume"` |
| trajectory | 轨迹数据 | Plane2D | `byDdCode({"dd_trajectory_data"})` | DateRange | PlaneZ,Filter,Basemap | `"plane2d"` |
段顺序即表序(电阻率→…→轨迹)。分流:`splitByCategory(rows)` 改为遍历 catalog对每行命中**首个** `classify(row)==true` 的描述符即归入该段(保留原顺序)。`Column2DDataset` / `splitByDimension` 退役;旧 `categoryConfigs()`/`CategorySpec` 由 `categoryCatalog()`/`CategoryDescriptor` 取代。
### 5.3 消费方只认抽象
- `CategorySection(descriptor)`:按 `descriptor.filters` 建筛选器(`FilterKind→UI` 映射一处)、按 `descriptor.operations` 建图标条(`OpKind→按钮+信号` 映射一处,见 §6/§7
- `DatasetColumn`:遍历 `categoryCatalog()` 建段、用 `descriptor.classify` 路由数据(即 `splitByCategory`)。
- `VtkSceneController`:勾选某 ds → 查其所属描述符 → `renderStrategyId` → 渲染策略 `add/remove` + 首勾/全消 `onTypeActivated/Deactivated`(见 §5.4、§8
### 5.4 可插拔渲染策略 `IDatasetRenderStrategy`controller/render 层)
```cpp
namespace geopro::controller {
class IDatasetRenderStrategy {
public:
virtual ~IDatasetRenderStrategy() = default;
virtual void add(const std::string& typeId, const std::string& dsId) = 0; // 异步加载+入场
virtual void remove(const std::string& dsId) = 0;
// 每类型场景资源生命周期(可选):本类型首个 ds 入场 / 全部离场
virtual void onTypeActivated(const std::string& typeId) {}
virtual void onTypeDeactivated(const std::string& typeId) {}
};
// 注册表renderStrategyId(字符串键) → 策略实例。当前 3 实现:
// "volume" VolumeRenderStrategy —— 体素/雷达体(包现 isVolumeDataset 分支)
// "curtain" CurtainRenderStrategy —— 反演帘面(包现 dd_section 等分支)
// "plane2d" Plane2DRenderStrategy —— 2D 折线落类型平面 + 平面底图(封装 §8.2 平面 z + §9.2 底图)
} // namespace geopro::controller
```
**关键**2D 的全部特殊性(按类型平面 z 生命周期 + N 个平面底图)**封死在 `Plane2DRenderStrategy` 一个类内**(内含 `PlaneZRegistry` + 平面底图管理3D 两个策略只是包住现有渲染分支。控制器主流程不再含维度/ddCode 分支,只做「查描述符 → 取策略 → 调用」。
### 5.5 「接入一个新 ds 类型」标准动作(即本规范)
| 场景 | 要做的 |
|---|---|
| 新类型、**沿用**已有筛选/操作/渲染 | **只加一条 `CategoryDescriptor`**(纯数据),完。 |
| 新类型、要**新渲染方式** | 加描述符 + 实现一个 `IDatasetRenderStrategy` 并注册(新 `renderStrategyId`)。 |
| 新类型、要**新操作** | `OpKind` 加一项 + `CategorySection` 加该 kind→UI 的**一处**映射;描述符 `operations` 列上。 |
| 新类型、要**新筛选器** | `FilterKind` 加一项 + `CategorySection` 加**一处**映射;描述符 `filters` 列上。 |
数据驱动优先;只有真出现「新渲染/新操作/新筛选器」才动对应那**一个**扩展点,且改动收敛在单一位置,不扩散。
---
## 6. 响应式图标工具条(新建 `SectionIconBar`
- 段头右侧承载图标工具条:**默认最多显示 3 个图标**;图标总数超 3**或段宽被挤压放不下时****右侧图标依次收进末尾「…」下拉菜单**。`resizeEvent` 动态重算可见数 —— 这是**必须实现并可验证**的行为:即使图标 ≤3当栏位宽度收窄到放不下时右侧图标也要实时折进「…」栏位变宽再弹回。
- 每个图标 = `QToolButton`autoRaise + glyph + tooltip点击触发对应操作部分弹 popup
- **图标集由描述符 `operations``OpKind` 列表)驱动**,不是按维度硬编码。`CategorySection` 内有一处 `OpKind→(图标 glyph + tooltip + 点击/popup)` 映射表新增操作只加一项映射§5.5)。当前 catalog 各段对应:
| 段 | `operations`(左→右,右侧优先收进…) |
|---|---|
| 3D 反演(电阻率/视电阻率/瞬变) | `GenerateVolume`、`Filter` |
| 3D 三维体 | `Filter` |
| 2D 轨迹 | `PlaneZ`、`Filter`、`Basemap` |
- 注:当前各段图标 ≤3「数量超 3」分支暂不触发但**「宽度挤压」分支必须工作**(窄栏即折叠);两个分支都要实现(后续图标会增加,数量分支随之生效)。
---
## 7. 段内操作行为(每个 = 一个 `OpKind`
> 下列每个操作对应 §5 的一个 `OpKind``CategorySection` 的 `OpKind→UI` 映射表据描述符 `operations` 装配。`PlaneZ`/`Basemap` 的渲染落点封装在 `Plane2DRenderStrategy`§5.4/§8.2/§9.2)。
### 7.1 筛选 `OpKind::Filter`通用D2+D3
- 现「固定显示的筛选行(日期范围 + 装置类型[仅 ERT])」改为**默认折叠**(段体内不占位)。
- 点「筛选」图标 → 展开筛选行;再点 → 收起toggle。筛选逻辑`passesFilters`/`rebuildList`)不变。
### 7.2 新增三维体 `OpKind::GenerateVolume`(仅 3D 反演段)
- 保持原功能(发 `generateVolumeRequested`),入口从文字按钮改图标。
### 7.3 z值 `OpKind::PlaneZ`(仅 2D 段)
- 点图标弹 popup一个滑块整体上下移动**该 2D 类型那块平面**的 z。
- 初值 = 该类型第一个被勾选 ds 的 z见 §8.2);范围按场景高程量级合理取(实现期定)。
### 7.4 底图 `OpKind::Basemap`(仅 2D 段)
- 点图标弹 popup【底图类型矢量平面(默认) / 无】+【透明度滑块(默认 50%)】。
- 作用于**该 2D 类型自己那块平面底图**§9.2,由 `Plane2DRenderStrategy` 持有)。
### 7.5 3D 底图(移到 `VtkViewToolbar`,非段操作)
-`Gear`(坐标轴设置) 正下方新增「地图」图标按钮,点击弹 popup【底图类型天地图(默认) / 无】+【透明度滑块(默认 50%)】。
- 控制**全局唯一**的 3D 底图(`TileBasemap`):「无」= 隐藏;透明度去掉固定 `0.55`、改默认 `0.5` 可调。
### 7.6 导入雷达(移到 `TopBar` 设备菜单,临时测试)
- `TopBar` 设备菜单加「导入雷达测线」→ 子项「规范化(.head/.data)…」「Impulse(.iprb)…」,发等价于现 `radarImportRequested(impulse)` 的信号到既有导入流程。
-`CategorySection`(voxel 段头) 移除该入口及 `radarImportRequested` 转发。
---
## 8. 渲染模型(经渲染策略 §5.4
> 控制器主流程:勾选 diff → 对每个新增/移除 ds 查其描述符 → 取 `renderStrategyId` 对应策略 → `add/remove`;某类型「首勾」「全消」时调 `onTypeActivated/Deactivated`。无维度/ddCode 分支。
### 8.1 删除维度耦合
- 移除 `VtkSceneView::setAnalysisMode2D` 的「相机锁定俯视 + 按维度翻可见标志」与 `VtkViewToolbar::setAnalysisMode2D` 的 6 向禁用。场景恒自由透视2D/3D actor 同时可见(各自由勾选控制显隐)。
- 移除 `view2DMode` 5 模式与旧 `set2DPlacement` mode 维度(其职责并入 `Plane2DRenderStrategy`)。
### 8.2 2D 按类型平面(需求 5—— 封装在 `Plane2DRenderStrategy`
- 同一 2D 类型(段)勾选的全部 ds 投影到**一块平面**
- 平面 z = 该类型**第一个被勾选 ds** 的 z首个勾选时确定后**固定不变**,仅由 z 滑块整体升降)。
- 平面**纯平、不渲染高程**。
- 同类型其余 ds 的折线全部落在此平面 z。
- **平面生命周期**:该类型**首个 ds 被勾选** → 创建平面(定 z+ 创建其平面底图§9.2);期间 z 固定;该类型**全部 ds 取消勾选** → 平面**与其底图一并销毁**。
- 逐 ds 独立拖动 Z`nudgeSelectedMapLinesZ` / `mapLineZOffset_`**废弃**,统一到类型平面 z。
- 渲染复用 `MapLineActor`(折线几何不变),仅 Z 落点改为「所属类型平面 z」。
---
## 9. 底图体系§6
### 9.1 3D 共享底图1 个)
- 沿用 `TileBasemap` 单例带高程地形Z=0。透明度参数化默认 0.5,由 §7.5 popup 调);支持隐藏。瓦片范围规则不变。
### 9.2 2D 平面底图N 个,每类型一块)—— 实现选型已定
- **决策:参数化 `TileBasemap` 支持多实例**(不另抽 `PlaneBasemap`)。依据:`TileBasemap` 的「相机驱动 LOD + 四叉树细分 + 视锥剔除 + 限并发下载 + GeoLocalFrame 配准」正是平面底图所需,且其状态全为 per-instance无全局/静态状态,`tileKey` 为纯函数),多实例天然可行。新抽类要么重写数百行 LOD/网络逻辑,要么退化成单层无 LOD 大平面(缩放发虚、且违背「瓦片范围参考三维规则」)。
- **需改的 3 处硬编码**
| 改动 | 现状 | 改为 |
|---|---|---|
| 地面 Z | `kGroundZ=0` 常量 | 构造/setter 传 `groundZ`2D 平面 = 类型平面 z |
| 透明度 | `kTerrainOpacity=0.55` 固定 | 参数化,默认 0.5 可调 |
| 平面/矢量模式 | 由 `Kind` 隐含 | 复用 `Street`(vec_w)+`buildFlat`(已是纯平矢量路径,跳过 DEM/warp |
- **最终布局**1 个 `TileBasemap`3DSatellite/带高程Z=0§9.1,挂控制器)+ N 个 `TileBasemap`(每 2D 类型一个Street/纯平矢量,`groundZ`=平面 z**由 `Plane2DRenderStrategy` 持有**)。共享同一 `Scene` / `GeoLocalFrame`
- **生命周期**(在 `Plane2DRenderStrategy` 内,经 `onTypeActivated/Deactivated`):按 2D 类型持有 N 个实例 + 各自平面 z`PlaneZRegistry`);该类型首勾 → 建实例§8.2 定 z该类型全消 → 销毁实例(连同折线平面)。
- 瓦片范围**复用** `dataHorizontalRadius()×10``[2000,30000]m` 规则(各实例共享同一 `dataRadiusProvider`)。
- 坐标对齐沿用 `GeoLocalFrame`经纬→局部frame 重锚逻辑不变。
---
## 10. 信号 / 默认勾选§7
- 2D 勾选/选中信号从 `Column2DDataset` 迁到 2D `CategorySection`(复用 `checkedDatasetsChanged` / `datasetSelected`)。`basemapChanged` / `view2DModeChanged` / `customZChanged` 退役,由 §7.4/§7.5 的 popup + §8.2 平面 z 替代。
- 「直接挂项目下 ds 默认进 VTK」既有逻辑保留动态显隐段时确保默认勾选的 ds 所属段被显示且勾选状态正确。
---
## 11. 迁移说明 / 取舍 / 待定
- **替代 2026-06-26 spec**:该 spec 的「一场景两相机 + 按维度显隐 + 高程拖动分层」中,**相机锁定 + 维度显隐被本 spec 推翻**改单一自由场景共存。其「2D 沿 Z 拖动分离」语义改由「按类型平面 + z 滑块」承担——逐 ds 独立拖动 `nudgeSelectedMapLinesZ` / `mapLineZOffset_`(及拾取拖动浮层读数)**废弃移除**,统一到类型平面 z。
- **`dd_raster`** 仍未接2026-06-26 §6 遗留),本 spec 不含;后续作为新的 2D 类型段加入时复用 §8.2/§9.2 平面+底图机制。
- **3D 反演段 vs 三维体段**:均为 D3但 3D 反演段渲染为帘面、三维体段为体素/雷达体——共用 3D 底图与自由场景,无需区分。
- **责任拆分**`CategoryDescriptor`/`categoryCatalog`(描述符)、`IDatasetRenderStrategy` 注册表(渲染)、`SectionIconBar`(响应式工具条)、`TileBasemap` 多实例、`DatasetColumn`(动态显隐)相互独立,可分别实现与测试。
- **抽象的 YAGNI 边界**`FilterKind`/`OpKind`/`SceneKind` 只列当前真用到的枚举值;渲染策略只实现 `volume`/`curtain`/`plane2d` 三个。扩展点是「加新值/新策略」的预留口,不预先实现任何未用类型。
---
## 12. 验收
1. 左侧只有**一个无标题数据集栏**(无 tab其中同时出现 3D 类型段(电阻率/视电阻率/瞬变/三维体)与 2D 类型段(轨迹)。
2. 段**动态显隐**:无对应数据的段不显示;面板全空时显示居中占位提示。
3. 段头操作为**图标工具条**:默认最多 3 个,超出/挤压时右侧收进「…」下拉。3D 反演段含「新增三维体/筛选」2D 轨迹段含「z值/筛选/底图」。
4. 「筛选」图标可**展开/收起**段内筛选行(默认折叠)。
5. 勾选 2D 轨迹:同类型 ds 投影到**一块纯平平面**,平面 z = 首个勾选 ds 的 z之后固定「z值」滑块整体升降该平面「底图」popup 可换矢量平面底图/无 + 调透明度(默认 50%)。该类型**全部取消勾选 → 平面与其底图一并消失**。
6. 3D 与 2D 数据在**同一自由透视场景**同时可见,可自由旋转(无锁定俯视、无 tab 切换)。
7. 渲染区工具栏 Gear 下方新增「地图」按钮,控制全局 3D 底图(天地图/无 + 透明度默认 50%)。
8. 「导入雷达」入口出现在顶部「设备」菜单;三维体段头不再有该按钮。
9. 「直接挂项目下的 ds」加载时默认勾选进 VTK 不被破坏。
10. **可扩展性**:现存 5 类全部经 `categoryCatalog()` 描述符 + 渲染策略注册表驱动,控制器/段头无维度/ddCode 散判。新增一个「沿用已有渲染/操作/筛选」的类型,只需在 catalog 加一条描述符即可显示+渲染(以一个验证性 demo 描述符或单元测试佐证分类/路由走通)。