KOSMIndoorMap

mapcssdeclaration.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 "mapcssdeclaration_p.h"
8
9#include "logging.h"
10#include "mapcssproperty.h"
11#include "mapcssvalue_p.h"
12
13#include <QDebug>
14#include <QIODevice>
15
16#include <cstring>
17
18using namespace KOSMIndoorMap;
19
20// keep this sorted by property name!
21struct {
22 const char* name;
23 MapCSSProperty property;
24 int flags;
25} static constexpr const property_types[] = {
26 // only those properties have their corresonding flag set that actually trigger emission of a scene graph item
27 // e.g. for a label we either need a text or an icon, the visual properties for those on their own would be a no-op
28 { "casing-color", MapCSSProperty::CasingColor, MapCSSDeclaration::NoFlag },
29 { "casing-dashes", MapCSSProperty::CasingDashes, MapCSSDeclaration::NoFlag },
30 { "casing-linecap", MapCSSProperty::CasingLineCap, MapCSSDeclaration::NoFlag },
31 { "casing-linejoin", MapCSSProperty::CasingLineJoin, MapCSSDeclaration::NoFlag },
32 { "casing-opacity", MapCSSProperty::CasingOpacity, MapCSSDeclaration::NoFlag },
33 { "casing-width", MapCSSProperty::CasingWidth, MapCSSDeclaration::NoFlag },
34 { "color", MapCSSProperty::Color, MapCSSDeclaration::LineProperty },
35 { "dashes", MapCSSProperty::Dashes, MapCSSDeclaration::NoFlag },
36 { "extrude", MapCSSProperty::Extrude, MapCSSDeclaration::ExtrudeProperty },
37 { "fill-color", MapCSSProperty::FillColor, MapCSSDeclaration::AreaProperty | MapCSSDeclaration::CanvasProperty }, // TODO this also applies to lines
38 { "fill-image", MapCSSProperty::FillImage, MapCSSDeclaration::AreaProperty | MapCSSDeclaration::CanvasProperty },
39 { "fill-opacity", MapCSSProperty::FillOpacity, MapCSSDeclaration::AreaProperty },
40 { "font-family", MapCSSProperty::FontFamily, MapCSSDeclaration::NoFlag },
41 { "font-size", MapCSSProperty::FontSize, MapCSSDeclaration::NoFlag },
42 { "font-style", MapCSSProperty::FontStyle, MapCSSDeclaration::NoFlag },
43 { "font-variant", MapCSSProperty::FontVariant, MapCSSDeclaration::NoFlag },
44 { "font-weight", MapCSSProperty::FontWeight, MapCSSDeclaration::NoFlag },
45 { "icon-allow-icon-overlap", MapCSSProperty::IconAllowIconOverlap, MapCSSDeclaration::NoFlag },
46 { "icon-allow-text-overlap", MapCSSProperty::IconAllowTextOverlap, MapCSSDeclaration::NoFlag },
47 { "icon-color", MapCSSProperty::IconColor, MapCSSDeclaration::NoFlag },
48 { "icon-height", MapCSSProperty::IconHeight, MapCSSDeclaration::NoFlag },
49 { "icon-image", MapCSSProperty::IconImage, MapCSSDeclaration::LabelProperty },
50 { "icon-opacity", MapCSSProperty::IconOpacity, MapCSSDeclaration::NoFlag },
51 { "icon-width", MapCSSProperty::IconWidth, MapCSSDeclaration::NoFlag },
52 { "image", MapCSSProperty::Image, MapCSSDeclaration::LineProperty },
53 { "linecap", MapCSSProperty::LineCap, MapCSSDeclaration::NoFlag },
54 { "linejoin", MapCSSProperty::LineJoin, MapCSSDeclaration::NoFlag },
55 { "max-width", MapCSSProperty::MaxWidth, MapCSSDeclaration::NoFlag },
56 { "opacity", MapCSSProperty::Opacity, MapCSSDeclaration::NoFlag },
57 { "shield-casing-color", MapCSSProperty::ShieldCasingColor, MapCSSDeclaration::LabelProperty },
58 { "shield-casing-width", MapCSSProperty::ShieldCasingWidth, MapCSSDeclaration::NoFlag },
59 { "shield-color", MapCSSProperty::ShieldColor, MapCSSDeclaration::LabelProperty },
60 { "shield-frame-color", MapCSSProperty::ShieldFrameColor, MapCSSDeclaration::LabelProperty },
61 { "shield-frame-width", MapCSSProperty::ShieldFrameWidth, MapCSSDeclaration::NoFlag },
62 { "shield-image", MapCSSProperty::ShieldImage, MapCSSDeclaration::LabelProperty },
63 { "shield-opacity", MapCSSProperty::ShieldOpacity, MapCSSDeclaration::NoFlag },
64 { "shield-shape", MapCSSProperty::ShieldShape, MapCSSDeclaration::NoFlag },
65 { "shield-text", MapCSSProperty::ShieldText, MapCSSDeclaration::LabelProperty },
66 { "text", MapCSSProperty::Text, MapCSSDeclaration::LabelProperty },
67 { "text-color", MapCSSProperty::TextColor, MapCSSDeclaration::CanvasProperty },
68 { "text-decoration", MapCSSProperty::TextDecoration, MapCSSDeclaration::NoFlag },
69 { "text-halo-color", MapCSSProperty::TextHaloColor, MapCSSDeclaration::NoFlag },
70 { "text-halo-radius", MapCSSProperty::TextHaloRadius, MapCSSDeclaration::NoFlag },
71 { "text-offset", MapCSSProperty::TextOffset, MapCSSDeclaration::NoFlag },
72 { "text-opacity", MapCSSProperty::TextOpacity, MapCSSDeclaration::NoFlag },
73 { "text-position", MapCSSProperty::TextPosition, MapCSSDeclaration::NoFlag },
74 { "text-require-fit", MapCSSProperty::TextRequireFit, MapCSSDeclaration::NoFlag }, // custom extension
75 { "text-transform", MapCSSProperty::TextTransform, MapCSSDeclaration::NoFlag },
76 { "width", MapCSSProperty::Width, MapCSSDeclaration::LineProperty },
77 { "z-index", MapCSSProperty::ZIndex, MapCSSDeclaration::NoFlag },
78};
79
80struct {
81 const char *name;
82 Qt::PenCapStyle capStyle;
83} static constexpr const capstyle_map[] = {
84 { "none", Qt::FlatCap },
85 { "round", Qt::RoundCap },
86 { "square", Qt::SquareCap },
87};
88
89struct {
90 const char *name;
91 Qt::PenJoinStyle joinStyle;
92} static constexpr const joinstyle_map[] = {
93 { "bevel", Qt::BevelJoin },
94 { "miter", Qt::MiterJoin },
95 { "round", Qt::RoundJoin },
96};
97
98struct {
99 const char *name;
100 QFont::Capitalization capitalizationStyle;
101} static constexpr const capitalizationstyle_map[] = {
102 { "capitalize", QFont::Capitalize },
103 { "lowercase", QFont::AllLowercase },
104 { "none", QFont::MixedCase },
105 { "normal", QFont::MixedCase },
106 { "small-caps", QFont::SmallCaps },
107 { "uppercase", QFont::AllUppercase },
108};
109
110struct {
111 const char *name;
112 MapCSSDeclaration::Unit unit;
113} static constexpr const unit_map[] = {
114 { "m", MapCSSDeclaration::Meters },
115 { "pt", MapCSSDeclaration::Point },
116 { "px", MapCSSDeclaration::Pixels },
117};
118
119struct {
120 const char *name;
121 MapCSSDeclaration::Position position;
122} static constexpr const position_map[] = {
123 { "center", MapCSSDeclaration::Position::Center },
124 { "line", MapCSSDeclaration::Position::Line },
125};
126
127MapCSSDeclaration::MapCSSDeclaration(Type type)
128 : m_type(type)
129{
130}
131
132MapCSSDeclaration::~MapCSSDeclaration() = default;
133
134bool MapCSSDeclaration::isValid() const
135{
136 switch (m_type) {
137 case PropertyDeclaration:
138 return property() != MapCSSProperty::Unknown;
139 case TagDeclaration:
140 return !m_identValue.isEmpty();
141 case ClassDeclaration:
142 return !m_class.isNull();
143 }
144
145 Q_UNREACHABLE();
146 return false;
147}
148
149MapCSSDeclaration::Type MapCSSDeclaration::type() const
150{
151 return m_type;
152}
153
154MapCSSProperty MapCSSDeclaration::property() const
155{
156 return m_property;
157}
158
159int MapCSSDeclaration::propertyFlags() const
160{
161 return m_flags;
162}
163
164int MapCSSDeclaration::intValue() const
165{
166 return m_doubleValue;
167}
168
169double MapCSSDeclaration::doubleValue() const
170{
171 return m_doubleValue;
172}
173
174bool MapCSSDeclaration::boolValue() const
175{
176 return m_boolValue;
177}
178
179QString MapCSSDeclaration::stringValue() const
180{
181 return m_stringValue;
182}
183
184QColor MapCSSDeclaration::colorValue() const
185{
186 if (!m_colorValue.isValid() && !m_stringValue.isEmpty()) {
187 return QColor(m_stringValue);
188 }
189 return m_colorValue;
190}
191
192QByteArray MapCSSDeclaration::keyValue() const
193{
194 return m_identValue;
195}
196
197QVector<double> MapCSSDeclaration::dashesValue() const
198{
199 return m_dashValue;
200}
201
202OSM::TagKey MapCSSDeclaration::tagKey() const
203{
204 return m_tagKey;
205}
206
207void MapCSSDeclaration::setDoubleValue(double val)
208{
209 m_doubleValue = val;
210}
211
212void MapCSSDeclaration::setBoolValue(bool val)
213{
214 m_boolValue = val;
215}
216
217bool MapCSSDeclaration::hasExpression() const
218{
219 return m_evalExpression.isValid();
220}
221
222MapCSSValue MapCSSDeclaration::evaluateExpression(const MapCSSExpressionContext &context) const
223{
224 return m_evalExpression.evaluate(context);
225}
226
227MapCSSProperty MapCSSDeclaration::propertyFromName(const char *name, std::size_t len)
228{
229 const auto it = std::lower_bound(std::begin(property_types), std::end(property_types), name, [len](const auto &lhs, const char *rhs) {
230 const auto lhsLen = std::strlen(lhs.name);
231 const auto cmp = std::strncmp(lhs.name, rhs, std::min(lhsLen, len));
232 return cmp < 0 || (cmp == 0 && lhsLen < len);
233 });
234 if (it == std::end(property_types) || std::strncmp((*it).name, name, std::max(len, std::strlen((*it).name))) != 0) {
235 return MapCSSProperty::Unknown;
236 }
237 return (*it).property;
238}
239
240void MapCSSDeclaration::setPropertyName(const char *name, std::size_t len)
241{
242 const auto it = std::lower_bound(std::begin(property_types), std::end(property_types), name, [len](const auto &lhs, const char *rhs) {
243 const auto lhsLen = std::strlen(lhs.name);
244 const auto cmp = std::strncmp(lhs.name, rhs, std::min(lhsLen, len));
245 return cmp < 0 || (cmp == 0 && lhsLen < len);
246 });
247 if (it == std::end(property_types) || std::strncmp((*it).name, name, std::max(len, std::strlen((*it).name))) != 0) {
248 qCWarning(Log) << "Unknown property declaration:" << QByteArray::fromRawData(name, len);
249 m_property = MapCSSProperty::Unknown;
250 return;
251 }
252 m_property = (*it).property;
253 m_flags = (*it).flags;
254}
255
256void MapCSSDeclaration::setIdentifierValue(const char *val, int len)
257{
258 m_identValue = QByteArray(val, len);
259}
260
261void MapCSSDeclaration::setStringValue(char *str)
262{
263 m_stringValue = QString::fromUtf8(str);
264 free(str);
265}
266
267void MapCSSDeclaration::setColorRgba(uint32_t argb)
268{
269 m_colorValue = QColor::fromRgba(argb);
270 //qDebug() << m_colorValue << argb;
271}
272
273void MapCSSDeclaration::setDashesValue(const QVector<double> &dashes)
274{
275 m_dashValue = dashes;
276}
277
278Qt::PenCapStyle MapCSSDeclaration::capStyle() const
279{
280 for (const auto &c : capstyle_map) {
281 if (std::strcmp(c.name, m_identValue.constData()) == 0) {
282 return c.capStyle;
283 }
284 }
285 qDebug() << "unknown line cap style:" << m_identValue;
286 return Qt::FlatCap;
287}
288
289Qt::PenJoinStyle MapCSSDeclaration::joinStyle() const
290{
291 for (const auto &j : joinstyle_map) {
292 if (std::strcmp(j.name, m_identValue.constData()) == 0) {
293 return j.joinStyle;
294 }
295 }
296 return Qt::RoundJoin;
297}
298
299QFont::Capitalization MapCSSDeclaration::capitalizationStyle() const
300{
301 for (const auto &c : capitalizationstyle_map) {
302 if (std::strcmp(c.name, m_identValue.constData()) == 0) {
303 return c.capitalizationStyle;
304 }
305 }
306 return QFont::MixedCase;
307}
308
309bool MapCSSDeclaration::isBoldStyle() const
310{
311 return m_identValue == "bold";
312}
313
314bool MapCSSDeclaration::isItalicStyle() const
315{
316 return m_identValue == "italic";
317}
318
319bool MapCSSDeclaration::isUnderlineStyle() const
320{
321 return m_identValue == "underline";
322}
323
324MapCSSDeclaration::Position MapCSSDeclaration::textPosition() const
325{
326 for (const auto &p : position_map) {
327 if (std::strcmp(p.name, m_identValue.constData()) == 0) {
328 return p.position;
329 }
330 }
331 return Position::NoPostion;
332}
333
334MapCSSDeclaration::Unit MapCSSDeclaration::unit() const
335{
336 return m_unit;
337}
338
339void MapCSSDeclaration::setUnit(const char *val, int len)
340{
341 for (const auto &u : unit_map) {
342 if (std::strncmp(u.name, val, std::max<std::size_t>(std::strlen(u.name), len)) == 0) {
343 m_unit = u.unit;
344 return;
345 }
346 }
347 qCWarning(Log) << "unknown unit:" << QByteArray(val, len);
348 m_unit = NoUnit;
349}
350
351ClassSelectorKey MapCSSDeclaration::classSelectorKey() const
352{
353 return m_class;
354}
355
356void MapCSSDeclaration::setClassSelectorKey(ClassSelectorKey key)
357{
358 m_class = key;
359}
360
361void MapCSSDeclaration::compile(OSM::DataSet &dataSet)
362{
363 // TODO resolve tag key if m_identValue is one
364 if (m_type == TagDeclaration) {
365 m_tagKey = dataSet.makeTagKey(m_identValue.constData());
366 }
367
368 if (m_evalExpression.isValid()) {
369 m_evalExpression.compile(dataSet);
370 }
371}
372
373static void writeQuotedString(QIODevice *out, QByteArrayView str)
374{
375 out->write("\"");
376 for (const auto c : str) {
377 switch (c) {
378 case '"':
379 out->write("\\\"");
380 break;
381 case '\n':
382 out->write("\\n");
383 break;
384 case '\t':
385 out->write("\\t");
386 break;
387 default:
388 out->write(&c, 1);
389 }
390 }
391 out->write("\"");
392}
393
394void MapCSSDeclaration::write(QIODevice *out) const
395{
396 out->write(" ");
397
398 switch (m_type) {
399 case PropertyDeclaration:
400 for (const auto &p : property_types) {
401 if (p.property == m_property) {
402 out->write(p.name);
403 break;
404 }
405 }
406
407 out->write(": ");
408 if (!std::isnan(m_doubleValue)) {
409 out->write(QByteArray::number(m_doubleValue));
410 } else if (m_colorValue.isValid()) {
411 out->write(m_colorValue.name(QColor::HexArgb).toUtf8());
412 } else if (!m_dashValue.isEmpty()) {
413 for (const auto &d : m_dashValue) {
414 out->write(QByteArray::number(d));
415 out->write(", ");
416 }
417 } else if (!m_stringValue.isNull()) {
418 writeQuotedString(out, m_stringValue.toUtf8());
419 } else if (!m_identValue.isEmpty()) {
420 out->write(m_identValue);
421 } else if (m_evalExpression.isValid()) {
422 out->write("eval(");
423 m_evalExpression.write(out);
424 out->write(")");
425 } else {
426 out->write(m_boolValue ? "true" : "false");
427 }
428
429 for (const auto &u : unit_map) {
430 if (u.unit == m_unit) {
431 out->write(u.name);
432 break;
433 }
434 }
435 break;
436 case TagDeclaration:
437 out->write("set ");
438 out->write(m_identValue);
439 if (!std::isnan(m_doubleValue)) {
440 out->write(" = ");
441 out->write(QByteArray::number(m_doubleValue));
442 } else if (!m_stringValue.isEmpty()) {
443 out->write(" = ");
444 writeQuotedString(out, m_stringValue.toUtf8());
445 } else if (m_evalExpression.isValid()) {
446 out->write(" = eval(");
447 m_evalExpression.write(out);
448 out->write(")");
449 }
450 break;
451 case ClassDeclaration:
452 out->write("set .");
453 out->write(m_class.name());
454 break;
455 }
456
457 out->write(";\n");
458}
A set of nodes, ways and relations.
Definition datatypes.h:346
TagKey makeTagKey(const char *keyName, StringMemory keyMemOpt=StringMemory::Transient)
Create a tag key for the given tag name.
Definition datatypes.cpp:28
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 fromRawData(const char *data, qsizetype size)
QByteArray number(double n, char format, int precision)
QColor fromRgba(QRgb rgba)
Capitalization
qint64 write(const QByteArray &data)
QString fromUtf8(QByteArrayView str)
PenCapStyle
PenJoinStyle
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Dec 6 2024 12:11:15 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.