KOSMIndoorMap

mapcsscondition.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 "mapcsscondition_p.h"
8#include "mapcssresult.h"
9#include "mapcssstate_p.h"
10
11#include <QDebug>
12#include <QIODevice>
13
14using namespace KOSMIndoorMap;
15
16MapCSSCondition::MapCSSCondition() = default;
17MapCSSCondition::MapCSSCondition(MapCSSCondition &&) = default;
18MapCSSCondition::~MapCSSCondition() = default;
19
20static double toNumber(const QByteArray &val)
21{
22 bool res = false;
23 const auto n = val.toDouble(&res);
24 return res ? n : NAN;
25}
26
27void MapCSSCondition::compile(const OSM::DataSet &dataSet)
28{
29 if (m_key == "mx:closed") {
30 m_tagKey = dataSet.tagKey("opening_hours");
31 m_op = (m_op == KeyNotSet ? IsNotClosed : IsClosed);
32 } else {
33 m_tagKey = dataSet.tagKey(m_key.constData());
34 }
35
36 switch(m_op) {
37 case KeySet:
38 case KeyNotSet:
39 break;
40 case Equal:
41 case NotEqual:
42 if (m_value.isEmpty() && std::isnan(m_numericValue)) {
43 qWarning() << "Empty comparison, use key (not) set operation instead!";
44 }
45 break;
46 case LessThan:
47 case GreaterThan:
48 case LessOrEqual:
49 case GreaterOrEqual:
50 if (std::isnan(m_numericValue)) {
51 qWarning() << "Numeric comparison without numeric value set!";
52 }
53 break;
54 case IsClosed:
55 case IsNotClosed:
56 break;
57 }
58}
59
60bool MapCSSCondition::matches(const MapCSSState &state, const MapCSSResultLayer &result) const
61{
62 if (m_tagKey.isNull()) {
63 // if we couldn't compile the tag, it doesn't exist and thus can never be set
64 return m_op == KeyNotSet || m_op == NotEqual;
65 }
66
67 // this method is such a hot path that even the ref/deref in QByteArray for OSM::Element::tagValue matters
68 // so we do tag lookup manually here
69 const auto tagValue = result.resolvedTagValue(m_tagKey, state);
70 const auto tagIsSet = tagValue.has_value();
71 switch (m_op) {
72 case KeySet:
73 return tagIsSet;
74 case KeyNotSet:
75 return !tagIsSet;
76 case Equal:
77 if (std::isnan(m_numericValue)) {
78 return !tagIsSet ? false : *tagValue == m_value;
79 }
80 return !tagIsSet ? false : toNumber(*tagValue) == m_numericValue;
81 case NotEqual:
82 if (std::isnan(m_numericValue)) {
83 return !tagIsSet ? true : *tagValue != m_value;
84 }
85 return !tagIsSet ? true : toNumber(*tagValue) != m_numericValue;
86 case LessThan: return !tagIsSet ? false : toNumber(*tagValue) < m_numericValue;
87 case GreaterThan: return !tagIsSet ? false : toNumber(*tagValue) > m_numericValue;
88 case LessOrEqual: return !tagIsSet ? false : toNumber(*tagValue) <= m_numericValue;
89 case GreaterOrEqual: return !tagIsSet ? false : toNumber(*tagValue) >= m_numericValue;
90 case IsClosed:
91 case IsNotClosed:
92 {
93 if (!tagIsSet || (*tagValue).isEmpty() || !state.openingHours) {
94 return m_op == IsNotClosed;
95 }
96 const auto closed = state.openingHours->isEntirelyClosedInRange(state.element, *tagValue);
97 return m_op == IsClosed ? closed : !closed;
98 }
99 }
100 return false;
101}
102
103bool MapCSSCondition::matchesCanvas(const MapCSSState &state) const
104{
105 if (m_key != "level") {
106 return false;
107 }
108
109 switch (m_op) {
110 case KeySet:
111 case KeyNotSet:
112 case IsClosed:
113 case IsNotClosed:
114 return false;
115 case Equal: return (state.floorLevel/10) == m_numericValue;
116 case NotEqual: return (state.floorLevel/10) != m_numericValue;
117 case LessThan: return (state.floorLevel/10) < m_numericValue;
118 case GreaterThan: return (state.floorLevel/10) > m_numericValue;
119 case LessOrEqual: return (state.floorLevel/10) <= m_numericValue;
120 case GreaterOrEqual: return (state.floorLevel/10) >= m_numericValue;
121 }
122
123 return false;
124}
125
126void MapCSSCondition::setKey(const char *key, int len)
127{
128 m_key = QByteArray(key, len);
129}
130
131void MapCSSCondition::setOperation(MapCSSCondition::Operator op)
132{
133 m_op = op;
134}
135
136void MapCSSCondition::setValue(const char *value, int len)
137{
138 m_value = QByteArray(value, len);
139}
140
141void MapCSSCondition::setValue(double val)
142{
143 m_numericValue = val;
144}
145
146void MapCSSCondition::write(QIODevice *out) const
147{
148 out->write("[");
149 if (m_op == KeyNotSet) { out->write("!"); }
150 out->write(m_key);
151
152 switch (m_op) {
153 case KeySet:
154 case KeyNotSet:
155 case IsClosed:
156 case IsNotClosed:
157 out->write("]"); return;
158 case Equal: out->write("="); break;
159 case NotEqual: out->write("!="); break;
160 case LessThan: out->write("<"); break;
161 case GreaterThan: out->write(">"); break;
162 case LessOrEqual: out->write("<="); break;
163 case GreaterOrEqual: out->write(">="); break;
164 }
165
166 if (m_numericValue != NAN && m_value.isEmpty()) {
167 out->write(QByteArray::number(m_numericValue));
168 } else {
169 // TODO quote if m_value contains non-identifier chars
170 out->write(m_value);
171 }
172
173 out->write("]");
174}
175
176
177void MapCSSConditionHolder::addCondition(MapCSSCondition *condition)
178{
179 conditions.push_back(std::unique_ptr<MapCSSCondition>(condition));
180}
Result of MapCSS stylesheet evaluation for a single layer selector.
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.
A set of nodes, ways and relations.
Definition datatypes.h:343
TagKey tagKey(const char *keyName) const
Look up a tag key for the given tag name, if it exists.
Definition datatypes.cpp:38
OSM-based multi-floor indoor maps for buildings.
QByteArray tagValue(const Elem &elem, TagKey key)
Returns the tag value for key of elem.
Definition datatypes.h:417
QByteArray number(double n, char format, int precision)
double toDouble(bool *ok) const const
qint64 write(const QByteArray &data)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 4 2024 12:02:24 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.