235 lines
18 KiB
Markdown
235 lines
18 KiB
Markdown
# Geopro 3.0 桌面客户端 — 数据集详情「二维图表」返工技术方案
|
||
|
||
**版本 v1.0** · 适用范围:数据集详情页的「原数据(散点图)」与「网格数据(填充等值线图)」
|
||
**状态**:返工指令(推翻当前 QGraphicsView 实现,重做)
|
||
|
||
---
|
||
|
||
## 0. 给开发(含 AI 编码)的硬约束 — 先读这一段
|
||
|
||
1. **不嵌入 QWebEngine,不引入 Chromium。** 整个客户端的技术路线是 Qt C++ 原生,核心动机包含离线/现场作业能力;把核心数据可视化做成依赖 web 渲染会破坏该前提,禁止。
|
||
2. **推翻当前的裸 `QGraphicsView` + VTK 渲染窗口方案。** 当前 9 个问题的根因是 2D 框架选错,不是"没嵌 web"。
|
||
3. **改用 `QwtPlot` 作为两类图的坐标系框架;等值线多边形用 VTK 算法(仅算法,不开 VTK 渲染窗口)。**
|
||
4. **验收标准 = 视觉等价 + 交互一致**,不是字节级像素 diff。原生渲染与 web 端 Plotly 在抗锯齿、字体、亚像素上天生有别,任何工具都无法字节级相同;目标是"用户看不出降级感"。
|
||
5. **不要自由发挥架构。** 本文档把分层、类、职责定死,照做;遇到本文档未覆盖的细节,先问,不要自行另起一套。
|
||
|
||
---
|
||
|
||
## 1. 根因诊断(为什么会做崩)
|
||
|
||
当前实现把"图形 + 坐标轴 + 色阶图例"全部糊在一个 `QGraphicsView` 场景里一起做变换,导致系统性错误。裸 `QGraphicsView` 是通用图形框架,**不具备"科学图表"语义**——它不知道什么是固定坐标轴、什么是"仅数据区缩放"、什么是"图例不参与变换"。
|
||
|
||
把用户列的 9 个问题按根因归类:
|
||
|
||
| # | 问题 | 根因类别 | 由谁解决 |
|
||
|---|---|---|---|
|
||
| 1 | 色阶图随大图缩放、与横坐标重叠 | 框架(图例未独立) | QwtPlot 分层 |
|
||
| 3 | 拖动时坐标轴跟着动(应只动数据、轴值跟随) | 框架(无数据区/轴分离) | QwtPlot 内置交互 |
|
||
| 4 | 一拖动画面就花 | 框架(手写变换 + 重绘错误) | QwtPlot 内置交互 |
|
||
| 9 | 图比原版瘦(纵横比错) | 框架(无纵横比控制) | QwtPlot 纵横比锁定 |
|
||
| 2 | 配色与原版不一致(离散色带 vs 连续插值) | 实现细节(色阶映射) | QwtLinearColorMap |
|
||
| 5 | 等值线数字没显示 | 实现细节(缺标注) | 等值线 label |
|
||
| 6 | 原数据/网格数据切换样式被改错 | UI 布局(与图表无关) | 改回页签样式 |
|
||
| 7 | 显示异常/等值线开关跑到面板外 | UI 布局(与图表无关) | 归位工具条 |
|
||
| 8 | 下方缺「描述」页签 | UI 布局(与图表无关) | 补页签 |
|
||
|
||
**关键结论**:9 个问题没有一个需要"嵌 web"才能解决。第 1 类(#1/#3/#4/#9)换对框架即消失;第 2 类(#2/#5)是渲染细节;第 3 类(#6/#7/#8)是纯 UI,跟用什么画图无关。
|
||
|
||
---
|
||
|
||
## 2. 目标架构 — 三层分离
|
||
|
||
整个二维图表 widget 必须做成**三层分离**,这是治本:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────┐
|
||
│ ChartPanel (QWidget) │
|
||
│ ┌───────────────────────────────────────┐ │
|
||
│ │ 工具条 ToolBar(网格/色阶配置/白化/...) │ │ ← 工具条层(属于面板,不属于绘图区)
|
||
│ ├───────────────────────────────────────┤ │
|
||
│ │ QwtPlot(数据区 + 固定坐标轴) │ │ ← 绘图层(QwtPlot 管坐标轴与交互)
|
||
│ │ • QwtPlotItem: 等值线多边形 / 散点 │ │
|
||
│ │ • QwtPlotMagnifier / Panner(交互) │ │
|
||
│ ├───────────────────────────────────────┤ │
|
||
│ │ ColorBarWidget(独立固定) │ │ ← 图例层(独立 widget,永不参与数据区变换)
|
||
│ └───────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────┘
|
||
```
|
||
|
||
**铁律**:
|
||
- **坐标轴**由 QwtPlot 管理,**永远固定**(不随拖动移动其位置),拖动/缩放时只有**数据区内容**平移缩放,**轴上的刻度数值自动跟随重算**。这正是 QwtPlot 的默认行为,也正是原版的行为。
|
||
- **色阶条(ColorBar)是独立 widget**,放在绘图区下方,**绝不放进 QwtPlot 的数据坐标系**,因此永远不会随图缩放、不会和横坐标重叠(直接消灭 #1)。
|
||
- **工具条属于面板**,不属于绘图区(修正 #7)。
|
||
|
||
---
|
||
|
||
## 3. 框架与依赖
|
||
|
||
| 用途 | 选用 | 协议 | 说明 |
|
||
|---|---|---|---|
|
||
| 2D 图表坐标系框架 | **QwtPlot(Qwt 6.2+)** | QWT License(LGPL + 例外,商用免费) | 坐标轴、缩放、平移、刻度跟随、图例、色阶映射全内置 |
|
||
| 等值线多边形算法 | **vtkBandedPolyDataContourFilter** | BSD(商用免费) | 仅当算法用,**不开 VTK 渲染窗口** |
|
||
| 等值线(线)算法 | vtkContourFilter / vtkFlyingEdges2D | BSD | 生成等值线及其标注位置 |
|
||
| 连续色阶映射 | **QwtLinearColorMap** | QWT License | 对齐原版 Plotly 的连续插值色阶 |
|
||
|
||
> 协议全部商用友好,零授权费。QWT 与 VTK 都不触发开源传染。
|
||
|
||
> **VTK 的双重角色**(务必理解,否则又会做重):在 3D 视图 / 地图视图里 VTK 是**渲染器**(开渲染窗口);在本页这种纯二维详情图里,VTK **退化为算法库**(只用它的 filter 算多边形,取出顶点交给 QwtPlot 画)。本页**不允许**出现 QVTKOpenGLWidget / 渲染窗口。
|
||
|
||
---
|
||
|
||
## 4. 逐项解法(对应 9 个问题)
|
||
|
||
### 4.1 色阶条独立固定(治 #1)
|
||
|
||
- 新建 `ColorBarWidget : public QWidget`,自绘一条水平色带 + 刻度数值 + 单位。
|
||
- 它在布局上位于 QwtPlot **下方**,是**兄弟 widget**,不是 QwtPlot 的内部 item。
|
||
- 它的色阶**与绘图区用同一个色阶对象**(见 §6 色阶服务),保证图与图例配色一致。
|
||
- 因为它不在数据坐标系内,**永远不随拖动/缩放移动,不会与横坐标轴重叠**。
|
||
- 刻度数值与原版对齐:用与数据相同的分级断点(原版色阶断点形如 17.105 / 25.937 / ...,等比/对数分布,见 §6)。
|
||
|
||
### 4.2 仅数据区缩放、坐标轴固定、轴值跟随(治 #3)
|
||
|
||
- 用 QwtPlot,坐标轴(`QwtPlot::xBottom` / `yLeft`)由 QwtPlot 框架绘制,位置固定。
|
||
- 缩放/平移用 QwtPlot 内置:
|
||
- `QwtPlotMagnifier`(滚轮缩放数据区)
|
||
- `QwtPlotPanner`(拖动平移数据区)
|
||
- 需要框选缩放可加 `QwtPlotZoomer`
|
||
- 这些组件只改变**坐标轴的数值范围(scaleDiv)**,QwtPlot 自动重算刻度并重绘——**轴不动、轴上的数字跟着变**,与原版完全一致。
|
||
- **原数据 = 四象限坐标系**(数据含正负,x/y 轴交叉于原点区域);**网格数据 = 单象限**(数据全正,原点在左下)。在 QwtPlot 里就是设置不同的轴范围与原点位置,不需要两套框架。
|
||
|
||
### 4.3 拖动不再花屏(治 #4)
|
||
|
||
- 当前"花屏"是手写 `QGraphicsView` 变换 + 重绘时机错误导致。改用 QwtPlot 后,重绘由 QwtPlot 的 replot 机制统一管理,不会出现脏区残留。
|
||
- 大数据量等值线多边形若导致拖动卡顿:对 `QwtPlotItem` 开启 `QwtPlotItem::RenderAntialiased` 视情况关闭、并启用绘制缓存;必要时对多边形做 LOD 简化(与原版"简化容差"滑块一致,见 §7)。
|
||
|
||
### 4.4 纵横比 / 宽度修正(治 #9)
|
||
|
||
- 原版图是"宽扁"的剖面图(x 范围远大于 y)。当前实现"偏瘦"是因为没有锁定数据纵横比、或 widget 尺寸策略错误。
|
||
- 解法:根据数据的 x/y 跨度设定数据区的纵横比;若要求等比例(1 个数据单位 x = 1 个数据单位 y 的像素),自定义布局在 replot 时按数据范围调整 canvas 的宽高比,或固定 y 轴范围、x 轴自适应填充。
|
||
- 先对齐原版的"宽扁"观感即可,不必强制物理等比,除非业务要求等比例尺。
|
||
|
||
### 4.5 连续插值色阶,对齐 Plotly 配色(治 #2)
|
||
|
||
- 原版散点/等值线用的是**连续插值色阶**(Plotly 的 colorscale),当前实现用了**离散色带**,所以配色不同。
|
||
- 用 `QwtLinearColorMap`,设置与原版相同的**控制点颜色 + 位置**,开启连续插值(`QwtLinearColorMap` 默认 `ScaledColors` 连续模式,不要用 `FixedColors`)。
|
||
- 控制点取自原版色阶(紫蓝→蓝→青→绿→黄→橙→红→深红的彩虹序列)。**必须从原版抓取实际色值**(见 §6 第 4 步),不要凭记忆配色。
|
||
- 色阶分布是**对数/等比**的(断点 17.105, 25.937, 34.382... 比值递增),映射时对数据取 log 再线性映射,否则颜色分布会与原版不同。
|
||
|
||
### 4.6 等值线数值标注(治 #5)
|
||
|
||
- 等值线(contour line)由 `vtkContourFilter` 在各分级阈值生成线,取出线的折点。
|
||
- 在 QwtPlot 中用自定义 `QwtPlotItem` 绘制这些线,并在线的合适位置绘制数值标签(label):沿线取中点或曲率较小处,绘制旋转贴合线方向的文本(原版标签是贴着等值线方向旋转的)。
|
||
- 受工具条「显示等值线标注」复选框控制开关。
|
||
- 「显示等值线提示信息」控制 hover 时的 tooltip(鼠标移到等值线上显示数值),与原版一致。
|
||
|
||
### 4.7 页签切换样式归位(治 #6)
|
||
|
||
- 「原数据 / 网格数据」切换**不是两个按钮**,而是与「数据集面板的 数据/文件」「右栏的 异常/对象属性」**同一种下划线页签样式**(之前已专门调过样式,复用那套 QSS/组件,不要重写成 QPushButton)。
|
||
- 直接复用既有的下划线 Tab 组件类,保证全客户端切换样式统一。
|
||
|
||
### 4.8 工具条归位到网格数据面板内(治 #7)
|
||
|
||
- 「显示异常 / 显示等值线标注 / 显示等值线提示信息 / 简化容差滑块 / 网格 / 色阶配置 / 白化 / 滤波处理 / 异常标注 / 自动标注 / 另存为」这些**属于网格数据视图的工具条**,必须在网格数据面板**内部**的工具条上,不能浮到外层。
|
||
- 「原数据」页签的工具条是另一套(网格 / 色阶配置 / 当前图形下拉 / 另存为),各自归属各自页签。
|
||
|
||
### 4.9 下方补「描述」页签(治 #8)
|
||
|
||
- 图下方区域是一个含两个页签的容器:**「异常列表」+「描述」**,当前只做了异常列表表格,缺「描述」。
|
||
- 补上「描述」页签(富文本/纯文本说明区),与原版结构一致。
|
||
|
||
---
|
||
|
||
## 5. 组件清单与职责(定死,不要改结构)
|
||
|
||
| 类 | 基类 | 职责 |
|
||
|---|---|---|
|
||
| `DatasetDetailPanel` | QWidget | 数据集详情页根容器:顶部下划线页签(原数据/网格数据)+ 内容区 + 下方(异常列表/描述)|
|
||
| `RawDataChartView` | QWidget | 「原数据」散点图:工具条 + QwtPlot(四象限)+ ColorBar |
|
||
| `GridDataChartView` | QWidget | 「网格数据」等值线图:工具条 + QwtPlot(单象限)+ ColorBar |
|
||
| `ContourPlotItem` | QwtPlotItem | 自定义:绘制 VTK 算出的分层填充多边形 + 等值线 + 标注 |
|
||
| `ScatterPlotItem` | QwtPlotItem / QwtPlotSpectroCurve | 散点,按值连续着色 |
|
||
| `ColorBarWidget` | QWidget | 独立色阶条(固定,不入数据坐标系)|
|
||
| `ColorMapService` | (单例/服务) | 全局色阶:断点、颜色、命名保存、对数映射;图与图例同源 |
|
||
| `ContourEngine` | (封装 VTK) | 输入网格数据 → 输出分层多边形 + 等值线折点(封装 vtkBandedPolyDataContourFilter / vtkContourFilter)|
|
||
|
||
---
|
||
|
||
## 6. 色阶服务(对齐原版的关键)
|
||
|
||
色阶是"配色一致"的命门,单独抽成服务,**图、图例、3D 视图共用同一份**(呼应视觉设计规范 §8):
|
||
|
||
1. **断点(分级阈值)**:采用原版的对数/等比分布。原版断点示例:`17.105, 25.937, 34.382, 35.972, 42.555, 49.051, 56.312, 68.950, 83.029, 98.454, 110.570, 129.660, 158.270, 204.160, 260.820, 321.710, 1335.500`(不同数据集断点不同,应由数据的 min/max + 分级规则动态生成,规则与原版一致)。
|
||
2. **颜色控制点**:彩虹序列(深蓝 → 蓝 → 亮蓝 → 青 → 浅绿 → 绿 → 黄绿 → 黄 → 土黄 → 橙 → 黄 → 红 → 深红 → 紫红 → 紫 → 深紫黑)。
|
||
3. **映射方式**:连续插值(`QwtLinearColorMap` ScaledColors)+ 对数标度(数据先 log,再线性映射到 [0,1])。
|
||
4. **必须从原版抓真实色值**:打开原版页面 `http://tenant.geomative.cn/#/projectSpace/datasetMange/datasetInfo?id=...&ddCode=dd_inversion_data`,用浏览器开发者工具检查 Plotly 的 `colorscale` 配置,或对色阶条截图取色(每个色块取中心像素 RGB)。**严禁凭记忆配色** —— 这是 #2 反复出错的根源。
|
||
5. **命名保存**:色阶可命名、保存、切换,调整后图与图例实时刷新(业务规约要求)。
|
||
|
||
---
|
||
|
||
## 7. 工具条功能对照(两个页签各一套)
|
||
|
||
**原数据页签工具条**:网格 | 色阶配置 | 当前图形(下拉:散点图/...)| 另存为
|
||
|
||
**网格数据页签工具条**:网格 | 色阶配置 | 白化 | 滤波处理 | ☑显示异常 | ☑显示等值线标注 | ☐显示等值线提示信息 | 简化容差(滑块,0–1)| 异常标注 | 自动标注 | 另存为
|
||
|
||
- 「简化容差」滑块:控制等值线多边形的简化程度(值越大,多边形越简化、越平滑),对应 VTK 侧对多边形做 `vtkDecimatePolylineFilter` 或道格拉斯-普克简化的容差参数。
|
||
- 「白化」「滤波处理」是对网格数据的预处理操作,作用于 ContourEngine 的输入。
|
||
- 「自动标注」弹出对话框(见 §8)。
|
||
|
||
---
|
||
|
||
## 8. 自动标注对话框(复用同一图表组件)
|
||
|
||
- 「自动标注」弹出的对话框里那张图,**必须与主页面网格数据图完全一致**——做法是**复用同一个 `GridDataChartView` / `ContourPlotItem` 类的另一个实例**,传入同一份数据 + 同一个 ColorMapService,**不要另写一套渲染**(否则又会出现两张图不一致)。
|
||
- 对话框结构:左侧「异常判定规则」(阈值模式 数值/百分位、最小点数、异常类型)+ 右侧统计信息(最大/最小/均值/中位数)+ 图(同主图)+ 自动标注结果列表 + 底部(取消/执行自动标注/确认保存)。
|
||
- 因为是普通 QWidget(QwtPlot 而非 VTK 窗口),嵌进对话框无任何 OpenGL 上下文问题,开关对话框不会闪烁/泄漏。
|
||
|
||
---
|
||
|
||
## 9. 实施顺序(建议分步验收,避免再次大返工)
|
||
|
||
> 每一步做完就和原版对照截图验收,通过再进入下一步。不要一次性全做完再看。
|
||
|
||
1. **搭三层骨架**:DatasetDetailPanel + 两个页签(下划线样式)+ 下方(异常列表/描述)。先不画图,验证 UI 结构对齐(治 #6/#7/#8)。
|
||
2. **QwtPlot 接入 + 交互**:空 QwtPlot,配好固定坐标轴 + Magnifier/Panner,验证"拖动只动数据、轴值跟随、不花屏、纵横比对"(治 #3/#4/#9)。
|
||
3. **ColorBar 独立**:加 ColorBarWidget 固定在下方,验证缩放时色阶条不动、不与轴重叠(治 #1)。
|
||
4. **色阶服务 + 抓原版色值**:实现 ColorMapService,从原版抓真实色值与断点,连续对数映射(治 #2 的基础)。
|
||
5. **散点图**:原数据页用 ScatterPlotItem 连续着色,对照原版配色(治 #2)。
|
||
6. **等值线图**:ContourEngine(VTK 算多边形)+ ContourPlotItem 绘制填充 + 等值线 + 标注(治 #2/#5)。
|
||
7. **工具条联动**:显示异常/等值线/提示、简化容差滑块、白化、滤波接通。
|
||
8. **自动标注对话框**:复用图表组件 + 规则面板 + 统计 + 结果。
|
||
9. **整体对照验收**:原数据、网格数据分别与原版并排截图,核对配色/布局/交互/标注/纵横比。
|
||
|
||
---
|
||
|
||
## 10. 验收标准(写清楚,避免预期错位)
|
||
|
||
**通过标准 = 视觉等价 + 交互一致**:
|
||
|
||
- [ ] 配色与原版视觉一致(同一色阶、连续插值、对数分布)
|
||
- [ ] 坐标轴固定,拖动/缩放只动数据区,轴刻度值跟随
|
||
- [ ] 色阶条固定在轴下方,不随图缩放,不与坐标轴重叠
|
||
- [ ] 等值线数值标注显示,贴合线方向
|
||
- [ ] 原数据=四象限、网格数据=单象限,缩放/移动行为分别与原版一致
|
||
- [ ] 纵横比/宽扁观感与原版一致
|
||
- [ ] 拖动流畅、无花屏
|
||
- [ ] 页签切换为下划线样式(与数据/文件、异常/属性一致)
|
||
- [ ] 工具条开关位于各自页签面板内
|
||
- [ ] 下方含「异常列表 + 描述」两个页签
|
||
- [ ] 自动标注对话框内图与主图完全一致
|
||
|
||
**不作为通过标准**(明确排除,避免无意义返工):
|
||
|
||
- ✗ 与 web 端逐像素 diff 相同(抗锯齿/字体/亚像素天生有别,任何原生方案都无法做到,非缺陷)。
|
||
|
||
---
|
||
|
||
## 11. 一句话方向
|
||
|
||
> 不嵌 Chromium。推翻 QGraphicsView,用 **QwtPlot(坐标系/交互/图例)+ VTK 算法(等值线多边形)+ 连续对数色阶(对齐原版)**,三层分离(绘图区 / 固定坐标轴 / 独立色阶条),UI 三处(页签样式/工具条归位/描述页)按原版恢复。验收按"视觉等价 + 交互一致",非字节级。
|
||
|
||
---
|
||
|
||
*本方案针对当前返工,钉死架构与分层,禁止在图表渲染框架上再自由发挥。未覆盖的细节先确认再实现。*
|