KOSMIndoorMap

mapcssresult.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 "mapcssresult.h"
8#include "mapcssdeclaration_p.h"
9#include "mapcssexpressioncontext_p.h"
10#include "mapcssstate_p.h"
11#include "mapcssvalue_p.h"
12
13#include <osm/datatypes.h>
14#include <osm/languages.h>
15
16#include <algorithm>
17
18using namespace KOSMIndoorMap;
19
20namespace KOSMIndoorMap {
21struct MapCSSResultLayerTag {
22 OSM::TagKey key;
23 QByteArray value;
24 const MapCSSDeclaration *expression = nullptr;
25
26 [[nodiscard]] inline bool operator<(OSM::TagKey rhs) const
27 {
28 return key < rhs;
29 }
30};
31
32class MapCSSResultLayerPrivate {
33public:
34 void setTag(OSM::TagKey key, QByteArray value);
35 void setTag(OSM::TagKey key, const MapCSSDeclaration *expression);
36
37 std::vector<const MapCSSDeclaration*> m_declarations;
38 std::vector<ClassSelectorKey> m_classes;
39 std::vector<MapCSSResultLayerTag> m_tags;
40 LayerSelectorKey m_layer;
41 int m_flags = 0;
42};
43}
44
45MapCSSResultLayer::MapCSSResultLayer()
46 : d(new MapCSSResultLayerPrivate)
47{
48}
49MapCSSResultLayer::MapCSSResultLayer(MapCSSResultLayer&&) noexcept = default;
50MapCSSResultLayer& MapCSSResultLayer::operator=(MapCSSResultLayer&&) noexcept = default;
52
53void MapCSSResultLayer::clear()
54{
55 d->m_declarations.clear();
56 d->m_classes.clear();
57 d->m_tags.clear();
58 d->m_flags = MapCSSDeclaration::NoFlag;
59 d->m_layer = {};
60}
61
63{
64 return d->m_flags & MapCSSDeclaration::AreaProperty;
65}
66
68{
69 return d->m_flags & MapCSSDeclaration::LineProperty;
70}
71
73{
74 return d->m_flags & MapCSSDeclaration::LabelProperty;
75}
76
78{
79 return d->m_flags & MapCSSDeclaration::ExtrudeProperty;
80}
81
82const MapCSSDeclaration* MapCSSResultLayer::declaration(MapCSSProperty prop) const
83{
84 const auto it = std::lower_bound(d->m_declarations.begin(), d->m_declarations.end(), prop, [](auto lhs, auto rhs) {
85 return lhs->property() < rhs;
86 });
87 if (it == d->m_declarations.end() || (*it)->property() != prop) {
88 return nullptr;
89 }
90 return (*it);
91}
92
93const std::vector<const MapCSSDeclaration*>& MapCSSResultLayer::declarations() const
94{
95 return d->m_declarations;
96}
97
98void MapCSSResultLayer::addDeclaration(const MapCSSDeclaration *decl)
99{
100 const auto it = std::lower_bound(d->m_declarations.begin(), d->m_declarations.end(), decl, [](auto lhs, auto rhs) {
101 return lhs->property() < rhs->property();
102 });
103 if (it == d->m_declarations.end() || (*it)->property() != decl->property()) {
104 d->m_declarations.insert(it, decl);
105 } else {
106 (*it) = decl;
107 }
108
109 d->m_flags |= decl->propertyFlags();
110}
111
112void MapCSSResultLayer::addClass(ClassSelectorKey cls)
113{
114 const auto it = std::lower_bound(d->m_classes.begin(), d->m_classes.end(), cls);
115 if (it == d->m_classes.end() || (*it) != cls) {
116 d->m_classes.insert(it, cls);
117 }
118}
119
120bool MapCSSResultLayer::hasClass(ClassSelectorKey cls) const
121{
122 return std::binary_search(d->m_classes.begin(), d->m_classes.end(), cls);
123}
124
125LayerSelectorKey MapCSSResultLayer::layerSelector() const
126{
127 return d->m_layer;
128}
129
130std::optional<QByteArray> MapCSSResultLayer::resolvedTagValue(OSM::TagKey key, const MapCSSState &state) const
131{
132 if (const auto it = std::lower_bound(d->m_tags.begin(), d->m_tags.end(), key); it != d->m_tags.end() && (*it).key == key) {
133 if ((*it).expression) {
134 MapCSSExpressionContext context{ .state = state, .result = *this };
135 auto r = (*it).expression->evaluateExpression(context).asString();
136 return r.isEmpty() ? std::optional<QByteArray>() : r;
137 }
138 return (*it).value;
139 }
140 const auto tagEnd = state.element.tagsEnd();
141 if (const auto it = std::lower_bound(state.element.tagsBegin(), tagEnd, key); it != tagEnd && (*it).key == key) {
142 return (*it).value;
143 }
144 return {};
145}
146
147std::optional<QByteArray> MapCSSResultLayer::resolvedTagValue(const char *key, const MapCSSState &state) const
148{
149 // NOTE: m_tags is not sorted by lexicographic order but my TagKey order!
150 if (const auto it = std::find_if(d->m_tags.begin(), d->m_tags.end(), [key](const auto &tag) { return std::strcmp(tag.key.name(), key) == 0; }); it != d->m_tags.end()) {
151 if ((*it).expression) {
152 MapCSSExpressionContext context{ .state = state, .result = *this };
153 auto v = (*it).expression->evaluateExpression(context).asString();
154 return v.isEmpty() ? std::optional<QByteArray>() : v;
155 }
156 return (*it).value;
157 }
158 auto v = state.element.tagValue(key);
159 return v.isEmpty() ? std::optional<QByteArray>() : v;
160}
161
162std::optional<QByteArray> MapCSSResultLayer::resolvedTagValue(const OSM::Languages &languages, const char *key, const MapCSSState &state) const
163{
164 // NOTE: m_tags is not sorted by lexicographic order but my TagKey order!
165 // TODO: multi-lingual expressions?
166 if (const auto it = std::find_if(d->m_tags.begin(), d->m_tags.end(), [key](const auto &tag) { return std::strcmp(tag.key.name(), key) == 0; }); it != d->m_tags.end()) {
167 if ((*it).expression) {
168 MapCSSExpressionContext context{ .state = state, .result = *this };
169 auto v = (*it).expression->evaluateExpression(context).asString();
170 return v.isEmpty() ? std::optional<QByteArray>() : v;
171 }
172 return (*it).value;
173 }
174 auto v = state.element.tagValue(languages, key);
175 return v.isEmpty() ? std::optional<QByteArray>() : v;
176}
177
178void MapCSSResultLayer::setLayerSelector(LayerSelectorKey layer)
179{
180 d->m_layer = layer;
181}
182
183void MapCSSResultLayerPrivate::setTag(OSM::TagKey key, QByteArray value)
184{
185 const auto it = std::lower_bound(m_tags.begin(), m_tags.end(), key);
186 if (it == m_tags.end() || (*it).key != key) {
187 m_tags.insert(it, {.key = key, .value = std::move(value), .expression = nullptr });
188 } else {
189 (*it).value = std::move(value);
190 (*it).expression = nullptr;
191 }
192}
193
194void MapCSSResultLayerPrivate::setTag(OSM::TagKey key, const MapCSSDeclaration *expression)
195{
196 const auto it = std::lower_bound(m_tags.begin(), m_tags.end(), key);
197 if (it == m_tags.end() || (*it).key != key) {
198 m_tags.insert(it, {.key = key, .value = {}, .expression = expression});
199 } else {
200 (*it).value.clear();
201 (*it).expression = expression;
202 }
203}
204
205void MapCSSResultLayer::applyDeclarations(const std::vector<std::unique_ptr<MapCSSDeclaration>> &declarations)
206{
207 for (const auto &decl : declarations) {
208 switch (decl->type()) {
209 case MapCSSDeclaration::PropertyDeclaration:
210 addDeclaration(decl.get());
211 break;
212 case MapCSSDeclaration::ClassDeclaration:
213 addClass(decl->classSelectorKey());
214 break;
215 case MapCSSDeclaration::TagDeclaration:
216 if (decl->hasExpression()) {
217 d->setTag(decl->tagKey(), decl.get());
218 } else if (!std::isnan(decl->doubleValue())) {
219 d->setTag(decl->tagKey(), QByteArray::number(decl->doubleValue()));
220 } else {
221 d->setTag(decl->tagKey(), decl->stringValue().toUtf8());
222 }
223 break;
224 }
225 }
226}
227
228
229namespace KOSMIndoorMap {
230class MapCSSResultPrivate {
231public:
232 std::vector<MapCSSResultLayer> m_results;
233 std::vector<MapCSSResultLayer> m_inactivePool; // for reuse of already allocated result items
234};
235}
236
237MapCSSResult::MapCSSResult()
238 : d(new MapCSSResultPrivate)
239{
240}
241
242MapCSSResult::MapCSSResult(MapCSSResult&&) noexcept = default;
243MapCSSResult::~MapCSSResult() = default;
244MapCSSResult& MapCSSResult::operator=(MapCSSResult&&) noexcept = default;
245
246void MapCSSResult::clear()
247{
248 std::move(d->m_results.begin(), d->m_results.end(), std::back_inserter(d->m_inactivePool));
249 d->m_results.clear();
250 std::for_each(d->m_inactivePool.begin(), d->m_inactivePool.end(), std::mem_fn(&MapCSSResultLayer::clear));
251}
252
253const std::vector<MapCSSResultLayer>& MapCSSResult::results() const
254{
255 return d->m_results;
256}
257
258MapCSSResultLayer& MapCSSResult::operator[](LayerSelectorKey layer)
259{
260 const auto it = std::find_if(d->m_results.begin(), d->m_results.end(), [layer](const auto &res) {
261 return res.layerSelector() == layer;
262 });
263 if (it != d->m_results.end()) {
264 return *it;
265 }
266
267 if (!d->m_inactivePool.empty()) {
268 auto res = std::move(d->m_inactivePool.back());
269 d->m_inactivePool.pop_back();
270 res.setLayerSelector(layer);
271 d->m_results.push_back(std::move(res));
272 } else {
274 res.setLayerSelector(layer);
275 d->m_results.push_back(std::move(res));
276 }
277 return d->m_results.back();
278}
279
280const MapCSSResultLayer& MapCSSResult::operator[](LayerSelectorKey layer) const
281{
282 const auto it = std::find_if(d->m_results.begin(), d->m_results.end(), [layer](const auto &res) {
283 return res.layerSelector() == layer;
284 });
285 if (it != d->m_results.end()) {
286 return *it;
287 }
288
289 if (d->m_inactivePool.empty()) {
290 d->m_inactivePool.emplace_back();
291 }
292 return d->m_inactivePool.back();
293}
Result of MapCSS stylesheet evaluation for a single layer selector.
bool hasClass(ClassSelectorKey cls) const
Check whether this result layer has class cls set.
LayerSelectorKey layerSelector() const
The layer selector for this result.
bool hasLineProperties() const
Returns true if a way/line needs to be drawn.
std::optional< QByteArray > resolvedTagValue(OSM::TagKey key, const MapCSSState &state) const
Returns the tag value set by preceding declarations, via MapCSS expressions or in the source data.
bool hasLabelProperties() const
Returns true if a label needs to be drawn.
const MapCSSDeclaration * declaration(MapCSSProperty prop) const
Returns the declaration for property @prop, or nullptr is this property isn't set.
bool hasExtrudeProperties() const
Returns true if a 3D extrusion is requested.
const std::vector< const MapCSSDeclaration * > & declarations() const
The active declarations for the queried element.
bool hasAreaProperties() const
Returns true if an area/polygon needs to be drawn.
Result of MapCSS stylesheet evaluation for all layer selectors.
const MapCSSResultLayer & operator[](LayerSelectorKey layer) const
Access a specific result layer selector.
const std::vector< MapCSSResultLayer > & results() const
Results for all layer selectors.
Languages in preference order to consider when looking up translated tag values.
Definition languages.h:25
A key of an OSM tag.
Definition datatypes.h:179
OSM-based multi-floor indoor maps for buildings.
MapCSSProperty
Known properties in MapCSS declarations.
QByteArray number(double n, char format, int precision)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:17:55 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.