KOSMIndoorMap

mapcssselector.cpp
1/*
2 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "mapcsselementstate.h"
8#include "mapcssselector_p.h"
9#include "mapcsscondition_p.h"
10#include "mapcssresult.h"
11#include "mapcssstate_p.h"
12
13#include <QDebug>
14#include <QIODevice>
15
16#include <cmath>
17#include <cstring>
18
19using namespace KOSMIndoorMap;
20
21MapCSSSelector::MapCSSSelector() = default;
22MapCSSSelector::~MapCSSSelector() = default;
23
24MapCSSBasicSelector::MapCSSBasicSelector() = default;
25MapCSSBasicSelector::~MapCSSBasicSelector() = default;
26
27void MapCSSBasicSelector::compile(const OSM::DataSet &dataSet)
28{
29 for (const auto &c : conditions) {
30 c->compile(dataSet);
31 }
32}
33
34bool MapCSSBasicSelector::matches(const MapCSSState &state, MapCSSResult &result, const std::vector<std::unique_ptr<MapCSSDeclaration>> &declarations) const
35{
36 // check object type
37 switch (m_objectType) {
38 case MapCSSObjectType::Node: if (state.element.type() != OSM::Type::Node) return false; break;
39 case MapCSSObjectType::Way: if (state.element.type() != OSM::Type::Way) return false; break;
40 case MapCSSObjectType::Relation: if (state.element.type() != OSM::Type::Relation) return false; break;
41 case MapCSSObjectType::Area: if (state.objectType !=MapCSSObjectType::Area && state.objectType != MapCSSObjectType::LineOrArea) return false; break;
42 case MapCSSObjectType::Line: if (state.objectType !=MapCSSObjectType::Line && state.objectType != MapCSSObjectType::LineOrArea) return false; break;
43 case MapCSSObjectType::Canvas: return false;
44 case MapCSSObjectType::Any: break;
45 case MapCSSObjectType::LineOrArea: Q_UNREACHABLE();
46 }
47
48 // check zoom level
49 if (m_zoomLow > 0 && state.zoomLevel < m_zoomLow) {
50 return false;
51 }
52 if (m_zoomHigh > 0 && state.zoomLevel >= m_zoomHigh) {
53 return false;
54 }
55
56 if (m_elementState && (state.state & m_elementState) == 0) {
57 return false;
58 }
59
60 auto &resultLayer = result[m_layer];
61 if (!m_class.isNull() && !resultLayer.hasClass(m_class)) {
62 return false;
63 }
64
65 if (std::all_of(conditions.begin(), conditions.end(), [&state, &resultLayer](const auto &cond) { return cond->matches(state, resultLayer); })) {
66 resultLayer.applyDeclarations(declarations);
67 return true;
68 }
69 return false;
70}
71
72bool MapCSSBasicSelector::matchesCanvas(const MapCSSState &state) const
73{
74 if (m_objectType != MapCSSObjectType::Canvas) {
75 return false;
76 }
77
78 if (m_zoomLow > 0 && state.zoomLevel < m_zoomLow) {
79 return false;
80 }
81 if (m_zoomHigh > 0 && state.zoomLevel >= m_zoomHigh) {
82 return false;
83 }
84
85 return std::all_of(conditions.begin(), conditions.end(), [&state](const auto &cond) { return cond->matchesCanvas(state); });
86}
87
88LayerSelectorKey MapCSSBasicSelector::layerSelector() const
89{
90 return m_layer;
91}
92
93struct {
94 const char *name;
95 MapCSSObjectType type;
96} static constexpr const object_type_map[] = {
97 { "node", MapCSSObjectType::Node },
98 { "way", MapCSSObjectType::Way },
99 { "relation", MapCSSObjectType::Relation },
100 { "area", MapCSSObjectType::Area },
101 { "line", MapCSSObjectType::Line },
102 { "canvas", MapCSSObjectType::Canvas },
103 { "*", MapCSSObjectType::Any },
104};
105
106void MapCSSBasicSelector::write(QIODevice *out) const
107{
108 for (const auto &t : object_type_map) {
109 if (m_objectType == t.type) {
110 out->write(t.name);
111 break;
112 }
113 }
114
115 if (!m_class.isNull()) {
116 out->write(".");
117 out->write(m_class.name());
118 }
119
120 if (m_zoomLow > 0 || m_zoomHigh > 0) {
121 out->write("|z");
122 if (m_zoomLow == m_zoomHigh) {
123 out->write(QByteArray::number(m_zoomLow));
124 } else {
125 if (m_zoomLow > 0) {
126 out->write(QByteArray::number(m_zoomLow));
127 }
128 out->write("-");
129 if (m_zoomHigh > 0) {
130 out->write(QByteArray::number(m_zoomHigh));
131 }
132 }
133 }
134
135 for (const auto &cond : conditions) {
136 cond->write(out);
137 }
138
139 if (m_elementState & MapCSSElementState::Active) {
140 out->write(":active");
141 }
142 if (m_elementState & MapCSSElementState::Hovered) {
143 out->write(":hovered");
144 }
145
146 if (!m_layer.isNull()) {
147 out->write("::");
148 out->write(m_layer.name());
149 }
150}
151
152void MapCSSBasicSelector::setObjectType(const char *str, std::size_t len)
153{
154 for (const auto &t : object_type_map) {
155 if (std::strncmp(t.name, str, std::max(std::strlen(t.name), len)) == 0) {
156 m_objectType = t.type;
157 return;
158 }
159 }
160}
161
162void MapCSSBasicSelector::setZoomRange(int low, int high)
163{
164 m_zoomLow = low;
165 m_zoomHigh = high;
166}
167
168void MapCSSBasicSelector::setConditions(MapCSSConditionHolder *conds)
169{
170 if (!conds) {
171 return;
172 }
173 conditions = std::move(conds->conditions);
174 delete conds;
175}
176
177void MapCSSBasicSelector::setClass(ClassSelectorKey key)
178{
179 m_class = key;
180}
181
182void MapCSSBasicSelector::setPseudoClass(const char *str, std::size_t len)
183{
184 if (std::strncmp(str, "active", len) == 0) {
185 m_elementState |= MapCSSElementState::Active;
186 } else if (std::strncmp(str, "hovered", len) == 0) {
187 m_elementState |= MapCSSElementState::Hovered;
188 } else {
189 qWarning() << "Unhandled pseudo class:" << QByteArrayView(str, (qsizetype)len);
190 }
191}
192
193void MapCSSBasicSelector::setLayer(LayerSelectorKey key)
194{
195 m_layer = key;
196}
197
198
199void MapCSSChainedSelector::compile(const OSM::DataSet &dataSet)
200{
201 for (const auto &s : selectors) {
202 s->compile(dataSet);
203 }
204}
205
206bool MapCSSChainedSelector::matches([[maybe_unused]] const MapCSSState &state, [[maybe_unused]] MapCSSResult &result,
207 [[maybe_unused]] const std::vector<std::unique_ptr<MapCSSDeclaration>> &declarations) const
208{
209 // TODO
210 return false;
211}
212
213bool MapCSSChainedSelector::matchesCanvas(const MapCSSState &state) const
214{
215 Q_UNUSED(state);
216 // canvas cannot be chained
217 return false;
218}
219
220LayerSelectorKey MapCSSChainedSelector::layerSelector() const
221{
222 return selectors.back()->layerSelector();
223}
224
225void MapCSSChainedSelector::write(QIODevice *out) const
226{
227 assert(selectors.size() > 1);
228 selectors[0]->write(out);
229 for (auto it = std::next(selectors.begin()); it != selectors.end(); ++it) {
230 out->write(" ");
231 (*it)->write(out);
232 }
233}
234
235
236MapCSSUnionSelector::MapCSSUnionSelector() = default;
237MapCSSUnionSelector::~MapCSSUnionSelector() = default;
238
239void MapCSSUnionSelector::compile(const OSM::DataSet &dataSet)
240{
241 for (const auto &ls : m_selectors) {
242 for (const auto &s : ls.selectors) {
243 s->compile(dataSet);
244 }
245 }
246}
247
248bool MapCSSUnionSelector::matches(const MapCSSState &state, MapCSSResult &result, const std::vector<std::unique_ptr<MapCSSDeclaration>> &declarations) const
249{
250 bool ret = false;
251 for (const auto &ls : m_selectors) {
252 if (std::any_of(ls.selectors.begin(), ls.selectors.end(), [&state, &result, &declarations](const auto &selector) {
253 return selector->matches(state, result, declarations);
254
255 })) {
256 // no short-circuit evaluation, we want the matchCallback to be called once per matching layer!
257 ret = true;
258 }
259 }
260 return ret;
261}
262
263bool MapCSSUnionSelector::matchesCanvas(const MapCSSState &state) const
264{
265 for (const auto &ls : m_selectors) {
266 if (ls.layer.isNull()) {
267 return std::any_of(ls.selectors.begin(), ls.selectors.end(), [&state](const auto &selector) { return selector->matchesCanvas(state); });
268 }
269 }
270 return false;
271}
272
273LayerSelectorKey MapCSSUnionSelector::layerSelector() const
274{
275 return {};
276}
277
278void MapCSSUnionSelector::write(QIODevice *out) const
279{
280 for (std::size_t i = 0; i < m_selectors.size(); ++i) {
281 for (std::size_t j = 0; j < m_selectors[i].selectors.size(); ++j) {
282 if (i != 0 || j != 0) {
283 out->write(",\n");
284 }
285 m_selectors[i].selectors[j]->write(out);
286 }
287 }
288}
289
290void MapCSSUnionSelector::addSelector(std::unique_ptr<MapCSSSelector> &&selector)
291{
292 auto it = std::find_if(m_selectors.begin(), m_selectors.end(), [&selector](const auto &ls) {
293 return ls.layer == selector->layerSelector();
294 });
295 if (it != m_selectors.end()) {
296 (*it).selectors.push_back(std::move(selector));
297 } else {
298 SelectorMap ls;
299 ls.layer = selector->layerSelector();
300 ls.selectors.push_back(std::move(selector));
301 m_selectors.push_back(std::move(ls));
302 }
303}
Result of MapCSS stylesheet evaluation for all layer selectors.
A set of nodes, ways and relations.
Definition datatypes.h:343
OSM-based multi-floor indoor maps for buildings.
QByteArray number(double n, char format, int precision)
qint64 write(const QByteArray &data)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:57:46 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.