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
gisrasterlayer.h
Go to the documentation of this file.
1
10#ifndef GISRASTERLAYER_H
11#define GISRASTERLAYER_H
12
14#include "render/colorramp.h"
16
17#include <QHash>
18#include <QImage>
19#include <QFutureWatcher>
20#include <QList>
21#include <QMutex>
22#include <QPair>
23#include <QSet>
24#include <QString>
25#include <QThreadPool>
26#include <QVector>
27
28#include <atomic>
29#include <memory>
30#include <vector>
31
32// Forward-declare GDAL types
33class GDALDataset;
34class OGRCoordinateTransformation;
35class OGRSpatialReference;
38class RasterTileItem;
39
41
42// RasterColorRamp is defined in render/colorramp.h (relocated 2026-05-24,
43// Slice BB-α). Kept at global scope for back-compat with existing callers
44// + Q_DECLARE_METATYPE.
45
61{
62 Q_OBJECT
63 Q_PROPERTY(QString filePath READ filePath NOTIFY filePathChanged)
64 Q_PROPERTY(int bandCount READ bandCount CONSTANT)
65 Q_PROPERTY(int renderBand READ renderBand WRITE setRenderBand NOTIFY renderBandChanged)
66 Q_PROPERTY(double noDataValue READ noDataValue NOTIFY noDataValueChanged)
67
68 // Slice U-7 — Q_CLASSINFO groups for the unified LayerStyleDialog.
69 Q_CLASSINFO("group:filePath", "Source")
70 Q_CLASSINFO("group:bandCount", "Source")
71 Q_CLASSINFO("group:renderBand", "Display")
72 Q_CLASSINFO("group:noDataValue", "Display")
73
74public:
75
76 explicit GISRasterLayer(const QString &filePath,
77 OpenSWMMVisWorkspace *parent = nullptr);
78
79 ~GISRasterLayer() override;
80
89 void openAsync(const QString &filePath);
90
91 // ----- Dataset info ---------------------------------------------------
92
93 [[nodiscard]] QString filePath() const;
94 [[nodiscard]] int bandCount() const;
95 [[nodiscard]] int renderBand() const;
96 [[nodiscard]] double noDataValue() const;
97
105 [[nodiscard]] QString detectVerticalUnit() const;
106
113 void setRenderBand(int band);
114
118 [[nodiscard]] bool hasNoDataValue() const;
119
120 // ----- Self-description for the Layer Properties dialog ----------------
121 [[nodiscard]] QString sourceDescription() const override;
122 [[nodiscard]] QVector<QPair<QString, QString>> extendedMetadata() const override;
123
124 // ----- Per-band data access (symbology inputs) -------------------------
125 // All read the primary dataset under m_datasetMutex and cache per band
126 // (caches drop on close / re-open). Pure: no signals are emitted.
127
132 [[nodiscard]] QPair<double, double> bandRange(int band) const;
133
140 [[nodiscard]] QVector<double> sampleValues(int band, int maxSamples = 200000) const;
141
147 [[nodiscard]] QList<int> uniqueValues(int band, int cap = 256) const;
148
150 [[nodiscard]] bool hasColorTable() const;
151
158 [[nodiscard]] QList<OpenSWMM::Render::PalettedRasterRenderer::Class> colorTableClasses() const;
159
162 [[nodiscard]] bool isByteRaster() const;
163
166 [[nodiscard]] std::vector<std::unique_ptr<openswmmvis::ui::ILayerStyleSubject>>
167 styleSubjects() override;
168
169 // ----- Raster renderer (Slice BI Phase 8.13.6.7; P5/R-1 full switch) ---
170 // The raster renderer is the §J.2 seam every raster paint path goes
171 // through: warpToCanvas() colourises single-band tiles via
172 // m_rasterRenderer->colorForValue(), and a MultiBandColorRenderer
173 // switches the warp onto the RGB composite path. The open-time default
174 // is chosen from the dataset (colour table ⇒ Paletted; ≥3 Byte bands ⇒
175 // MultiBandColor; otherwise Graduated continuous grayscale over the band
176 // statistics). Persisted / restored by StyleFileIO + ProjectSerializer.
177
184 [[nodiscard]] OpenSWMM::Render::IRasterRenderer *rasterRenderer() const;
185
193 void setRasterRenderer(std::unique_ptr<OpenSWMM::Render::IRasterRenderer> r);
194
202
203 // ----- Pixel query ----------------------------------------------------
204
213 [[nodiscard]] double valueAt(double mapX, double mapY,
214 const SpatialReferenceSystem *canvasSRS,
215 int band = 1,
216 bool *ok = nullptr) const;
217
218 // ----- OpenSWMMVisLayer interface -----------------------------------------
219
220 [[nodiscard]] bool isRasterLayer() const override { return true; }
221
225 void setIsBasemap(bool on);
226 [[nodiscard]] bool isBasemapLayer() const override { return m_isBasemap; }
227
228 void fetchCache(const MapExtent &extent,
229 const QSize &viewportSize,
230 const SpatialReferenceSystem *srs) override;
231
232 void render(QPainter *painter,
233 const MapExtent &extent,
234 const QSize &imageSize,
235 const SpatialReferenceSystem *srs) override;
236
237 void populateScene(QGraphicsScene *scene,
238 const MapExtent &canvasExtent,
239 const SpatialReferenceSystem *canvasSRS) override;
240
241 void depopulateScene(QGraphicsScene *scene) override;
242
243 void refreshScene(QGraphicsScene *scene,
244 const MapExtent &canvasExtent,
245 const SpatialReferenceSystem *canvasSRS) override;
246
247 void onCanvasCRSChanged(const SpatialReferenceSystem *newCanvasSRS) override;
248
249 // ----- VS.6 — hillshade relief overlay (single-band DTM rasters) ---------
250 // When enabled, warpToCanvas() composites a hillshade lighting factor over
251 // the colour-ramped pixels using the warped elevation grid. Parameters
252 // mirror HillshadeSymbolLayerSpec; stored as primitives to keep this
253 // header free of the render/ spec include.
254 [[nodiscard]] bool hillshadeEnabled() const { return m_hillshadeEnabled; }
255 void setHillshadeEnabled(bool on);
256 [[nodiscard]] double hillshadeAzimuthDeg() const { return m_hillshadeAzimuthDeg; }
257 [[nodiscard]] double hillshadeAltitudeDeg() const { return m_hillshadeAltitudeDeg; }
258 [[nodiscard]] double hillshadeZFactor() const { return m_hillshadeZFactor; }
259 [[nodiscard]] double hillshadeStrength() const { return m_hillshadeStrength; }
260 void setHillshadeParams(double azimuthDeg, double altitudeDeg,
261 double zFactor, double strength);
262
263signals:
264 void filePathChanged(const QString &path);
265 void renderBandChanged(int band);
266 void noDataValueChanged(double value);
270
272 void openFinished(bool ok);
273
276 void overviewBuildStarted(const QString &name);
280
281private:
282 // Worker-thread payload for openAsync(): the GDAL open + metadata read +
283 // band-stats scan produce this POD (no QObject state), which
284 // applyOpenResult() then folds into the layer on the GUI thread. Defined
285 // in the .cpp.
286 struct OpenResult;
287 [[nodiscard]] static OpenResult doOpenWork(const QString &filePath);
288 void applyOpenResult(const OpenResult &r);
289
290 void openDataset(const QString &filePath);
291 void closeDataset();
292 void invalidateCache();
293
294 // ── Overview (pyramid) preprocessing — Phase 1 ────────────────────────
298 void maybeBuildOverviews();
303 void buildOverviewsAsync();
304
305 // Snapshot of everything a worker-thread tile warp reads, taken on the
306 // GUI thread at launch so the worker never dereferences live layer state
307 // (the renderer is a clone() — zero locking in the colourise loop).
308 struct WarpParams
309 {
310 int renderBand = 1;
311 // RGB composite path (MultiBandColorRenderer): `bands` lists the
312 // source bands feeding R, G, B (and optionally A); otherwise it holds
313 // the single render band and the colourise loop runs.
314 bool rgb = false;
315 QVector<int> bands;
316 bool hasNoData = false;
317 double noDataValue = 0.0;
318 bool hillshadeEnabled = false;
319 double hillshadeAzimuthDeg = 315.0;
320 double hillshadeAltitudeDeg = 45.0;
321 double hillshadeZFactor = 1.0;
322 double hillshadeStrength = 0.5;
323 std::shared_ptr<const OpenSWMM::Render::IRasterRenderer> renderer;
324 };
325 [[nodiscard]] WarpParams snapshotWarpParams() const;
326
333 [[nodiscard]] QImage warpToCanvas(GDALDataset *src,
334 const MapExtent &canvasExtent,
335 const SpatialReferenceSystem *canvasSRS,
336 int pixelWidth,
337 int pixelHeight,
338 const WarpParams &params) const;
339
349 [[nodiscard]] GDALDataset *buildWindowedSource(
350 GDALDataset *src,
351 const MapExtent &canvasExtent, const SpatialReferenceSystem *canvasSRS,
352 int pixelWidth, int pixelHeight, int outBands, bool isRGB,
353 const WarpParams &params) const;
354
355 // ── Phase 3 — fixed-grid tile pyramid (off-thread, cached) ───────────
356 // Tiles are 256² images in CANVAS CRS on a fixed power-of-2 grid, so a pan
357 // reuses the tiles it already has and only the new edge tiles are produced.
358 // Each tile is one warpToCanvas() of its canvas-CRS extent (overview-aware),
359 // produced on up to N concurrent workers (one pooled GDAL handle each),
360 // cached in the TilePyramidLayer base, and composited by render() with a
361 // coarser-tile fallback for tiles still in production.
362 static constexpr int kTilePx = 256;
363 // Fallback stand-ins may be at most 2^6 = 64× coarser than the view level
364 // (a 4-px sub-rect of a 256² ancestor).
365 static constexpr double kMaxAncestorSpanRatio = 64.0;
366 struct TileReq { int level; int col; int row; };
367 // Worker → GUI payload for one produced tile. `gen` is the cache
368 // generation captured at launch; results from before an invalidateCache()
369 // are dropped instead of polluting the fresh cache.
370 struct TileResult
371 {
372 QString key;
373 QImage img;
374 MapExtent extent;
375 int level = 0;
376 quint64 gen = 0;
377 };
380 [[nodiscard]] static int levelForResolution(double mapUnitsPerPixel);
382 [[nodiscard]] static MapExtent tileExtent(int level, int col, int row);
383 [[nodiscard]] static QString tileKey(int level, int col, int row);
386 void requestTiles(const MapExtent &extent, const QSize &viewportSize,
387 const SpatialReferenceSystem *canvasSRS);
391 void enqueueSeedTiles();
394 void startNextTiles();
397 void onSlotFinished(int slotIndex);
398
399 // ── Parallel tile production — N-slot GDAL handle pool ───────────────
400 // N read-only handles to the same file; a slot borrows one for the warp
401 // duration. Acquire (startNextTiles) and release (onSlotFinished) both run
402 // on the GUI thread, so the free-list needs no mutex; workers only *use*
403 // their borrowed handle.
404 struct TileSlot
405 {
406 std::unique_ptr<QFutureWatcher<TileResult>> watcher;
407 bool busy = false;
408 GDALDataset *handle = nullptr; // borrowed from m_freeHandles
409 SpatialReferenceSystem *srs = nullptr; // per-tile clone; freed on finish
410 };
413 void openHandlePool();
416 void closeHandlePool();
421 void drainTileSlots();
424 void finishSlot(TileSlot &slot);
425
428 [[nodiscard]] QList<OpenSWMM::Render::PalettedRasterRenderer::Class>
429 readColorTableLocked() const;
430
431 QString m_filePath;
432 int m_renderBand = 1;
433 bool m_isBasemap = false;
434 double m_noDataValue = std::numeric_limits<double>::quiet_NaN();
435 bool m_hasNoData = false;
436
437 // Per-band symbology inputs (see bandRange / sampleValues). Guarded by
438 // m_datasetMutex; cleared whenever the dataset closes or re-opens.
439 mutable QHash<int, QPair<double, double>> m_bandRangeCache;
440 mutable QHash<int, QVector<double>> m_sampleCache;
441
442 // VS.6 — hillshade relief overlay parameters (see public accessors).
443 bool m_hillshadeEnabled = false;
444 double m_hillshadeAzimuthDeg = 315.0;
445 double m_hillshadeAltitudeDeg = 45.0;
446 double m_hillshadeZFactor = 1.0;
447 double m_hillshadeStrength = 0.5;
448
449 GDALDataset *m_dataset = nullptr;
451 // Guards m_dataset for the paths that still share the primary handle:
452 // valueAt() (single-pixel probe), closeDataset(), and the post-.ovr-build
453 // reload/reopen. Tile warps no longer take it — each worker reads through
454 // its own pooled handle with a WarpParams snapshot.
455 mutable QMutex m_datasetMutex;
456
457 // valueAt()'s cached canvas→raster probe transform (guarded by
458 // m_datasetMutex). Keyed on the two OGRSpatialReference identities;
459 // nullptr CT with matching keys means the CRSs are the same.
460 mutable OGRCoordinateTransformation *m_probeCT = nullptr;
461 mutable const OGRSpatialReference *m_probeCTCanvasKey = nullptr;
462 mutable const OGRSpatialReference *m_probeCTRasterKey = nullptr;
463 // Set after a background .ovr build; consumed by the next fetchCache()
464 // (GUI thread), which reopens m_dataset so the new overviews become visible.
465 std::atomic<bool> m_datasetReloadPending{false};
466 bool m_overviewBuildInFlight = false;
467
468 // Tile pyramid state. The tile cache itself lives in the TilePyramidLayer
469 // base (mutex-guarded: render() reads from the MapRenderJob worker while
470 // the GUI thread inserts). Everything below is GUI-thread only.
471 QList<TileReq> m_tileQueue; // FIFO of tiles still to produce
472 QSet<QString> m_queuedKeys; // de-dup: queued ∪ in-flight
473 quint64 m_cacheGeneration = 0; // bumped by invalidateCache()
474 quint64 m_seedGeneration = ~0ULL; // last gen seeds were queued
475 SpatialReferenceSystem *m_currentSRS = nullptr; // latest canvas-CRS snapshot (template)
476
477 // Parallel production (see TileSlot above). m_tilePool bounds tile warps
478 // to N threads and keeps them off the global pool used by openAsync /
479 // overview builds.
480 int m_maxConcurrentTiles = 1;
481 QThreadPool m_tilePool;
482 std::vector<GDALDataset *> m_poolHandles; // owns the N pooled handles
483 QList<GDALDataset *> m_freeHandles; // GUI-thread free-list
484 std::vector<TileSlot> m_slots;
485
486 // Persistent scene item (kept visible as placeholder until new warp completes)
487 RasterTileItem *m_sceneItem = nullptr;
488
489 // Single source of truth for raster colouring (P5/R-1). Initialised
490 // eagerly in the ctor (a grayscale GraduatedRasterRenderer) so
491 // rasterRenderer() never returns null; applyOpenResult() replaces it with
492 // the dataset-appropriate default, and warpToCanvas() colourises through
493 // a clone of it.
494 std::unique_ptr<OpenSWMM::Render::IRasterRenderer> m_rasterRenderer;
495};
496
497Q_DECLARE_METATYPE(GISRasterLayer *)
498// Q_DECLARE_METATYPE(RasterColorRamp) is in render/colorramp.h (Slice BB-α).
499
500#endif // GISRASTERLAYER_H
A map layer backed by a GDAL raster dataset.
Definition gisrasterlayer.h:61
int renderBand
Definition gisrasterlayer.h:65
void setRenderBand(int band)
Sets the 1-based band index to render on the single-band colour path. Re-reads that band's NoData val...
Definition gisrasterlayer.cpp:313
void setHillshadeEnabled(bool on)
Definition gisrasterlayer.cpp:1285
void openAsync(const QString &filePath)
Asynchronously open filePath. GDALOpenEx + metadata read + ComputeStatistics run on a worker thread; ...
Definition gisrasterlayer.cpp:1234
QList< OpenSWMM::Render::PalettedRasterRenderer::Class > colorTableClasses() const
Band 1's GDAL colour table as paletted classes (value, label = value, RGBA). Tables larger than 32 en...
Definition gisrasterlayer.cpp:458
void fetchCache(const MapExtent &extent, const QSize &viewportSize, const SpatialReferenceSystem *srs) override
Triggers any asynchronous (or synchronous) tile/data fetch needed to populate this layer's internal c...
Definition gisrasterlayer.cpp:597
void populateScene(QGraphicsScene *scene, const MapExtent &canvasExtent, const SpatialReferenceSystem *canvasSRS) override
Creates / updates QGraphicsItems representing this layer in the scene.
Definition gisrasterlayer.cpp:882
void setRasterRenderer(std::unique_ptr< OpenSWMM::Render::IRasterRenderer > r)
Replaces the current raster renderer.
Definition gisrasterlayer.cpp:196
QPair< double, double > bandRange(int band) const
Approximate (min, max) of band from GDAL statistics (PAM-cached when available). (NaN,...
Definition gisrasterlayer.cpp:358
QVector< QPair< QString, QString > > extendedMetadata() const override
Ordered key→value pairs of TYPE-SPECIFIC metadata, rendered by the Metadata tab below the common bloc...
Definition gisrasterlayer.cpp:249
std::vector< std::unique_ptr< openswmmvis::ui::ILayerStyleSubject > > styleSubjects() override
Definition gisrasterlayer.cpp:490
bool isBasemapLayer() const override
Returns true for world-spanning basemap layers (XYZ tile providers, WMS/WMTS services covering the wh...
Definition gisrasterlayer.h:226
double hillshadeStrength() const
Definition gisrasterlayer.h:259
void depopulateScene(QGraphicsScene *scene) override
Removes all QGraphicsItems belonging to this layer from the scene.
Definition gisrasterlayer.cpp:955
int bandCount
Definition gisrasterlayer.h:64
double valueAt(double mapX, double mapY, const SpatialReferenceSystem *canvasSRS, int band=1, bool *ok=nullptr) const
Returns the pixel value at the given geographic coordinate.
Definition gisrasterlayer.cpp:504
double hillshadeZFactor() const
Definition gisrasterlayer.h:258
double hillshadeAltitudeDeg() const
Definition gisrasterlayer.h:257
void setIsBasemap(bool on)
Marks this raster as a basemap (added via Add Basemap ▸ Local File). Also retags the layer type to SW...
Definition gisrasterlayer.cpp:305
void renderBandChanged(int band)
void notifyRasterRendererEdited()
Call after mutating the live renderer in place (through its own setters) so cached tiles are dropped,...
Definition gisrasterlayer.cpp:217
void setHillshadeParams(double azimuthDeg, double altitudeDeg, double zFactor, double strength)
Definition gisrasterlayer.cpp:1294
void noDataValueChanged(double value)
void overviewBuildFinished(bool ok)
The background overview build finished (ok ⇒ pyramids ready and the render will pick them up on the n...
bool isRasterLayer() const override
Returns true for layers that render into a raster buffer (WMS, WMTS, GISRaster) rather than populatin...
Definition gisrasterlayer.h:220
QString filePath
Definition gisrasterlayer.h:63
void filePathChanged(const QString &path)
void overviewBuildStarted(const QString &name)
A background overview (.ovr pyramid) build has started for this raster. name is the display file name...
bool isByteRaster() const
True when band 1 is 8-bit (GDT_Byte) — the only type the RGB composite path renders without saturatin...
Definition gisrasterlayer.cpp:478
double noDataValue
Definition gisrasterlayer.h:66
void openFinished(bool ok)
Emitted on the GUI thread when openAsync() completes.
bool hasNoDataValue() const
Returns true when the dataset has a known no-data value.
Definition gisrasterlayer.cpp:241
bool hasColorTable() const
True when band 1 carries a GDAL colour table (paletted raster).
Definition gisrasterlayer.cpp:449
void render(QPainter *painter, const MapExtent &extent, const QSize &imageSize, const SpatialReferenceSystem *srs) override
Renders this layer's content directly into painter.
Definition gisrasterlayer.cpp:894
void onCanvasCRSChanged(const SpatialReferenceSystem *newCanvasSRS) override
Called when the user changes the canvas CRS so layers can pre-compute any CRS-dependent transform cac...
Definition gisrasterlayer.cpp:968
QVector< double > sampleValues(int band, int maxSamples=200000) const
Decimated, overview-aware value sample of band — at most maxSamples finite, non-NoData values on a re...
Definition gisrasterlayer.cpp:381
double hillshadeAzimuthDeg() const
Definition gisrasterlayer.h:256
bool hillshadeEnabled() const
Definition gisrasterlayer.h:254
void rasterRendererChanged()
Emitted when setRasterRenderer() swaps the renderer pointer or notifyRasterRendererEdited() reports a...
QString sourceDescription() const override
Human-readable source of this layer: a file path, a service URL, or a description for in-memory / der...
Definition gisrasterlayer.cpp:243
QString detectVerticalUnit() const
Heuristically detects the vertical unit of the raster's elevation values from the embedded CRS metada...
Definition gisrasterlayer.cpp:259
QList< int > uniqueValues(int band, int cap=256) const
Distinct integer values observed in sampleValues(band), ascending, truncated to cap entries....
Definition gisrasterlayer.cpp:427
void refreshScene(QGraphicsScene *scene, const MapExtent &canvasExtent, const SpatialReferenceSystem *canvasSRS) override
Smart scene refresh — updates existing items instead of destroying and recreating.
Definition gisrasterlayer.cpp:945
OpenSWMM::Render::IRasterRenderer * rasterRenderer() const
The IRasterRenderer that will drive this layer's warp pass.
Definition gisrasterlayer.cpp:191
An axis-aligned bounding box in the coordinate space of a given CRS.
Definition mapextent.h:26
Abstract interface — every raster layer's paint path goes here.
Definition irasterrenderer.h:66
QString name() const
Returns the user-visible name of this layer.
Definition openswmmvislayer.cpp:62
SpatialReferenceSystem * srs() const
Returns the spatial reference system for this layer, or nullptr if the layer has no known CRS.
Definition openswmmvislayer.cpp:201
MapExtent extent() const
Returns the layer extent in the layer's own CRS.
Definition openswmmvislayer.cpp:252
Top-level project container managing sessions, the SWMM model layer, and persistence paths for one SW...
Definition openswmmvisworkspace.h:42
Definition graphicsitems.h:300
Represents a coordinate reference system backed by a GDAL OGRSpatialReference.
Definition spatialreferencesystem.h:28
Base class owning the tile cache + parent-fallback compositor shared by tile-pyramid layers.
Definition tilepyramidlayer.h:47
RasterColorRamp — gradient mapping from a normalised value in [0,1] to a display QColor.
QGraphicsScene * scene
Definition inletdrawingview.cpp:78
bool ok
Definition inpmeshwriter.cpp:575
QString params
Definition lidlayerdiagram.cpp:96
const char * key
Definition meshcellparams.cpp:24
Definition gisrasterlayer.h:40
Definition contourchain.h:23
Definition aquiferprovider.h:21
VS.6 — categorical (paletted) raster renderer.
int path
Definition pslgminsize.cpp:234
int src
index into the caller's vector; -1 = new
Definition pslgminsize.cpp:152
QVector< int > parent
union-find
Definition pslgminsize.cpp:176
Definition gisrasterlayer.cpp:981
Shared tile-pyramid machinery for tiled raster layers (GISRasterLayer, WMTSLayer): a thread-safe tile...