KOSMIndoorMap

equipmentmodel.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 "equipmentmodel.h"
8#include "../loader/levelparser_p.h"
9
10#include <osm/geomath.h>
11
12#include <QDebug>
13
14using namespace KOSMIndoorMap;
15
16float Equipment::distanceTo(const OSM::DataSet &dataSet, float lat, float lon) const
17{
18 if (sourceElements.empty()) {
19 return std::numeric_limits<float>::max();
20 }
21
22 switch (sourceElements[0].type()) {
23 case OSM::Type::Null:
24 return std::numeric_limits<float>::max();
25 case OSM::Type::Node:
26 return OSM::distance(sourceElements[0].center(), OSM::Coordinate(lat, lon));
27 case OSM::Type::Way:
28 case OSM::Type::Relation:
29 {
30 const auto path = sourceElements[0].outerPath(dataSet);
31 return OSM::distance(path, OSM::Coordinate(lat, lon));
32 }
33 }
34 Q_UNREACHABLE();
35 return std::numeric_limits<float>::max();
36}
37
38
39EquipmentModel::EquipmentModel(QObject* parent)
40 : AbstractOverlaySource(nullptr, parent)
41{
42}
43
44EquipmentModel::~EquipmentModel() = default;
45
46MapData EquipmentModel::mapData() const
47{
48 return m_data;
49}
50
51void EquipmentModel::setMapData(const MapData &data)
52{
53 if (m_data == data) {
54 return;
55 }
56
57 m_equipment.clear();
58 m_data = data;
59
60 if (!m_data.isEmpty()) {
61 m_tagKeys.building = m_data.dataSet().tagKey("building");
62 m_tagKeys.buildling_part = m_data.dataSet().tagKey("building:part");
63 m_tagKeys.conveying = m_data.dataSet().tagKey("conveying");
64 m_tagKeys.elevator = m_data.dataSet().tagKey("elevator");
65 m_tagKeys.highway = m_data.dataSet().tagKey("highway");
66 m_tagKeys.indoor = m_data.dataSet().tagKey("indoor");
67 m_tagKeys.level = m_data.dataSet().tagKey("level");
68 m_tagKeys.room = m_data.dataSet().tagKey("room");
69 m_tagKeys.stairwell = m_data.dataSet().tagKey("stairwell");
70
71 m_tagKeys.mxoid = m_data.dataSet().makeTagKey("mx:oid");
72 m_tagKeys.realtimeStatus = m_data.dataSet().makeTagKey("mx:realtime_status");
73 findEquipment();
74 }
75
76 for (const auto &eq : m_equipment) {
77 qDebug() << " E" << eq.sourceElements.size() << eq.levels << eq.type;
78 }
79
80 Q_EMIT update();
81}
82
83void EquipmentModel::forEach(int floorLevel, const std::function<void (OSM::Element, int)> &func) const
84{
85 for (const auto &eq : m_equipment) {
86 if (!eq.syntheticElement || std::find_if(eq.levels.begin(), eq.levels.end(), [floorLevel](int lvl) { return std::abs(floorLevel - lvl) < 10; }) == eq.levels.end()) {
87 continue;
88 }
89 func(eq.syntheticElement, floorLevel);
90 }
91}
92
93void EquipmentModel::hiddenElements(std::vector<OSM::Element> &elems) const
94{
95 for (const auto &eq : m_equipment) {
96 if (!eq.syntheticElement) {
97 continue;
98 }
99 elems.insert(elems.end(), eq.sourceElements.begin(), eq.sourceElements.end());
100 }
101}
102
103static bool isConveying(const QByteArray &b)
104{
105 return b == "yes" || b == "forward" || b == "backward" || b == "reversible";
106}
107
108void EquipmentModel::findEquipment()
109{
110 OSM::for_each(m_data.dataSet(), [this](OSM::Element e) {
111 if (!e.hasTags()) {
112 return;
113 }
114
115 // escalators
116 const auto highway = e.tagValue(m_tagKeys.highway);
117 const auto conveying = e.tagValue(m_tagKeys.conveying);
118 if ((highway == "footway" || highway == "steps") && isConveying(conveying)) {
119 Equipment escalator;
120 escalator.type = Equipment::Escalator;
121 escalator.sourceElements.push_back(e);
122 LevelParser::parse(e.tagValue(m_tagKeys.level), e, [&escalator](int level, OSM::Element) {
123 escalator.levels.push_back(level);
124 });
125 m_equipment.push_back(std::move(escalator));
126 }
127
128 // elevators
129 const auto building = e.tagValue(m_tagKeys.building);
130 const auto buildling_part = e.tagValue(m_tagKeys.buildling_part);
131 const auto elevator = e.tagValue(m_tagKeys.elevator);
132 const auto indoor = e.tagValue(m_tagKeys.indoor);
133 const auto room = e.tagValue(m_tagKeys.room);
134 const auto stairwell = e.tagValue(m_tagKeys.stairwell);
135
136 if (building == "elevator"
137 || buildling_part == "elevator" || (buildling_part == "yes" && elevator == "yes")
138 || highway == "elevator"
139 || room == "elevator"
140 || stairwell == "elevator"
141 || (indoor == "room" && elevator == "yes"))
142 {
143 Equipment elevator;
144 elevator.type = Equipment::Elevator;
145 elevator.sourceElements.push_back(e);
146 LevelParser::parse(e.tagValue(m_tagKeys.level), e, [&elevator](int level, OSM::Element) {
147 elevator.levels.push_back(level);
148 });
149 if (elevator.levels.empty()) {
150 elevator.levels.push_back(0);
151 }
152
153 // try to find duplicate elements on other levels
154 for (auto &e : m_equipment) {
155 if (e.type != Equipment::Elevator) {
156 continue;
157 }
158 if (OSM::intersects(e.sourceElements[0].boundingBox(), elevator.sourceElements[0].boundingBox())) {
159 // TODO check for non-intersecting but present level sets?
160 qDebug() << "merging elevator elements:" << elevator.sourceElements[0].url() << e.sourceElements[0].url();
161 e.sourceElements.push_back(elevator.sourceElements[0]);
162 e.levels.insert(e.levels.end(), elevator.levels.begin(), elevator.levels.end());
163 return;
164 }
165 }
166
167 m_equipment.push_back(std::move(elevator));
168 }
169
170 }, OSM::IncludeNodes | OSM::IncludeWays);
171
172 // finalize elevator merging
173 for (auto &elevator : m_equipment) {
174 if (elevator.type != Equipment::Elevator || elevator.sourceElements.size() < 2) {
175 continue;
176 }
177
178 std::sort(elevator.levels.begin(), elevator.levels.end());
179 elevator.levels.erase(std::unique(elevator.levels.begin(), elevator.levels.end()), elevator.levels.end());
180 if (elevator.levels.size() < 2) {
181 continue;
182 }
183
184 std::sort(elevator.sourceElements.begin(), elevator.sourceElements.end(), [](auto lhs, auto rhs) { return lhs.type() > rhs.type(); });
185 createSyntheticElement(elevator);
186 }
187}
188
189void EquipmentModel::createSyntheticElement(Equipment& eq) const
190{
191 if (eq.syntheticElement) {
192 return;
193 }
194
195 eq.syntheticElement = OSM::copy_element(eq.sourceElements[0]);
196 eq.syntheticElement.setTagValue(m_tagKeys.mxoid, QByteArray::number((qlonglong)eq.syntheticElement.element().id()));
197 eq.syntheticElement.setId(m_data.dataSet().nextInternalId());
198
199 // clone tags
200 for (auto it = std::next(eq.sourceElements.begin()); it != eq.sourceElements.end(); ++it) {
201 for (auto tagIt = (*it).tagsBegin(); tagIt != (*it).tagsEnd(); ++tagIt) {
202 if ((*tagIt).key == m_tagKeys.level) {
203 continue;
204 }
205
206 if (eq.syntheticElement.element().hasTag((*tagIt).key)) {
207 // ### for testing only
208 if (eq.syntheticElement.element().tagValue((*tagIt).key) != (*tagIt).value) {
209 qDebug() << " tag value conflict:" << (*tagIt).key.name() << (*tagIt).value << eq.sourceElements[0].url() << eq.syntheticElement.element().tagValue((*tagIt).key);
210 }
211 continue;
212 }
213 eq.syntheticElement.setTagValue((*tagIt).key, QByteArray((*tagIt).value));
214 }
215 }
216
217 if (eq.levels.size() > 1) {
218 auto levelValue = QByteArray::number(eq.levels.at(0) / 10.0);
219 for (auto it = std::next(eq.levels.begin()); it != eq.levels.end(); ++it) {
220 levelValue += ';' + QByteArray::number((*it) / 10.0);
221 }
222 eq.syntheticElement.setTagValue(m_tagKeys.level, std::move(levelValue));
223 }
224}
225
226#include "moc_equipmentmodel.cpp"
A source for overlay elements, drawn on top of the static map data.
void update()
Trigger map re-rendering when the source changes.
void hiddenElements(std::vector< OSM::Element > &elems) const override
Adds hidden elements to.
void forEach(int floorLevel, const std::function< void(OSM::Element, int)> &func) const override
Iteration interface with floor level filtering.
Elevator or escalator element.
Raw OSM map data, separated by levels.
Definition mapdata.h:60
Coordinate, stored as 1e7 * degree to avoid floating point precision issues, and offset to unsigned v...
Definition datatypes.h:37
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
Id nextInternalId() const
Create a unique id for internal use (ie.
TagKey makeTagKey(const char *keyName, StringMemory keyMemOpt=StringMemory::Transient)
Create a tag key for the given tag name.
Definition datatypes.cpp:17
A reference to any of OSM::Node/OSM::Way/OSM::Relation.
Definition element.h:24
bool hasTag(TagKey key) const
Returns true if this element has a tag with key key.
Definition element.cpp:114
QString path(const QString &relativePath)
OSM-based multi-floor indoor maps for buildings.
KOSM_EXPORT double distance(double lat1, double lon1, double lat2, double lon2)
Distance between two coordinates.
Definition geomath.cpp:16
KOSM_EXPORT UniqueElement copy_element(Element e)
Creates a copy of element.
Definition element.cpp:300
QByteArray number(double n, char format, int precision)
Q_EMITQ_EMIT
QTextStream & center(QTextStream &stream)
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.