23 KiB
数据集详情视图(平面图表)改造 设计
- 日期:2026-06-11
- 分支建议:
feat/dataset-detail-chart - 状态:ERT 反演(dd_inversion_data)展示功能已落地并经用户验收;其余 dd 类型 + 工具条编辑功能待后续
状态更新(2026-06-11)
架构偏离(重要): spec 原定渲染器为 QGraphicsView,实际落地改用 QwtPlot(轴/交互/图例)+ VTK 算法层(等值线几何)+ 连续/离散色阶(见返工方案 plans/2026-06-11-dataset-detail-chart-v2-qwt.md,权威)。展示结果视觉等价,下文 §5.2/§8 的 QGraphicsView 细节已被 QwtPlot 方案取代,保留作背景参考。
已完成(仅 dd_inversion_data ERT 反演,§2.2 展示范围内): 原数据散点(方形点/白描边/连续色阶/x 轴顶部)+ 网格等值面(填充栅格 + 黑色等值线 + 沿线数值标注 + NaN 白边裁剪)+ 色阶图例 + 异常叠加 + 底部异常表/描述 + 多 Tab + 网格数据懒加载 + 页签内滚动/分割条 + 实时平移/滚轮缩放。数据加载已异步化(见 specs/2026-06-11-apiclient-async-design.md)。
未完成:
- 其余 dd 类型的详情图渲染(§2.4):
dd_ert_measurement_data、dd_ert_measurement_gr_data、dd_grid、dd_trajectory_data、测井(深度/时序折线)、GPR(dd/gpr/channel/image)、TEM 等。控制器目前对非dd_inversion_data直接「暂不支持该类型预览」。现实约束:当前租户仅 ERT/TEM/GPR 三类,GPR 对象无数据、无测井数据 → 多数类型无活样本,须先取样本。 实现计划见plans/2026-06-11-dataset-detail-other-dd-types.md(如已生成)。 - 工具条编辑功能(§2.3,范围外/后续单独立项):白化 / 滤波处理 / 色阶配置 / 异常框注 / 自动标注 / 网格化 / 另存为 / 导出 / 描述富文本 / 大视图全屏。当前为占位按钮。
- 加载态:网格懒加载已有「加载中」遮罩;原数据初次加载仅 busy 光标,未做骨架屏。
- 参考材料:
- 客户端菜单:
D:\Projects\GEOPRO\Geopro3.0 菜单.xlsx「客户端」页签(R051–R096)、「测井参数表」「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 当前实现有两个问题:
- 用了 VTK 渲染:
main.cpp下方「数据详情」dock 是一个独立的QVTKOpenGLStereoWidget+vtkRenderer,rebuildDetaillambda 用vtkBandedPolyDataContourFilter/散点/电极 actor 配合applyTop2D正交相机"平躺"渲染剖面。数据集详情本质是平面图表,不该走 3D 渲染管线。 - 从未真正接上数据集选择:
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: 等值面色带 / 散点方块 / 坐标轴 / 异常 / 图例 │
│ └─ AnomalyTablePanel(ds 级异常表, 行显隐→图表叠加) │
└─────────────────────────────────────────────────────────────┘
右上「对象异常」面板(不动)= 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 等比(对齐 Plotlyscaleanchor)。 - 交互:滚轮缩放、拖动平移、点击异常高亮、重置视图。
- 同一个类供详情页、(后续)大视图、自动框注对话框复用——普通 QWidget、无 OpenGL 上下文负担。
5.3 dd 类型策略(新,扩展点,src/app/panels/chart/)
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/v,projectX/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,不重写整套引擎):
- 2× 双线性上采样
core::Grid(对齐 webDEFAULT_UPSAMPLE_SCALE:2)——可用vtkImageData+vtkImageResize/手写双线性。 - 高斯平滑(对齐 web
DEFAULT_SMOOTHING:.3)。 vtkBandedPolyDataContourFilter按 colorBar 分段值出色带多边形 + 等值线。- 数据凸包裁剪:按 NaN 掩膜裁掉测区外(对齐 web
makeBinaryMask/clipLevel .95),得不规则白边。 - 等值线 Douglas-Peucker 简化(容差参数,默认 0.5,对应"简化容差")。
- 默认
fill+lines + showLabels;等值线色/显隐由 colorBarlineConfig驱动。
原数据散点: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. 风险与开放问题
- 凸包裁剪精度:web 用 binary mask + clipLevel .95;VTK 侧用 NaN 掩膜裁剪需调参,可能与 web 边界略有出入(在 95% 目标内可接受)。
- 坐标轴钉边重算:QGraphicsView 缩放时轴刻度重算是已知但非免费的模式,需专门处理。
IDatasetRepository.loadColorScale签名变更(加 variant)会动到LocalSampleRepository与现有 VTK 详情调用点——后者将随本次重建移除,注意配平。- 其它 dd 类型无活样本(测井/GPR/TEM 详情页):框架预留,落地需数据样本。
- 大数据集性能:散点 240 点、网格 32×100→上采样 64×200 量级,QGraphicsView 可承受;更大测线需评估(web 用
hightPerformaceContainer,本版先不优化)。
12. 落地顺序(供 plan 细化)
core::GridNaN 支持 +ContourBuilder(VTK 几何提取 + 上采样/平滑/裁剪/简化)+ 单测。ApiDatasetRepository(4 接口 + 解析)+loadColorScalevariant + 单测。DatasetChartView(散点/等值面/轴/图例/异常叠加 + 交互)。- dd 策略框架 +
ErtInversionStrategy+ 注册表。 AnomalyTablePanel+DatasetDetailPage/Panel(多 Tab)。DatasetDetailController+main.cpp接线(移除旧 VTK 详情 dock,接上数据集单击/双击 + 反向联动)。- 视觉核对 + 集成测试。