geopro/docs/superpowers/specs/2026-06-11-dataset-detail-v...

284 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.

# 数据集详情视图(平面图表)改造 设计
- 日期2026-06-11
- 分支建议:`feat/dataset-detail-chart`
- 状态:设计(待评审)
- 参考材料:
- 客户端菜单:`D:\Projects\GEOPRO\Geopro3.0 菜单.xlsx`「客户端」页签R051R096、「测井参数表」「DD类型」
- 原 web 系统:`http://tenant.geomative.cn/#/projectSpace/datasetMange/datasetInfo`(经 Playwright 操作页面 + 抓取 JS chunk 做源码级分析)
- API`docs/apis/business_OpenAPI.json`
- 参考截图:`assets/web-datasetinfo-scatter.png`(原数据散点)、`assets/web-datasetinfo-grid-with-anomaly.png`(网格等值面 + 异常叠加 + 异常表)
---
## 1. 背景与问题
客户端工作台的「数据详情」dock 当前实现有两个问题:
1. **用了 VTK 渲染**`main.cpp` 下方「数据详情」dock 是一个独立的 `QVTKOpenGLStereoWidget` + `vtkRenderer``rebuildDetail` lambda 用 `vtkBandedPolyDataContourFilter`/散点/电极 actor 配合 `applyTop2D` 正交相机"平躺"渲染剖面。数据集详情本质是**平面图表**,不该走 3D 渲染管线。
2. **从未真正接上数据集选择**`main.cpp:705-713` 数据集列表单击只调 `nav.selectDataset(dsId)`(驱动右下「数据集属性」表单),`main.cpp:604` 创建的 `currentDsId` 此后**再无任何赋值**`itemClicked` 也不触发 `rebuildDetail`。即详情图表收不到选中的 dsId点数据集时它是空的。
本设计将「数据详情」dock 重建为**本地面板 + 平面图表**QGraphicsView接真实 API并真正接上数据集选择链路目标 100% 复刻原 web 系统 datasetInfo 页面的展示功能。
---
## 2. 目标与范围
### 2.1 目标
- 数据详情 dock 改为平面图表QGraphicsView**不再使用 VTK 渲染窗口**VTK 仅作几何算法库)。
- 100% 复刻原 web datasetInfo 的**展示**:原数据散点视图、网格等值面视图、色阶图例、等值线/标注、异常叠加 + 异常列表。
- 接真实 API`ApiDatasetRepository`)。
- 架构按 **dd 类型驱动的图表策略框架**搭建,首版落地 `dd_inversion_data`ERT 反演),其余 dd 类型作为框架内后续扩展。
### 2.2 范围内(仅展示)
- 原数据/网格数据 视图切换
- 网格等值面渲染(混合方案,见 §8+ 等值线 + 标注
- 原数据散点渲染
- 色阶图例(原数据 type1 / 网格 type2
- 显示异常/电极/等值线 开关
- 异常叠加(剖面上)+ 底部异常列表数据集级§7.3 辨析)
- 多 Tab 壳一个或多个数据集详情页R095
### 2.3 范围外(编辑/工具类,后续单独立项)
网格化参数GridDialog、色阶配置colorEditor、白化WhiteningDialog、滤波/迭代处理、异常框注/自动标注AutoAnnotationDialog、另存为SaveAsDialog、导出ExportDialog、描述富文本编辑、大视图(Esc) 全屏。
### 2.4 后续 dd 类型(框架内扩展,非本 spec
`dd_ert_measurement_data` / `dd_ert_measurement_gr_data` / `dd_grid` / `dd_trajectory_data`、测井(`y深度-x数值折线` / `x时间-y数值曲线`见「测井参数表」、GPR`dd/gpr/channel/image`、TEM 等。
> 现实约束:当前可访问租户只有 ERT/TEM/GPR 三类,且 GPR 对象无数据、无测井数据。其它 dd 类型详情页**无活样本可参照**,须待拿到数据样本后再精确复刻。
---
## 3. 原 web 系统分析(源码级)
> 方法Playwright 操作 datasetInfo 页面 → 抓取无障碍快照、网络请求、localStorage、活 API 响应;进一步抓取并分析前端 JS chunk`contourChartAdapter`/`plotlyScatterSimpleChart`/`contourPage`/`colorUtils` 等)。无 sourcemap代码已压缩但可读。
### 3.1 页面结构(客户端只取内容区,左侧 web 导航不复刻)
标题ds 名)→ `原数据 | 网格数据` 切换 → 工具条 → 图表 → 色阶图例 →(网格视图)底部 `异常列表 | 描述` 表。
### 3.2 原数据散点 —— Plotly `scattergl``plotlyScatterSimpleChart`
```
type:"scattergl", mode:"markers",
marker:{ size:10-12, color:vlist, colorscale, zmin/zmax, symbol:"square",
line:{color:white, width:1} }
yaxis.scaleanchor = x // x:y 等比锁定
colorbar:{ title:"数值", thickness:15, len:.7 } // 可选
```
方形点、按 `vlist` 经色阶着色、白描边、等比。
**客户端用 `QGraphicsRectItem`(方块)按色阶着色复刻输出,不引入 Plotly。**
### 3.3 网格等值面 —— 自制 marching-squares 引擎(`contourChartAdapter`canvas
源码为自带等值线库(`window.contourCore`API`computeContours / marchingSquares / pathFinding / levels / smooth / nullHandling / labels / colorbar / renderers / axes / Overlay`。
- **渲染模式**`heatmap``putImageData` 逐格 + canvas 缩放平滑)/ `fill`(填充色带 `drawFilledPaths`/ `lines`(等值线 `drawStrokePaths`/ `fill+lines`(默认显示:色带 + 等值线 + 标注)。
- **levels** = colorBar 离散分段值;`mapColors(v, vmin, vmax, colorscale)` 着色。
- **额外处理**2× 双线性上采样(`DEFAULT_UPSAMPLE_SCALE:2`+ 平滑(`DEFAULT_SMOOTHING:.3`+ 简化容差(`simplifyTolerance` 默认 `.5`,滑块)+ **数据凸包裁剪**`makeBinaryMask`/`createClipPath`/`DEFAULT_CLIP_LEVEL:.95`,对应截图里随地形起伏的不规则白边)+ 标注(`showLabels:true`)。
### 3.4 数据接口(全部真实 API 验活)
| 用途 | 接口 | 返回(关键字段) |
|---|---|---|
| 原数据散点 | `GET dd/ert/inversion/getErtRawDataScatterGraph/{dsId}` | `xlist/ylist/vlist/hlist/projectXList/projectYList`(各 1D本例 240、`min/max` |
| 网格等值面 | `GET dd/ert/inversion/rows/{dsId}` | `DDErtInversionGraphDataVO``x[nx]`、`y[ny]`、`v[ny][nx]`、`z[ny][nx]`(地形)、`elevation[nx]`、`vmin/vmax`、`sectionType`(1垂直/2水平) |
| 色阶 | `POST lvl/colorGradation/getDetail`body `{dsObjectId, businessCode:"", type}`type1=原数据 / type2=网格) | `data.properties.colorBar=[[值,"rgba(r,g,b,a)"],…]`、`lvlMinMax`、`lineConfig{showLines,color,lineType}`、`labelConfig{showLabels,color}` |
| 异常 | `GET exception/queryException/{dsId}` | 见 §6.4(与 `core::Anomaly` 一致) |
> 注:网格等值面数据来自服务端**已网格化**的 `inversion/rows`,前端不做插值;前端"网格"按钮是把 `xSpacing/xsize…` POST 给 `inversion/grid` 让服务端重算(范围外)。
---
## 4. 架构总览
**核心思路**:详情 dock 的渲染器从「VTK 3D 管线」换成「VTK 仅算等值面几何 + QGraphicsView 画 2D 场景」。数据模型、仓储接口复用,新增渲染器 + 面板壳 + dd 策略 + API 仓储 + 一个小详情控制器;并真正接上数据集选择链路。
```
┌─ 数据详情 dock重建──────────────────────────────────────┐
│ DatasetDetailPanel (QTabWidget 壳, 每 ds 一页, R095 多Tab) │
│ └─ DatasetDetailPage │
│ ├─ 标题栏ds 名) │
│ ├─ 原数据 / 网格数据 切换 + 显示异常/电极/等值线 开关 │
│ ├─ DatasetChartView : QGraphicsView ← 新渲染器 │
│ │ scene: 等值面色带 / 散点方块 / 坐标轴 / 异常 / 图例 │
│ └─ AnomalyTablePanelds 级异常表, 行显隐→图表叠加) │
└─────────────────────────────────────────────────────────────┘
右上「对象异常」面板(不动)= TM 异常总和, 眼睛→中央 VTK 地图
右下「数据集属性」面板(不动)= dynamicForm 表单
▲ 渲染输入(复用现有 core 模型) ▲ dd 类型分派
┌─ chart 层(新)──────────────────┐ ┌─ 策略注册(新)──────────────┐
│ ContourBuilder (src/render/) │ │ IDatasetChartStrategy │
│ VTK 仅算法: 上采样+平滑→ │ │ + ErtInversionStrategy(首个)│
│ vtkBandedPolyDataContourFilter │ │ registry: ddCode → strategy │
│ → 分层带多边形+等值线(几何,无窗口)│ │ (扩展点: 测井/时序/GPR…) │
└──────────────────────────────────┘ └──────────────────────────────┘
▲ 数据模型(复用,零改动)
┌─ core::Grid / ScatterField / ColorScale / Anomaly ──────────┐
└─────────────────────────────────────────────────────────────┘
▲ 加载
┌─ IDatasetRepository接口已存在───────────────────────────┐
│ loadGrid / loadScatter / loadColorScale / loadAnomalies │
│ · ApiDatasetRepository新, 真实 API, §6.3
│ · LocalSampleRepository保留, 离线/测试) │
└─────────────────────────────────────────────────────────────┘
▲ 编排(新小控制器, 不污染 WorkbenchNavController
┌─ DatasetDetailController新, 持 IDatasetRepository + 注册表)┐
│ 在 main.cpp 与 nav.selectDataset 并联挂到 datasetList 单击 │
│ 读 ddCode → 选策略 → 拉数据 → 发信号 → 面板被动渲染 │
└─────────────────────────────────────────────────────────────┘
```
**关键边界**
- **中央 2D/3D 地图视图仍是 VTK**(在地图上显示数据集是 VTK 的正职),本次只换数据详情 dock。
- **VTK 在新链路里只当算法库**(无 render window**新增依赖仅 Qt Widgets 的 `QGraphicsView`**LGPL 合规),无第三方图表库。
---
## 5. 组件细化
### 5.1 `ContourBuilder`(新,`src/render/`VTK 仅算法)
复用 `GridContourActor.cpp:36-82` 已验证的「`core::Grid` → `vtkStructuredGrid``vtkDataSetSurfaceFilter``vtkBandedPolyDataContourFilter`(按 colorBar 真实非均匀分段 + `GenerateContourEdgesOn`)」管线,**只在输出端分叉**:不建 mapper/actor而是从 `banded->GetOutput()`(port0, cell 标量) 提取色带多边形、从 port1 提取等值线折线。
混合保真预处理(见 §8入网格先 **2× 双线性上采样 + 高斯平滑** → banded → 按 NaN 掩膜做**数据凸包裁剪** → 等值线 Douglas-Peucker **简化**(容差参数)。
- 入:`core::Grid`(含 NaN 标记无效区)+ `core::ColorScale`(分段值)+ `ContourOptions{upsample, smooth, simplifyTol, showLines, showLabels}`
- 出:`std::vector<BandPolygon{ core::Rgba color; std::vector<core::Vec2> ring; }>` + `std::vector<ContourLine{ double level; std::vector<core::Vec2> pts; }>`
- 纯函数、无 Qt 依赖、可单测(给定网格→断言色带数/层级/裁剪边界)。
### 5.2 `DatasetChartView : QGraphicsView`(新,`src/app/panels/chart/`
`QGraphicsScene`,对外:`showContour(grid, scale, opts)` / `showScatter(field, scale)` / `setAnomalies(list, hidden)` / `setOverlays(showAnomaly, showElectrode, showContourLine)`
- Item色带 `QGraphicsPathItem`(填充)、等值线 `QGraphicsPathItem`、散点 `QGraphicsRectItem`(方块, 白描边)、异常 `QGraphicsPathItem`(可拾取)、电极顶部标记、标注 `QGraphicsSimpleTextItem`
- **坐标轴**viewport 层 overlay 自绘,监听 `transform` 变化重算刻度(缩放/平移时轴钉边缘)。**y 轴用高程、向上为正**(场景 y 翻转);散点视图 **x:y 等比**(对齐 Plotly `scaleanchor`)。
- 交互:滚轮缩放、拖动平移、点击异常高亮、重置视图。
- **同一个类**供详情页、(后续)大视图、自动框注对话框复用——普通 QWidget、无 OpenGL 上下文负担。
### 5.3 dd 类型策略(新,扩展点,`src/app/panels/chart/`
```cpp
struct IDatasetChartStrategy {
virtual ~IDatasetChartStrategy() = default;
virtual std::string ddCode() const = 0;
virtual void load(DatasetDetailController&, const std::string& dsId) = 0; // 决定拉哪些数据、画什么
};
```
- `ErtInversionStrategy`(首个,`ddCode()=="dd_inversion_data"`):原数据→`loadScatter`+散点;网格→`loadGrid`+`ContourBuilder`+色带/等值线;异常→`loadAnomalies`。
- `ChartStrategyRegistry``ddCode → strategy`;未注册 → 页内占位「暂不支持该类型预览」(优雅降级)。
### 5.4 `ApiDatasetRepository`(新,实现 `IDatasetRepository`,真实 API`src/data/api/`
| 方法 | 接口 | 解析(→ `src/data/parse/` |
|---|---|---|
| `loadScatter(dsId)` | `getErtRawDataScatterGraph/{dsId}` | → `ScatterField`xlist/ylist/hlist/vlist→x/y/z/vprojectX/Y |
| `loadGrid(dsId)` | `inversion/rows/{dsId}` | `DDErtInversionGraphDataVO``Grid`x/y/v/z/elevation/vmin/vmax |
| `loadColorScale(dsId, variant)` | `colorGradation/getDetail`(type1/2) | `properties.colorBar[[值,rgba]]``ColorScale::addStop` |
| `loadAnomalies(dsId)` | `queryException/{dsId}` | → `core::Anomaly[]`§6.4 |
> 现有 `IDatasetRepository` 的 `loadColorScale` 需加 `variant`(原数据/网格) 参数;`loadScatterColorScale` 可并入。`LocalSampleRepository` 保留为离线/测试桩。
### 5.5 `AnomalyTablePanel`(新或复用 `AnomalyListPanel``src/app/panels/`
ds 级异常表,列:名称 / 异常类型 / 几何类型 / 创建时间 / 备注 / 操作(仅展示版:显隐眼睛;定位)。行显隐 → 发信号驱动 `DatasetChartView::setAnomalies` 的 hidden 集(对齐现有 `hiddenAnoms` 语义)。
### 5.6 `DatasetDetailPanel` / `DatasetDetailPage`(新,`src/app/panels/`
- `DatasetDetailPanel``QTabWidget`,按 dsId 去重托管多个 `DatasetDetailPage`R095
- `DatasetDetailPage`:标题 + 原数据/网格数据 切换 + 叠加开关 + `DatasetChartView` + `AnomalyTablePanel`
### 5.7 `DatasetDetailController`(新,`src/controller/`
`IDatasetRepository&` + `ChartStrategyRegistry&`slot `openDataset(dsId, ddCode)`:选策略→拉数据→`emit chartReady(pageModel)`;被动视图。**不并入 `WorkbenchNavController`**(后者只持 `IProjectRepository`、专注项目/结构/异常树导航,混入图表数据会破坏单一职责)。
---
## 6. 数据模型与映射
### 6.1 复用的 core 模型(零改动)
- `core::Grid``Field.hpp``nx/ny/valueAt(i,j)`、`x/y`、`z[nx*ny]`、`elevation[nx]`、`vmin/vmax` ⇆ `DDErtInversionGraphDataVO`
- `core::ScatterField``x/y/z/v`、`projX/projY` ⇆ 散点 VO。
- `core::ColorScale`:阶梯色阶 `addStop/colorAt/stopValues` ⇆ colorBar。
- `core::Anomaly``markType`(点/线/面)、`localPts`、`lineColor/lineWidth/dashed` ⇆ queryException。
### 6.2 NaN / 无效区
`inversion/rows``v` 在测区外为无效;`core::Grid` 用 NaN 标记,`ContourBuilder` 据此做凸包裁剪§8渲染成不规则白边。
### 6.3 色阶colorBar
`properties.colorBar``[[值字符串, "rgba(r,g,b,a)"], …]`(本例 17 段),`type1`(原数据)/`type2`(网格) 颜色相同、分段值不同。映射:每段 `addStop(parseDouble(值), core::parseColor(rgba))``lineConfig` 驱动等值线显隐/色/线型。
### 6.4 异常queryException → core::Anomaly活数据验证
```
exceptionName → name
exceptionTypeName(异常区) → typeName
exceptionMarkType(1点/2线/3面) → markType
exceptionMarkTypeName(多段线…) → 表"几何类型"列
location.coordinate[{x,y}] → localPts (剖面局部坐标: x=桩号, y=高程)
legend.polylineColor/Width → lineColor/lineWidth
legend.polylineShape=="dash"→ dashed
createTime/remark → 表"创建时间/备注"列
geographicalCoordinates / latitudeLongitude → (地图用, 详情视图不需要)
```
> 客户端已有 `parseExceptions` 映射此结构(`Anomaly.hpp` 注释印证),活数据已验证一致。
---
## 7. 数据流与交互
### 7.1 选择 → 渲染
`datasetList` 单击 → 同时:(a) `nav.selectDataset(dsId)`(现状,驱动右下属性表单);(b) `detailCtrl.openDataset(dsId, ddCode)`(新)→ 选策略 → 拉 scatter/grid/colorScale/anomalies → `emit chartReady``DatasetDetailPanel` 渲染。
### 7.2 Tab 与联动R055-057, R095
- 双击数据集 = 新建或聚焦已开页(按 dsId 去重);单击 = 聚焦已开页(若已打开)。
- 活动 Tab 切换 → 反向高亮数据集列表对应项。
- 视图切换 原数据/网格数据;叠加开关 显示异常/电极/等值线。
### 7.3 异常归属辨析(决策记录)
原 web datasetInfo 是**单页**,底部异常表是单页布局产物。客户端是**多面板工作台**,存在两处不同作用域的异常显示,**经决策保留两者、各管各的视图**
| 位置 | 接口 | 作用域 | 眼睛控制 |
|---|---|---|---|
| 右上「对象异常」面板(现状不动) | `queryExceptionByTmObjectId/{tmId}` | 对象/TM 级(勾选驱动,多 TM 聚合) | 中央 VTK 2D/3D **地图** |
| 详情 dock 底部「异常列表」(本 spec, 复刻 web | `queryException/{dsId}` | 数据集级(更直观) | **图表剖面**叠加 |
剖面上的**异常叠加**属"画在图上",天然归详情视图;右上面板的列表与地图叠加不变。
---
## 8. 渲染保真度(混合方案)
网格等值面采用 **VTK 算法 + 预处理** 混合方案(~95% 保真,复用 VTK不重写整套引擎
1. **2× 双线性上采样** `core::Grid`(对齐 web `DEFAULT_UPSAMPLE_SCALE:2`)——可用 `vtkImageData` + `vtkImageResize`/手写双线性。
2. **高斯平滑**(对齐 web `DEFAULT_SMOOTHING:.3`)。
3. `vtkBandedPolyDataContourFilter` 按 colorBar 分段值出**色带多边形 + 等值线**。
4. **数据凸包裁剪**:按 NaN 掩膜裁掉测区外(对齐 web `makeBinaryMask`/`clipLevel .95`),得不规则白边。
5. 等值线 **Douglas-Peucker 简化**(容差参数,默认 0.5,对应"简化容差")。
6. 默认 `fill+lines + showLabels`;等值线色/显隐由 colorBar `lineConfig` 驱动。
原数据散点:`QGraphicsRectItem` 方块、按色阶着色、白描边、**x:y 等比**(对齐 Plotly `scaleanchor`)。
> 与「轻量 VTK banded 原生」(~85%, 矩形范围/块状) 和「高保真整体移植引擎」(~100%, 工作量最大) 相比,混合方案是保真度/工作量的折中。
---
## 9. 错误 / 空 / 加载态
- 加载中:图表区骨架/转圈。
- 空态:异常表「暂无数据」;未注册 ddCode「暂不支持该类型预览」无网格/散点数据时空图占位。
- 错误API 失败 → 页内内联错误(沿用 `RepoResult` 错误传播,不崩)。
- 跨项目上下文/数据过期:优雅降级。主题令牌复用(明暗一致)。
---
## 10. 测试策略GoogleTest/CTest
- **单元**`ContourBuilder`(给网格→断言色带数/层级/凸包裁剪/简化点数);`ColorScale`colorBar 解析、`colorAt` 边界);解析器(散点/反演网格/色阶/异常 VO→模型`ChartStrategyRegistry` 查找与降级。
- **组件**`DatasetChartView` 按数据生成 scene item 数;叠加开关隐藏对应 item`AnomalyTablePanel` 行开关发信号;`DatasetDetailPanel` 按 dsId 去重 Tab。
- **集成**`DatasetDetailController.openDataset` 编排(用 `LocalSampleRepository` 桩)→ 断言 `chartReady` 负载。
- **视觉**:对照 `assets/web-datasetinfo-*.png` 人工核对散点/等值面/异常叠加。
---
## 11. 风险与开放问题
1. **凸包裁剪精度**web 用 binary mask + clipLevel .95VTK 侧用 NaN 掩膜裁剪需调参,可能与 web 边界略有出入(在 95% 目标内可接受)。
2. **坐标轴钉边重算**QGraphicsView 缩放时轴刻度重算是已知但非免费的模式,需专门处理。
3. **`IDatasetRepository.loadColorScale` 签名变更**(加 variant会动到 `LocalSampleRepository` 与现有 VTK 详情调用点——后者将随本次重建移除,注意配平。
4. **其它 dd 类型无活样本**(测井/GPR/TEM 详情页):框架预留,落地需数据样本。
5. **大数据集性能**:散点 240 点、网格 32×100→上采样 64×200 量级QGraphicsView 可承受更大测线需评估web 用 `hightPerformaceContainer`,本版先不优化)。
---
## 12. 落地顺序(供 plan 细化)
1. `core::Grid` NaN 支持 + `ContourBuilder`VTK 几何提取 + 上采样/平滑/裁剪/简化)+ 单测。
2. `ApiDatasetRepository`4 接口 + 解析)+ `loadColorScale` variant + 单测。
3. `DatasetChartView`(散点/等值面/轴/图例/异常叠加 + 交互)。
4. dd 策略框架 + `ErtInversionStrategy` + 注册表。
5. `AnomalyTablePanel` + `DatasetDetailPage/Panel`(多 Tab
6. `DatasetDetailController` + `main.cpp` 接线(移除旧 VTK 详情 dock接上数据集单击/双击 + 反向联动)。
7. 视觉核对 + 集成测试。