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 "mapcssstate_p.h"
9
10#include <QDebug>
11#include <QIODevice>
12
13using namespace KOSMIndoorMap;
14
15MapCSSCondition::MapCSSCondition() = default;
16MapCSSCondition::MapCSSCondition(MapCSSCondition &&) = default;
17MapCSSCondition::~MapCSSCondition() = default;
18
19static double toNumber(const QByteArray &val)
20{
21 bool res = false;
22 const auto n = val.toDouble(&res);
23 return res ? n : NAN;
24}
25
26void MapCSSCondition::compile(const OSM::DataSet &dataSet)
27{
28 if (m_key == "mx:closed") {
29 m_tagKey = dataSet.tagKey("opening_hours");
30 m_op = (m_op == KeyNotSet ? IsNotClosed : IsClosed);
31 } else {
32 m_tagKey = dataSet.tagKey(m_key.constData());
33 }
34
35 switch(m_op) {
36 case KeySet:
37 case KeyNotSet:
38 break;
39 case Equal:
40 case NotEqual:
41 if (m_value.isEmpty() && std::isnan(m_numericValue)) {
42 qWarning() << "Empty comparison, use key (not) set operation instead!";
43 }
44 break;
45 case LessThan:
46 case GreaterThan:
47 case LessOrEqual:
48 case GreaterOrEqual:
49 if (std::isnan(m_numericValue)) {
50 qWarning() << "Numeric comparison without numeric value set!";
51 }
52 break;
53 case IsClosed:
54 case IsNotClosed:
55 break;
56 }
57}
58
59bool MapCSSCondition::matches(const MapCSSState &state) const
60{
61 if (m_tagKey.isNull()) {
62 // if we couldn't compile the tag, it doesn't exist and thus can never be set
63 return m_op == KeyNotSet || m_op == NotEqual;
64 }
65
66 // this method is such a hot path that even the ref/deref in QByteArray for OSM::Element::tagValue matters
67 // so we do tag lookup manually here
68 const auto tagEnd = state.element.tagsEnd();
69 const auto tagIt = std::lower_bound(state.element.tagsBegin(), tagEnd, m_tagKey);
70 const auto tagIsSet = (tagIt != tagEnd && (*tagIt).key == m_tagKey);
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 : (*tagIt).value == m_value;
79 }
80 return !tagIsSet ? false : toNumber((*tagIt).value) == m_numericValue;
81 case NotEqual:
82 if (std::isnan(m_numericValue)) {
83 return !tagIsSet ? true : (*tagIt).value != m_value;
84 }
85 return !tagIsSet ? true : toNumber((*tagIt).value) != m_numericValue;
86 case LessThan: return !tagIsSet ? false : toNumber((*tagIt).value) < m_numericValue;
87 case GreaterThan: return !tagIsSet ? false : toNumber((*tagIt).value) > m_numericValue;
88 case LessOrEqual: return !tagIsSet ? false : toNumber((*tagIt).value) <= m_numericValue;
89 case GreaterOrEqual: return !tagIsSet ? false : toNumber((*tagIt).value) >= m_numericValue;
90 case IsClosed:
91 case IsNotClosed:
92 {
93 if (!tagIsSet || (*tagIt).value.isEmpty() || !state.openingHours) {
94 return m_op == IsNotClosed;
95 }
96 const auto closed = state.openingHours->isClosed(state.element, (*tagIt).value);
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}
A set of nodes, ways and relations.
Definition datatypes.h:340
TagKey tagKey(const char *keyName) const
Look up a tag key for the given tag name, if it exists.
Definition datatypes.cpp:27
OSM-based multi-floor indoor maps for buildings.
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 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.