SWMMVis  6.0.0-alpha.4
Qt6/C++ GIS-based graphical user interface for the SWMMVis engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
profileplotwidget.h
Go to the documentation of this file.
1
37#ifndef PROFILE_PLOT_WIDGET_H
38#define PROFILE_PLOT_WIDGET_H
39
40#include "plot/profilebuilder.h"
42
43#include <QBrush>
44#include <QColor>
45#include <QDateTime>
46#include <QPen>
47#include <QPointer>
48#include <QSet>
49#include <QString>
50#include <QVector>
51#include <QWidget>
52
53#include <limits>
54#include <memory>
55#include <optional>
56
57class ProfilePlotWidget : public QWidget
58{
59 Q_OBJECT
60
61public:
82 {
83 QString label;
84 QColor color;
86 std::shared_ptr<ProfileBuilder::SourceDerived> derived;
87 QPen pen;
88 QBrush brush;
89 bool visible = true;
90 };
91
106 {
108
109 // Per-output user preferences — read by the dialog, ignored by the
110 // widget. Default matches the historic look: HGL + EGL current,
111 // Max HGL band on, Max EGL off.
112 // Live HGL split into independent line + fill toggles. The
113 // legend still shows a single HGL entry whenever either is on.
114 bool currentHglLine = true;
115 bool currentHglFill = true;
116 bool currentEgl = true;
117 bool maxHglBand = false;
118 bool maxHglLine = false;
119 // EGL has no fill — the only knob is the Max-EGL envelope line.
120 bool maxEglLine = false;
121
122 // Used directly by the widget.
123 bool showNodeLabels = false;
124 bool showLinkLabels = false;
125 bool inlineNodeLabels = false;
127 int labelAngleDeg = 45;
128 bool useTerrainGround = false;
129 };
130
131 explicit ProfilePlotWidget(QWidget *parent = nullptr);
132
137
143 void setSeries(const QVector<SeriesBinding> &series);
144 [[nodiscard]] const QVector<SeriesBinding> &series() const { return m_series; }
145
152 void setCurrentPeriod(int seriesIdx, int period);
153
159 void setCurrentDateTime(const QDateTime &dt);
160
166 void setCurrentPeriod(int period);
167
168 void setLayerToggles(const LayerToggles &toggles);
169 [[nodiscard]] LayerToggles layerToggles() const;
170
181 {
182 double chainage = 0.0;
183 double bed = std::numeric_limits<double>::quiet_NaN();
184 double wse = std::numeric_limits<double>::quiet_NaN();
185 };
186
194 void setSurface2DSamples(const QVector<Surface2DSample> &samples);
195 [[nodiscard]] const QVector<Surface2DSample> &surface2DSamples() const
196 { return m_surface2D; }
197
206 [[nodiscard]] ProfilePlotOptions *options() const { return m_options; }
207
212 void setAxisLabels(const QString &xLabel, const QString &yLabel);
213
220 [[nodiscard]] int nodeIndexAt(const QPoint &widgetPos) const;
221
226 [[nodiscard]] int linkIndexAt(const QPoint &widgetPos) const;
227
228 // ── Zoom / pan controls (driven by the dialog's toolbar) ─────────────
229
232 enum class Mode { Identify = 0, Pan, ZoomIn, ZoomOut };
233
234 enum class AxisEdge {
235 None = 0,
236 XMinimum,
237 XMaximum,
238 YMinimum,
239 YMaximum,
240 };
241 Q_ENUM(AxisEdge)
242
243 void setMode(Mode m);
244 [[nodiscard]] Mode mode() const { return m_mode; }
245
246 [[nodiscard]] AxisEdge axisEdgeAt(const QPoint &widgetPos) const;
247 bool setAxisEdgeValue(AxisEdge edge, double value);
248 [[nodiscard]] QRectF visibleDataRange() const;
249
250 // ── Shared-x API (attribute tracks pane) ────────────────────────────
251 //
252 // The tracks pane below the profile reproduces this widget's horizontal
253 // pixel mapping exactly — same virtual-chainage x quantity, same left/
254 // right gutters — so the two charts stay column-aligned through every
255 // zoom and pan. Everything it needs is exposed here rather than
256 // duplicated: duplicated constants drift.
257
261 void setVisibleXRange(double vxMin, double vxMax);
262
266 [[nodiscard]] const QVector<double> &virtualChainageTable() const
267 { return m_virtualChainage; }
268
272 [[nodiscard]] double virtualToRealChainage(double vx) const;
273
276 [[nodiscard]] static int chartLeftMarginPx();
277 [[nodiscard]] static int chartRightMarginPx();
278
280 void zoomBy(double factor);
281
283 void fitToExtent();
284
291 void setSelectedElementNames(const QStringList &names);
292
293signals:
301 void visibleXRangeChanged(double vxMin, double vxMax);
302
307 void nodeRightClicked(int pathNodeIdx, const QPoint &globalPos);
308
312 void linkRightClicked(int pathLinkIdx, const QPoint &globalPos);
313
320 void backgroundRightClicked(const QPoint &globalPos);
321
326 void nodeClicked(int pathNodeIdx);
327
331 void linkClicked(int pathLinkIdx);
332
337 void nodeDoubleClicked(int pathNodeIdx);
338
342 void linkDoubleClicked(int pathLinkIdx);
343
350
351protected:
352 void paintEvent (QPaintEvent *event) override;
353 void resizeEvent (QResizeEvent *event) override;
354 void mousePressEvent (QMouseEvent *event) override;
355 void mouseMoveEvent (QMouseEvent *event) override;
356 void mouseReleaseEvent(QMouseEvent *event) override;
357 void mouseDoubleClickEvent(QMouseEvent *event) override;
358 void wheelEvent (QWheelEvent *event) override;
359
360private:
361 // Plot-rect (data area, excluding axis label gutters) in widget pixels.
362 [[nodiscard]] QRectF plotRect() const;
363
364 // Dynamic top margin: grows when node / link labels are visible
365 // (rendered above the plot as a secondary axis) so they don't overlap
366 // the bottom axis tick labels.
367 [[nodiscard]] int topMargin() const;
368 [[nodiscard]] int bottomMargin() const;
369
370 // Data-space → pixel-space conversion.
371 [[nodiscard]] QPointF dataToPixel(double chainage, double elev) const;
372
373 // ── Virtual-chainage helpers ────────────────────────────────────────
374 //
375 // Zero-length links (pump/weir/orifice/outlet) would collapse to a single
376 // x-pixel using real chainage, so we render in a "virtual" x where each
377 // such link contributes a small visual gap. The axis ticks still display
378 // the real chainage at every node — adjacent ticks sharing a value make
379 // the gap legible without distorting the distance axis.
380
383 [[nodiscard]] double virtualX(int nodeIdx) const;
384
387 [[nodiscard]] double virtualXAlongLink(int linkIdx, double frac) const;
388
389 // (virtualToRealChainage is declared in the public section — the tracks
390 // pane labels its shared x-axis with it.)
391
392 // ── Virtual-junction helpers ────────────────────────────────────────
393
400 [[nodiscard]] bool isVirtualNode(int nodeIdx) const;
401
407 void hglEdgePixels(int linkIdx, qreal &pxU, qreal &pxD) const;
408
409 // Recomputes data-space bounds (m_dataXMin, etc.) from m_path + m_series.
410 void recomputeBounds();
411 bool editAxisEdge(AxisEdge edge);
412
413 // Emits visibleXRangeChanged if m_dataXMin/Max moved since the last
414 // emission. Called after every x-mutation site so external consumers
415 // (the attribute-tracks pane) see exactly one signal per change.
416 void emitXRangeIfChanged();
417
418 // Per-layer painters (broken out so the paint pipeline reads top-down).
419 void paintBackgroundAndAxes (QPainter &p) const;
420 void paintLabelAxis (QPainter &p) const;
421 void paintSoilFill (QPainter &p) const;
422 void paintConduits (QPainter &p) const;
423 void paintNodes (QPainter &p) const;
427 void paintBranchStubs (QPainter &p) const;
431 void paintNodeRoses (QPainter &p) const;
432 void paintSelectionHighlights(QPainter &p) const;
433 [[nodiscard]] QColor themeNodeFill (ProfileBuilder::NodeKind k) const;
434 [[nodiscard]] QColor themeNodeOutline(ProfileBuilder::NodeKind k) const;
438 [[nodiscard]] QPen themeVirtualJunctionPen() const;
439 [[nodiscard]] QColor themeLinkFill (ProfileBuilder::LinkKind k) const;
440 [[nodiscard]] QColor themeLinkOutline(ProfileBuilder::LinkKind k) const;
441 [[nodiscard]] QColor themeSoilFill () const;
442 [[nodiscard]] QColor themeBeddingFill() const;
443 [[nodiscard]] QPen themeConduitOutlinePen() const;
444 [[nodiscard]] QPen themeLinkOutlinePen(ProfileBuilder::LinkKind k) const;
445 [[nodiscard]] QBrush themeStreetInvertBrush() const;
446
447 // Per-kind dispatch helpers. Each takes a series index into m_series.
448 // The series carries its own resolved pen/brush; the helpers reach into
449 // `series.derived` for the appropriate per-period or envelope array.
450 //
451 // Current-time series (HGL / EGL / WaterSurface) animate with
452 // m_currentPeriod; envelope series (Max/Min HGL / EGL / WaterSurface)
453 // are static.
454 void paintSeriesCurrentLine (QPainter &p, int seriesIdx) const;
455 void paintSeriesEnvelope (QPainter &p, int seriesIdx) const;
456
457 // Envelope-series only — fills the manhole shaft at each node from
458 // the node invert up to the envelope value (capped at rim). Counterpart
459 // to paintSeriesEnvelope's per-link fill so the Max/Min HGL / EGL /
460 // WaterSurface band reads continuously across nodes instead of leaving
461 // empty manhole columns between links.
462 void paintNodeEnvelopeFill (QPainter &p, int seriesIdx) const;
463
464 // HGL-series only — in-pipe and in-manhole water fills. Drawn for
465 // every visible HGL series so multi-model overlay reads correctly
466 // (each model gets its own water tint).
467 void paintHglFill (QPainter &p, int seriesIdx) const;
468 void paintNodeFill (QPainter &p, int seriesIdx) const;
469 // HGL-series only — short horizontal HGL segment at each node,
470 // spanning the manhole tube width. Butts up against the link HGL
471 // line at each pipe edge to form a continuous node+link trace.
472 void paintNodeHglLine (QPainter &p, int seriesIdx) const;
473 // WaterSurface-series only — in-pipe fill from invert to free-surface
474 // depth (caps at rim under pressurization, unlike HGL).
475 void paintWaterSurfaceFill (QPainter &p, int seriesIdx) const;
476 // 2D inundation overlay — translucent band from the mesh bed up to the
477 // 2D water surface plus a WSE line, per contiguous wet run of stations.
478 // Drawn after the soil so it reads as water standing on the ground,
479 // and before the 1D fills/lines so the network's own HGL stays on top.
480 void paintSurface2D (QPainter &p) const;
481 // Elevation of the DRAWN ground line at a real chainage — the sampled
482 // ground (DEM / 2D mesh) or rim-to-rim — so the 2D band fills exactly
483 // to it.
484 [[nodiscard]] double groundElevAtReal(double realX) const;
485 // Real path chainage → virtual x (zero-length links get a visual gap).
486 // Shared by the terrain ground line and the 2D overlay.
487 [[nodiscard]] double realChainageToVirtualX(double realX) const;
488
489 void paintLegend (QPainter &p) const;
490 void paintTimeLabel (QPainter &p) const;
491
492 // Returns true if `kind` is a current-time (animated) series. All
493 // other kinds are envelope series.
494 [[nodiscard]] static bool isCurrentTimeKind(ProfileBuilder::OutputKind k);
495 // Returns the appropriate per-period array on `derived` for the kind.
496 // Returns an empty vector for envelope kinds.
497 [[nodiscard]] static const QVector<QVector<double>> &
498 currentTimeArray(const ProfileBuilder::SourceDerived &derived,
500 // Returns the envelope array on `derived` for the kind. Returns an
501 // empty vector for current-time kinds.
502 [[nodiscard]] static const QVector<double> &
503 envelopeArray(const ProfileBuilder::SourceDerived &derived,
505
507 QVector<SeriesBinding> m_series;
508 QVector<Surface2DSample> m_surface2D;
509 double m_surface2DMaxWse = std::numeric_limits<double>::quiet_NaN();
510 LayerToggles m_toggles;
511 QPointer<ProfilePlotOptions> m_options;
512 QSet<QString> m_selectedNames;
513 int m_currentSrc = -1;
514 int m_currentPeriod = -1;
515 QDateTime m_currentDateTime;
516 QString m_xLabel = QStringLiteral("Distance");
517 QString m_yLabel = QStringLiteral("Elevation");
518
519 // Last-painted bounding rects of the legend and timestamp chips. Used
520 // for hit-testing in mousePressEvent so the user can grab and drag
521 // either overlay. Updated inside paintLegend / paintTimeLabel.
522 mutable QRectF m_legendRect;
523 mutable QRectF m_timeLabelRect;
524 enum class OverlayDrag { None = 0, Legend, TimeLabel };
525 OverlayDrag m_overlayDrag = OverlayDrag::None;
526 QPoint m_overlayDragLastPos;
527
528 // Per-node virtual x (matches m_path.chainage size). Rebuilt in
529 // recomputeBounds(). Zero-length links contribute m_virtualGap; all
530 // others contribute their real length.
531 QVector<double> m_virtualChainage;
532 double m_virtualGap = 0.0;
533 double m_beddingFloorElev = 0.0;
534 bool m_haveBeddingFloor = false;
535
536 // Auto-computed data extent (re-derived on setPath / setSeries).
537 double m_autoXMin = 0.0;
538 double m_autoXMax = 1.0;
539 double m_autoYMin = 0.0;
540 double m_autoYMax = 1.0;
541 // Currently-visible view bounds. Equal to the auto extent when the
542 // user hasn't zoomed/panned (m_fitMode == true).
543 double m_dataXMin = 0.0;
544 double m_dataXMax = 1.0;
545 double m_dataYMin = 0.0;
546 double m_dataYMax = 1.0;
547 bool m_fitMode = true;
548 // Last-emitted visibleXRangeChanged values (see emitXRangeIfChanged).
549 double m_lastEmittedXMin = std::numeric_limits<double>::quiet_NaN();
550 double m_lastEmittedXMax = std::numeric_limits<double>::quiet_NaN();
551 Mode m_mode = Mode::Identify;
552 bool m_panActive = false; // pan-drag in progress
553 bool m_zoomActive = false; // zoom-rubberband in progress
554 AxisEdge m_pressedAxisEdge = AxisEdge::None;
555 QPoint m_lastMousePos;
556 QPoint m_zoomAnchor; // start of rubberband
557 class QRubberBand *m_rubberBand = nullptr;
558};
559
560#endif // PROFILE_PLOT_WIDGET_H
Definition profileplotoptions.h:28
Definition profileplotwidget.h:58
void linkDoubleClicked(int pathLinkIdx)
Emitted on left double-click over a link glyph.
void wheelEvent(QWheelEvent *event) override
Definition profileplotwidget.cpp:1513
void setVisibleXRange(double vxMin, double vxMax)
Definition profileplotwidget.cpp:828
LayerToggles layerToggles() const
Definition profileplotwidget.cpp:580
static int chartRightMarginPx()
Definition profileplotwidget.cpp:852
void mouseDoubleClickEvent(QMouseEvent *event) override
Definition profileplotwidget.cpp:1502
void fitToExtent()
Definition profileplotwidget.cpp:805
void setSeries(const QVector< SeriesBinding > &series)
Replaces all rendered series at once. Order matters only for z-order between equal-kind series (e....
Definition profileplotwidget.cpp:383
void nodeRightClicked(int pathNodeIdx, const QPoint &globalPos)
Emitted on right-click over a node glyph; consumed by Stage 6 to pop an edit-in-place context menu.
void linkRightClicked(int pathLinkIdx, const QPoint &globalPos)
Emitted on right-click over a link glyph.
void mouseReleaseEvent(QMouseEvent *event) override
Definition profileplotwidget.cpp:1413
void backgroundClicked()
Emitted on left-click in Identify mode when the click misses every node and link glyph....
const QVector< SeriesBinding > & series() const
Definition profileplotwidget.h:144
int linkIndexAt(const QPoint &widgetPos) const
Hit-test: returns the path-link index whose conduit / glyph passes through widgetPos within a reasona...
Definition profileplotwidget.cpp:1088
void setCurrentDateTime(const QDateTime &dt)
Sets the wall-clock datetime corresponding to the current period. Used by the on-plot timestamp overl...
Definition profileplotwidget.cpp:442
void resizeEvent(QResizeEvent *event) override
Definition profileplotwidget.cpp:1279
static int chartLeftMarginPx()
Definition profileplotwidget.cpp:851
void backgroundRightClicked(const QPoint &globalPos)
Emitted on right-click that hits neither a node nor a link. Hosts use this to show a context menu who...
void setSurface2DSamples(const QVector< Surface2DSample > &samples)
Replaces the 2D inundation overlay stations (static geometry + the current frame's WSE)....
Definition profileplotwidget.cpp:455
ProfilePlotOptions * options() const
Definition profileplotwidget.h:206
AxisEdge axisEdgeAt(const QPoint &widgetPos) const
Definition profileplotwidget.cpp:872
void visibleXRangeChanged(double vxMin, double vxMax)
Emitted whenever the visible x-range (virtual chainage) changes — fit, zoom, pan, wheel,...
void setCurrentPeriod(int seriesIdx, int period)
Selects the time-step shown by current-time series (HGL / EGL / WaterSurface). seriesIdx is the sourc...
Definition profileplotwidget.cpp:390
void setAxisLabels(const QString &xLabel, const QString &yLabel)
Sets axis labels for the plot frame. Defaults to "Chainage" / "Elevation" if not set.
Definition profileplotwidget.cpp:585
int nodeIndexAt(const QPoint &widgetPos) const
Hit-test: returns the path-node index whose manhole glyph is nearest widgetPos within a reasonable to...
Definition profileplotwidget.cpp:1050
void nodeDoubleClicked(int pathNodeIdx)
Emitted on left double-click over a node glyph. Hosts use this to zoom the main map to the element.
Mode mode() const
Definition profileplotwidget.h:244
void setOptions(ProfilePlotOptions *options)
Binds a ProfilePlotOptions object — the widget's theming (per-type fills/outlines,...
Definition profileplotwidget.cpp:471
void linkClicked(int pathLinkIdx)
Emitted on left-click over a conduit / non-conduit glyph.
void setPath(const ProfileBuilder::PathStatic &path)
Sets the static path + chainage to render. Triggers a repaint.
Definition profileplotwidget.cpp:375
double virtualToRealChainage(double vx) const
Definition profileplotwidget.cpp:777
void setLayerToggles(const LayerToggles &toggles)
Definition profileplotwidget.cpp:449
void nodeClicked(int pathNodeIdx)
Emitted on left-click over a node glyph. Consumed by ProfilePlotDialog to drive the main-map selectio...
void mousePressEvent(QMouseEvent *event) override
Definition profileplotwidget.cpp:1284
Mode
Definition profileplotwidget.h:232
void setMode(Mode m)
Definition profileplotwidget.cpp:854
void paintEvent(QPaintEvent *event) override
Definition profileplotwidget.cpp:1182
const QVector< Surface2DSample > & surface2DSamples() const
Definition profileplotwidget.h:195
const QVector< double > & virtualChainageTable() const
Definition profileplotwidget.h:266
QRectF visibleDataRange() const
Definition profileplotwidget.cpp:930
void mouseMoveEvent(QMouseEvent *event) override
Definition profileplotwidget.cpp:1363
AxisEdge
Definition profileplotwidget.h:234
void setSelectedElementNames(const QStringList &names)
Marks a set of element names (nodes or links) as selected so the matching glyphs render with a highli...
Definition profileplotwidget.cpp:1079
bool setAxisEdgeValue(AxisEdge edge, double value)
Definition profileplotwidget.cpp:899
void zoomBy(double factor)
Definition profileplotwidget.cpp:814
ArrowPlacement p
Definition linesymbollayer.cpp:27
MaskMode m
Definition maskspec.cpp:18
int k
Definition mesh2dresultsexport.cpp:549
NodeKind
Topology category for the manhole-glyph picker in ProfilePlotWidget. Maps 1:1 to the relevant SWMMMod...
Definition profilebuilder.h:52
LinkKind
Connector category for the conduit-vs-glyph render branch in ProfilePlotWidget.
Definition profilebuilder.h:70
OutputKind
Which elevation-axis quantity a profile series represents. Every option resolves to an elevation (ft ...
Definition profilebuilder.h:96
Pure-logic data assembly for Slice BC's longitudinal profile plot: chainage, HGL/EGL time-series,...
Q_OBJECT facade for every user-configurable display option on the profile plot. Both the per-dialog L...
int path
Definition pslgminsize.cpp:234
QVector< int > parent
union-find
Definition pslgminsize.cpp:176
TokenKind kind
Definition queryparser.cpp:34
A fully-resolved profile path: ordered nodes, joining links, and cumulative chainage from nodes[0] to...
Definition profilebuilder.h:242
Output of compute(): per-source HGL/EGL series and per-node envelope min/max. Sized to match the inpu...
Definition profilebuilder.h:292
Label / terrain / orientation switches and per-output user preferences (currentHglLine,...
Definition profileplotwidget.h:106
bool showLinkLabels
Definition profileplotwidget.h:124
bool maxEglLine
Definition profileplotwidget.h:120
LabelOrientation
Definition profileplotwidget.h:107
@ Diagonal
Definition profileplotwidget.h:107
@ Vertical
Definition profileplotwidget.h:107
@ Horizontal
Definition profileplotwidget.h:107
bool showNodeLabels
Definition profileplotwidget.h:123
bool currentHglFill
Definition profileplotwidget.h:115
int labelAngleDeg
Definition profileplotwidget.h:127
bool maxHglBand
Definition profileplotwidget.h:117
bool useTerrainGround
Definition profileplotwidget.h:128
LabelOrientation labelOrientation
Definition profileplotwidget.h:126
bool inlineNodeLabels
Definition profileplotwidget.h:125
bool currentHglLine
Definition profileplotwidget.h:114
bool currentEgl
Definition profileplotwidget.h:116
bool maxHglLine
Definition profileplotwidget.h:118
One elevation-axis curve to render: a (results-layer × output kind) pair with resolved style and pre-...
Definition profileplotwidget.h:82
QPen pen
Definition profileplotwidget.h:87
QColor color
Definition profileplotwidget.h:84
std::shared_ptr< ProfileBuilder::SourceDerived > derived
Definition profileplotwidget.h:86
QString label
Definition profileplotwidget.h:83
bool visible
Definition profileplotwidget.h:89
ProfileBuilder::OutputKind kind
Definition profileplotwidget.h:85
QBrush brush
Definition profileplotwidget.h:88
One station of the 2D inundation overlay: the active 2D results layer's water surface sampled where t...
Definition profileplotwidget.h:181
double chainage
Definition profileplotwidget.h:182
double bed
Definition profileplotwidget.h:183
double wse
Definition profileplotwidget.h:184