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_class.isNull() && !result[m_layer].hasClass(m_class)) {
57 return false;
58 }
59
60 if (m_elementState && (state.state & m_elementState) == 0) {
61 return false;
62 }
63
64 if (std::all_of(conditions.begin(), conditions.end(), [&state](const auto &cond) { return cond->matches(state); })) {
65 result.applyDeclarations(m_layer, declarations);
66 return true;
67 }
68 return false;
69}
70
71bool MapCSSBasicSelector::matchesCanvas(const MapCSSState &state) const
72{
73 if (m_objectType != MapCSSObjectType::Canvas) {
74 return false;
75 }
76
77 if (m_zoomLow > 0 && state.zoomLevel < m_zoomLow) {
78 return false;
79 }
80 if (m_zoomHigh > 0 && state.zoomLevel >= m_zoomHigh) {
81 return false;
82 }
83
84 return std::all_of(conditions.begin(), conditions.end(), [&state](const auto &cond) { return cond->matchesCanvas(state); });
85}
86
87LayerSelectorKey MapCSSBasicSelector::layerSelector() const
88{
89 return m_layer;
90}
91
92struct {
93 const char *name;
94 MapCSSObjectType type;
95} static constexpr const object_type_map[] = {
96 { "node", MapCSSObjectType::Node },
97 { "way", MapCSSObjectType::Way },
98 { "relation", MapCSSObjectType::Relation },
99 { "area", MapCSSObjectType::Area },
100 { "line", MapCSSObjectType::Line },
101 { "canvas", MapCSSObjectType::Canvas },
102 { "*", MapCSSObjectType::Any },
103};
104
105void MapCSSBasicSelector::write(QIODevice *out) const
106{
107 for (const auto &t : object_type_map) {
108 if (m_objectType == t.type) {
109 out->write(t.name);
110 break;
111 }
112 }
113
114 if (!m_class.isNull()) {
115 out->write(".");
116 out->write(m_class.name());
117 }
118
119 if (m_zoomLow > 0 || m_zoomHigh > 0) {
120 out->write("|z");
121 if (m_zoomLow == m_zoomHigh) {
122 out->write(QByteArray::number(m_zoomLow));
123 } else {
124 if (m_zoomLow > 0) {
125 out->write(QByteArray::number(m_zoomLow));
126 }
127 out->write("-");
128 if (m_zoomHigh > 0) {
129 out->write(QByteArray::number(m_zoomHigh));
130 }
131 }
132 }
133
134 for (const auto &cond : conditions) {
135 cond->write(out);
136 }
137
138 if (m_elementState & MapCSSElementState::Active) {
139 out->write(":active");
140 }
141 if (m_elementState & MapCSSElementState::Hovered) {
142 out->write(":hovered");
143 }
144
145 if (!m_layer.isNull()) {
146 out->write("::");
147 out->write(m_layer.name());
148 }
149}
150
151void MapCSSBasicSelector::setObjectType(const char *str, std::size_t len)
152{
153 for (const auto &t : object_type_map) {
154 if (std::strncmp(t.name, str, std::max(std::strlen(t.name), len)) == 0) {
155 m_objectType = t.type;
156 return;
157 }
158 }
159}
160
161void MapCSSBasicSelector::setZoomRange(int low, int high)
162{
163 m_zoomLow = low;
164 m_zoomHigh = high;
165}
166
167void MapCSSBasicSelector::setConditions(MapCSSConditionHolder *conds)
168{
169 if (!conds) {
170 return;
171 }
172 conditions = std::move(conds->conditions);
173 delete conds;
174}
175
176void MapCSSBasicSelector::setClass(ClassSelectorKey key)
177{
178 m_class = key;
179}
180
181void MapCSSBasicSelector::setPseudoClass(const char *str, std::size_t len)
182{
183 if (std::strncmp(str, "active", len) == 0) {
184 m_elementState |= MapCSSElementState::Active;
185 } else if (std::strncmp(str, "hovered", len) == 0) {
186 m_elementState |= MapCSSElementState::Hovered;
187 } else {
188 qWarning() << "Unhandled pseudo class:" << QByteArrayView(str, (qsizetype)len);
189 }
190}
191
192void MapCSSBasicSelector::setLayer(LayerSelectorKey key)
193{
194 m_layer = key;
195}
196
197
198void MapCSSChainedSelector::compile(const OSM::DataSet &dataSet)
199{
200 for (const auto &s : selectors) {
201 s->compile(dataSet);
202 }
203}
204
205bool MapCSSChainedSelector::matches([[maybe_unused]] const MapCSSState &state, [[maybe_unused]] MapCSSResult &result,
206 [[maybe_unused]] const std::vector<std::unique_ptr<MapCSSDeclaration>> &declarations) const
207{
208 // TODO
209 return false;
210}
211
212bool MapCSSChainedSelector::matchesCanvas(const MapCSSState &state) const
213{
214 Q_UNUSED(state);
215 // canvas cannot be chained
216 return false;
217}
218
219LayerSelectorKey MapCSSChainedSelector::layerSelector() const
220{
221 return selectors.back()->layerSelector();
222}
223
224void MapCSSChainedSelector::write(QIODevice *out) const
225{
226 assert(selectors.size() > 1);
227 selectors[0]->write(out);
228 for (auto it = std::next(selectors.begin()); it != selectors.end(); ++it) {
229 out->write(" ");
230 (*it)->write(out);
231 }
232}
233
234
235MapCSSUnionSelector::MapCSSUnionSelector() = default;
236MapCSSUnionSelector::~MapCSSUnionSelector() = default;
237
238void MapCSSUnionSelector::compile(const OSM::DataSet &dataSet)
239{
240 for (const auto &ls : m_selectors) {
241 for (const auto &s : ls.selectors) {
242 s->compile(dataSet);
243 }
244 }
245}
246
247bool MapCSSUnionSelector::matches(const MapCSSState &state, MapCSSResult &result, const std::vector<std::unique_ptr<MapCSSDeclaration>> &declarations) const
248{
249 bool ret = false;
250 for (const auto &ls : m_selectors) {
251 if (std::any_of(ls.selectors.begin(), ls.selectors.end(), [&state, &result, &declarations](const auto &selector) {
252 return selector->matches(state, result, declarations);
253
254 })) {
255 // no short-circuit evaluation, we want the matchCallback to be called once per matching layer!
256 ret = true;
257 }
258 }
259 return ret;
260}
261
262bool MapCSSUnionSelector::matchesCanvas(const MapCSSState &state) const
263{
264 for (const auto &ls : m_selectors) {
265 if (ls.layer.isNull()) {
266 return std::any_of(ls.selectors.begin(), ls.selectors.end(), [&state](const auto &selector) { return selector->matchesCanvas(state); });
267 }
268 }
269 return false;
270}
271
272LayerSelectorKey MapCSSUnionSelector::layerSelector() const
273{
274 return {};
275}
276
277void MapCSSUnionSelector::write(QIODevice *out) const
278{
279 for (std::size_t i = 0; i < m_selectors.size(); ++i) {
280 for (std::size_t j = 0; j < m_selectors[i].selectors.size(); ++j) {
281 if (i != 0 || j != 0) {
282 out->write(",\n");
283 }
284 m_selectors[i].selectors[j]->write(out);
285 }
286 }
287}
288
289void MapCSSUnionSelector::addSelector(std::unique_ptr<MapCSSSelector> &&selector)
290{
291 auto it = std::find_if(m_selectors.begin(), m_selectors.end(), [&selector](const auto &ls) {
292 return ls.layer == selector->layerSelector();
293 });
294 if (it != m_selectors.end()) {
295 (*it).selectors.push_back(std::move(selector));
296 } else {
297 SelectorMap ls;
298 ls.layer = selector->layerSelector();
299 ls.selectors.push_back(std::move(selector));
300 m_selectors.push_back(std::move(ls));
301 }
302}
Result of MapCSS stylesheet evaluation for all layer selectors.
A set of nodes, ways and relations.
Definition datatypes.h:340
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 Tue Mar 26 2024 11:20:03 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.