KOSMIndoorMap

equipmentmodel.cpp
1 /*
2  SPDX-FileCopyrightText: 2020 Volker Krause <[email protected]>
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 
14 using namespace KOSMIndoorMap;
15 
16 float 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 
39 EquipmentModel::EquipmentModel(QObject* parent)
40  : AbstractOverlaySource(nullptr, parent)
41 {
42 }
43 
44 EquipmentModel::~EquipmentModel() = default;
45 
46 MapData EquipmentModel::mapData() const
47 {
48  return m_data;
49 }
50 
51 void 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 
83 void 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(eq.levels.begin(), eq.levels.end(), floorLevel) == eq.levels.end()) {
87  continue;
88  }
89  func(eq.syntheticElement, floorLevel);
90  }
91 }
92 
93 void 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 
103 static bool isConveying(const QByteArray &b)
104 {
105  return b == "yes" || b == "forward" || b == "backward" || b == "reversible";
106 }
107 
108 void 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 
189 void 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, (*tagIt).value);
214  }
215  }
216 
217  if (eq.levels.size() > 1) {
218  auto levelValue = QByteArray::number(eq.levels.at(0) / 10);
219  for (auto it = std::next(eq.levels.begin()); it != eq.levels.end(); ++it) {
220  levelValue += ';' + QByteArray::number((*it) / 10);
221  }
222  eq.syntheticElement.setTagValue(m_tagKeys.level, levelValue);
223  }
224 }
OSM-based multi-floor indoor maps for buildings.
Coordinate, stored as 1e7 * degree to avoid floating point precision issues, and offset to unsigned v...
Definition: datatypes.h:37
A reference to any of OSM::Node/OSMWay/OSMRelation.
Definition: element.h:22
Elevator or escalator element.
Raw OSM map data, separated by levels.
Definition: mapdata.h:59
QByteArray number(int n, int base)
QTextStream & center(QTextStream &stream)
TagKey tagKey(const char *keyName) const
Look up a tag key for the given tag name, if it exists.
Definition: datatypes.cpp:27
A source for overlay elements, drawn on top of the static map data.
Definition: overlaysource.h:26
KOSM_EXPORT UniqueElement copy_element(Element e)
Creates a copy of element.
Definition: element.cpp:300
A set of nodes, ways and relations.
Definition: datatypes.h:283
void update(Part *part, const QByteArray &data, qint64 dataSize)
KOSM_EXPORT double distance(double lat1, double lon1, double lat2, double lon2)
Distance between two coordinates.
Definition: geomath.cpp:17
bool hasTag(TagKey key) const
Returns true if this element has a tag with key key.
Definition: element.cpp:114
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Sep 27 2021 23:05:52 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.