KOSMIndoorMap

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

KDE's Doxygen guidelines are available online.