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
isublayerhost.h
Go to the documentation of this file.
1
43#ifndef OPENSWMM_RENDER_ISUBLAYERHOST_H
44#define OPENSWMM_RENDER_ISUBLAYERHOST_H
45
46#include "render/isublayer.h"
48
49#include <QHash>
50#include <QJsonArray>
51#include <QJsonObject>
52#include <QJsonValue>
53#include <QList>
54#include <QString>
55
56namespace OpenSWMM::Render
57{
58
64{
65public:
66 virtual ~ISublayerHost() = default;
67
79 [[nodiscard]] virtual QList<ISublayer *> sublayers() const = 0;
80
92 virtual bool moveSublayer(int from, int to)
93 {
94 Q_UNUSED(from);
95 Q_UNUSED(to);
96 return false;
97 }
98
116 virtual void dispatchAnimationTick(int period)
117 {
118 Q_UNUSED(period);
119 for (ISublayer *s : sublayers())
120 {
121 if (s && s->isDynamic())
122 s->invalidate();
123 }
124 }
125
133 virtual void onSublayersJsonLoaded(const QJsonObject &sublayersJson)
134 {
135 Q_UNUSED(sublayersJson);
136 }
137
138 // ── Slice S6.1 — JSON persistence helpers ──────────────────────────
139 //
140 // Layers call these at their `.oswp` save/load points to round-trip
141 // every sublayer's visibility / opacity / style bag. Schema:
142 //
143 // {
144 // "sublayers": [
145 // { "id": "...", "visible": true, "opacity": 1.0,
146 // "style": { ... per-sublayer style JSON ... } },
147 // ...
148 // ]
149 // }
150 //
151 // Free static functions because the host interface is non-QObject
152 // and we want them callable from layer save/load without
153 // instantiating anything. Forward-compatible: unknown sublayer ids
154 // in the JSON are silently skipped (the layer ships with whatever
155 // sublayers it constructed in its ctor).
156
157 [[nodiscard]] static QJsonObject saveSublayersToJson(const ISublayerHost &host)
158 {
159 QJsonArray arr;
160 for (const ISublayer *s : host.sublayers())
161 {
162 if (!s) continue;
163 QJsonObject row;
164 row.insert(QStringLiteral("id"), s->id());
165 row.insert(QStringLiteral("visible"), s->isVisible());
166 row.insert(QStringLiteral("opacity"), s->opacity());
167 if (auto *style = const_cast<ISublayer *>(s)->style())
168 row.insert(QStringLiteral("style"), style->toJson());
169 arr.append(row);
170 }
171 QJsonObject obj;
172 obj.insert(QStringLiteral("sublayers"), arr);
173 return obj;
174 }
175
191 [[nodiscard]] static QList<LegendSymbolItem>
193 {
194 QList<LegendSymbolItem> out;
195 for (const ISublayer *s : host.sublayers())
196 {
197 if (!s || !s->isVisible()) continue;
198 out.append(s->legendSymbolItems());
199 }
200 return out;
201 }
202
210 [[nodiscard]] static ISublayer *
211 findSublayer(const ISublayerHost &host, const QString &id)
212 {
213 for (ISublayer *s : host.sublayers())
214 if (s && s->id() == id) return s;
215 return nullptr;
216 }
217
218 static void loadSublayersFromJson(ISublayerHost &host, const QJsonObject &j)
219 {
220 const QJsonArray arr = j.value(QStringLiteral("sublayers")).toArray();
221 // Index host's current sublayers by id so we can apply rows out of order
222 // and tolerate added/removed sublayers between schema versions.
223 QHash<QString, ISublayer *> byId;
224 for (ISublayer *s : host.sublayers())
225 if (s) byId.insert(s->id(), s);
226
227 for (const QJsonValue &val : arr)
228 {
229 const QJsonObject row = val.toObject();
230 const QString id = row.value(QStringLiteral("id")).toString();
231 auto it = byId.constFind(id);
232 if (it == byId.constEnd()) continue; // unknown sublayer id — skip
233
234 ISublayer *s = it.value();
235 if (row.contains(QStringLiteral("visible")))
236 s->setVisible(row.value(QStringLiteral("visible")).toBool());
237 if (row.contains(QStringLiteral("opacity")))
238 s->setOpacity(row.value(QStringLiteral("opacity")).toDouble());
239 if (row.contains(QStringLiteral("style")))
240 if (auto *style = s->style())
241 style->fromJson(row.value(QStringLiteral("style")).toObject());
242 }
243
244 // Replay the saved paint order — JSON array index 0 = bottom of
245 // the paint stack. For each known id, ensure it ends up at the
246 // intended target index by calling moveSublayer(). Hosts that
247 // don't implement reorder return false and the order silently
248 // stays as constructed.
249 int target = 0;
250 for (const QJsonValue &val : arr)
251 {
252 const QString id = val.toObject()
253 .value(QStringLiteral("id")).toString();
254 const auto cur = host.sublayers();
255 int found = -1;
256 for (int i = 0; i < cur.size(); ++i)
257 if (cur[i] && cur[i]->id() == id) { found = i; break; }
258 if (found < 0) continue; // not in this host — skip silently
259 if (found != target)
260 host.moveSublayer(found, target);
261 ++target;
262 }
263
264 // Give the host a chance to migrate style data that moved between
265 // sublayers across schema versions (see onSublayersJsonLoaded).
266 host.onSublayersJsonLoaded(j);
267 }
268};
269
270} // namespace OpenSWMM::Render
271
272#endif // OPENSWMM_RENDER_ISUBLAYERHOST_H
Mixin interface for layers that expose a list of ISublayers.
Definition isublayerhost.h:64
static QJsonObject saveSublayersToJson(const ISublayerHost &host)
Definition isublayerhost.h:157
static QList< LegendSymbolItem > aggregatedLegendSymbolItems(const ISublayerHost &host)
Aggregates every visible sublayer's legendSymbolItems() into a single flat list (Slice S4 / §J....
Definition isublayerhost.h:192
virtual void onSublayersJsonLoaded(const QJsonObject &sublayersJson)
Hook fired at the end of loadSublayersFromJson with the raw payload, so hosts can migrate style data ...
Definition isublayerhost.h:133
static ISublayer * findSublayer(const ISublayerHost &host, const QString &id)
O(N) lookup of a sublayer by its stable id. Returns nullptr when not found. Used by the legend dock's...
Definition isublayerhost.h:211
static void loadSublayersFromJson(ISublayerHost &host, const QJsonObject &j)
Definition isublayerhost.h:218
virtual void dispatchAnimationTick(int period)
Hook invoked by AnimationController on every time-step change.
Definition isublayerhost.h:116
virtual ~ISublayerHost()=default
virtual bool moveSublayer(int from, int to)
Move the sublayer at from to position to in paint order.
Definition isublayerhost.h:92
virtual QList< ISublayer * > sublayers() const =0
The ordered list of sublayers owned by this layer.
One toggleable visual aspect of a results / model layer.
Definition isublayer.h:87
virtual SublayerStyle * style()=0
Returns the property-bag QObject that owns this sublayer's style knobs.
virtual void setVisible(bool)=0
size_t i
Definition contourjob.cpp:27
double s
Scene px per model length unit.
Definition inletdrawingview.cpp:82
Toggleable visual aspect of an output layer.
hid_t id
Definition mesh2dh5reader.cpp:29
Definition gisrasterlayer.h:40
QObject base class for per-sublayer style property bags.