geopro/docs/Geopro3.0_二维图表返工技术方案.md

18 KiB
Raw Permalink Blame History

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 图表坐标系框架 QwtPlotQwt 6.2+ QWT LicenseLGPL + 例外,商用免费) 坐标轴、缩放、平移、刻度跟随、图例、色阶映射全内置
等值线多边形算法 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
  • 这些组件只改变坐标轴的数值范围scaleDivQwtPlot 自动重算刻度并重绘——轴不动、轴上的数字跟着变,与原版完全一致。
  • 原数据 = 四象限坐标系数据含正负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 linevtkContourFilter 在各分级阈值生成线,取出线的折点。
  • 在 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. 工具条功能对照(两个页签各一套)

原数据页签工具条:网格 | 色阶配置 | 当前图形(下拉:散点图/...| 另存为

网格数据页签工具条:网格 | 色阶配置 | 白化 | 滤波处理 | ☑显示异常 | ☑显示等值线标注 | ☐显示等值线提示信息 | 简化容差滑块01| 异常标注 | 自动标注 | 另存为

  • 「简化容差」滑块:控制等值线多边形的简化程度(值越大,多边形越简化、越平滑),对应 VTK 侧对多边形做 vtkDecimatePolylineFilter 或道格拉斯-普克简化的容差参数。
  • 「白化」「滤波处理」是对网格数据的预处理操作,作用于 ContourEngine 的输入。
  • 「自动标注」弹出对话框(见 §8

8. 自动标注对话框(复用同一图表组件)

  • 「自动标注」弹出的对话框里那张图,必须与主页面网格数据图完全一致——做法是复用同一个 GridDataChartView / ContourPlotItem 类的另一个实例,传入同一份数据 + 同一个 ColorMapService不要另写一套渲染(否则又会出现两张图不一致)。
  • 对话框结构:左侧「异常判定规则」(阈值模式 数值/百分位、最小点数、异常类型)+ 右侧统计信息(最大/最小/均值/中位数)+ 图(同主图)+ 自动标注结果列表 + 底部(取消/执行自动标注/确认保存)。
  • 因为是普通 QWidgetQwtPlot 而非 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. 等值线图ContourEngineVTK 算多边形)+ ContourPlotItem 绘制填充 + 等值线 + 标注(治 #2/#5
  7. 工具条联动:显示异常/等值线/提示、简化容差滑块、白化、滤波接通。
  8. 自动标注对话框:复用图表组件 + 规则面板 + 统计 + 结果。
  9. 整体对照验收:原数据、网格数据分别与原版并排截图,核对配色/布局/交互/标注/纵横比。

10. 验收标准(写清楚,避免预期错位)

通过标准 = 视觉等价 + 交互一致

  • 配色与原版视觉一致(同一色阶、连续插值、对数分布)
  • 坐标轴固定,拖动/缩放只动数据区,轴刻度值跟随
  • 色阶条固定在轴下方,不随图缩放,不与坐标轴重叠
  • 等值线数值标注显示,贴合线方向
  • 原数据=四象限、网格数据=单象限,缩放/移动行为分别与原版一致
  • 纵横比/宽扁观感与原版一致
  • 拖动流畅、无花屏
  • 页签切换为下划线样式(与数据/文件、异常/属性一致)
  • 工具条开关位于各自页签面板内
  • 下方含「异常列表 + 描述」两个页签
  • 自动标注对话框内图与主图完全一致

不作为通过标准(明确排除,避免无意义返工):

  • ✗ 与 web 端逐像素 diff 相同(抗锯齿/字体/亚像素天生有别,任何原生方案都无法做到,非缺陷)。

11. 一句话方向

不嵌 Chromium。推翻 QGraphicsViewQwtPlot坐标系/交互/图例)+ VTK 算法(等值线多边形)+ 连续对数色阶(对齐原版),三层分离(绘图区 / 固定坐标轴 / 独立色阶条UI 三处(页签样式/工具条归位/描述页)按原版恢复。验收按"视觉等价 + 交互一致",非字节级。


本方案针对当前返工,钉死架构与分层,禁止在图表渲染框架上再自由发挥。未覆盖的细节先确认再实现。