File indexing completed on 2024-11-17 04:17:22
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "equipmentmodel.h" 0008 #include "../loader/levelparser_p.h" 0009 0010 #include <osm/geomath.h> 0011 0012 #include <QDebug> 0013 0014 using namespace KOSMIndoorMap; 0015 0016 float Equipment::distanceTo(const OSM::DataSet &dataSet, float lat, float lon) const 0017 { 0018 if (sourceElements.empty()) { 0019 return std::numeric_limits<float>::max(); 0020 } 0021 0022 switch (sourceElements[0].type()) { 0023 case OSM::Type::Null: 0024 return std::numeric_limits<float>::max(); 0025 case OSM::Type::Node: 0026 return OSM::distance(sourceElements[0].center(), OSM::Coordinate(lat, lon)); 0027 case OSM::Type::Way: 0028 case OSM::Type::Relation: 0029 { 0030 const auto path = sourceElements[0].outerPath(dataSet); 0031 return OSM::distance(path, OSM::Coordinate(lat, lon)); 0032 } 0033 } 0034 Q_UNREACHABLE(); 0035 return std::numeric_limits<float>::max(); 0036 } 0037 0038 0039 EquipmentModel::EquipmentModel(QObject* parent) 0040 : AbstractOverlaySource(nullptr, parent) 0041 { 0042 } 0043 0044 EquipmentModel::~EquipmentModel() = default; 0045 0046 MapData EquipmentModel::mapData() const 0047 { 0048 return m_data; 0049 } 0050 0051 void EquipmentModel::setMapData(const MapData &data) 0052 { 0053 if (m_data == data) { 0054 return; 0055 } 0056 0057 m_equipment.clear(); 0058 m_data = data; 0059 0060 if (!m_data.isEmpty()) { 0061 m_tagKeys.building = m_data.dataSet().tagKey("building"); 0062 m_tagKeys.buildling_part = m_data.dataSet().tagKey("building:part"); 0063 m_tagKeys.conveying = m_data.dataSet().tagKey("conveying"); 0064 m_tagKeys.elevator = m_data.dataSet().tagKey("elevator"); 0065 m_tagKeys.highway = m_data.dataSet().tagKey("highway"); 0066 m_tagKeys.indoor = m_data.dataSet().tagKey("indoor"); 0067 m_tagKeys.level = m_data.dataSet().tagKey("level"); 0068 m_tagKeys.room = m_data.dataSet().tagKey("room"); 0069 m_tagKeys.stairwell = m_data.dataSet().tagKey("stairwell"); 0070 0071 m_tagKeys.mxoid = m_data.dataSet().makeTagKey("mx:oid"); 0072 m_tagKeys.realtimeStatus = m_data.dataSet().makeTagKey("mx:realtime_status"); 0073 findEquipment(); 0074 } 0075 0076 for (const auto &eq : m_equipment) { 0077 qDebug() << " E" << eq.sourceElements.size() << eq.levels << eq.type; 0078 } 0079 0080 Q_EMIT update(); 0081 } 0082 0083 void EquipmentModel::forEach(int floorLevel, const std::function<void (OSM::Element, int)> &func) const 0084 { 0085 for (const auto &eq : m_equipment) { 0086 if (!eq.syntheticElement || std::find_if(eq.levels.begin(), eq.levels.end(), [floorLevel](int lvl) { return std::abs(floorLevel - lvl) < 10; }) == eq.levels.end()) { 0087 continue; 0088 } 0089 func(eq.syntheticElement, floorLevel); 0090 } 0091 } 0092 0093 void EquipmentModel::hiddenElements(std::vector<OSM::Element> &elems) const 0094 { 0095 for (const auto &eq : m_equipment) { 0096 if (!eq.syntheticElement) { 0097 continue; 0098 } 0099 elems.insert(elems.end(), eq.sourceElements.begin(), eq.sourceElements.end()); 0100 } 0101 } 0102 0103 static bool isConveying(const QByteArray &b) 0104 { 0105 return b == "yes" || b == "forward" || b == "backward" || b == "reversible"; 0106 } 0107 0108 void EquipmentModel::findEquipment() 0109 { 0110 OSM::for_each(m_data.dataSet(), [this](OSM::Element e) { 0111 if (!e.hasTags()) { 0112 return; 0113 } 0114 0115 // escalators 0116 const auto highway = e.tagValue(m_tagKeys.highway); 0117 const auto conveying = e.tagValue(m_tagKeys.conveying); 0118 if ((highway == "footway" || highway == "steps") && isConveying(conveying)) { 0119 Equipment escalator; 0120 escalator.type = Equipment::Escalator; 0121 escalator.sourceElements.push_back(e); 0122 LevelParser::parse(e.tagValue(m_tagKeys.level), e, [&escalator](int level, OSM::Element) { 0123 escalator.levels.push_back(level); 0124 }); 0125 m_equipment.push_back(std::move(escalator)); 0126 } 0127 0128 // elevators 0129 const auto building = e.tagValue(m_tagKeys.building); 0130 const auto buildling_part = e.tagValue(m_tagKeys.buildling_part); 0131 const auto elevator = e.tagValue(m_tagKeys.elevator); 0132 const auto indoor = e.tagValue(m_tagKeys.indoor); 0133 const auto room = e.tagValue(m_tagKeys.room); 0134 const auto stairwell = e.tagValue(m_tagKeys.stairwell); 0135 0136 if (building == "elevator" 0137 || buildling_part == "elevator" || (buildling_part == "yes" && elevator == "yes") 0138 || highway == "elevator" 0139 || room == "elevator" 0140 || stairwell == "elevator" 0141 || (indoor == "room" && elevator == "yes")) 0142 { 0143 Equipment elevator; 0144 elevator.type = Equipment::Elevator; 0145 elevator.sourceElements.push_back(e); 0146 LevelParser::parse(e.tagValue(m_tagKeys.level), e, [&elevator](int level, OSM::Element) { 0147 elevator.levels.push_back(level); 0148 }); 0149 if (elevator.levels.empty()) { 0150 elevator.levels.push_back(0); 0151 } 0152 0153 // try to find duplicate elements on other levels 0154 for (auto &e : m_equipment) { 0155 if (e.type != Equipment::Elevator) { 0156 continue; 0157 } 0158 if (OSM::intersects(e.sourceElements[0].boundingBox(), elevator.sourceElements[0].boundingBox())) { 0159 // TODO check for non-intersecting but present level sets? 0160 qDebug() << "merging elevator elements:" << elevator.sourceElements[0].url() << e.sourceElements[0].url(); 0161 e.sourceElements.push_back(elevator.sourceElements[0]); 0162 e.levels.insert(e.levels.end(), elevator.levels.begin(), elevator.levels.end()); 0163 return; 0164 } 0165 } 0166 0167 m_equipment.push_back(std::move(elevator)); 0168 } 0169 0170 }, OSM::IncludeNodes | OSM::IncludeWays); 0171 0172 // finalize elevator merging 0173 for (auto &elevator : m_equipment) { 0174 if (elevator.type != Equipment::Elevator || elevator.sourceElements.size() < 2) { 0175 continue; 0176 } 0177 0178 std::sort(elevator.levels.begin(), elevator.levels.end()); 0179 elevator.levels.erase(std::unique(elevator.levels.begin(), elevator.levels.end()), elevator.levels.end()); 0180 if (elevator.levels.size() < 2) { 0181 continue; 0182 } 0183 0184 std::sort(elevator.sourceElements.begin(), elevator.sourceElements.end(), [](auto lhs, auto rhs) { return lhs.type() > rhs.type(); }); 0185 createSyntheticElement(elevator); 0186 } 0187 } 0188 0189 void EquipmentModel::createSyntheticElement(Equipment& eq) const 0190 { 0191 if (eq.syntheticElement) { 0192 return; 0193 } 0194 0195 eq.syntheticElement = OSM::copy_element(eq.sourceElements[0]); 0196 eq.syntheticElement.setTagValue(m_tagKeys.mxoid, QByteArray::number((qlonglong)eq.syntheticElement.element().id())); 0197 eq.syntheticElement.setId(m_data.dataSet().nextInternalId()); 0198 0199 // clone tags 0200 for (auto it = std::next(eq.sourceElements.begin()); it != eq.sourceElements.end(); ++it) { 0201 for (auto tagIt = (*it).tagsBegin(); tagIt != (*it).tagsEnd(); ++tagIt) { 0202 if ((*tagIt).key == m_tagKeys.level) { 0203 continue; 0204 } 0205 0206 if (eq.syntheticElement.element().hasTag((*tagIt).key)) { 0207 // ### for testing only 0208 if (eq.syntheticElement.element().tagValue((*tagIt).key) != (*tagIt).value) { 0209 qDebug() << " tag value conflict:" << (*tagIt).key.name() << (*tagIt).value << eq.sourceElements[0].url() << eq.syntheticElement.element().tagValue((*tagIt).key); 0210 } 0211 continue; 0212 } 0213 eq.syntheticElement.setTagValue((*tagIt).key, QByteArray((*tagIt).value)); 0214 } 0215 } 0216 0217 if (eq.levels.size() > 1) { 0218 auto levelValue = QByteArray::number(eq.levels.at(0) / 10.0); 0219 for (auto it = std::next(eq.levels.begin()); it != eq.levels.end(); ++it) { 0220 levelValue += ';' + QByteArray::number((*it) / 10.0); 0221 } 0222 eq.syntheticElement.setTagValue(m_tagKeys.level, std::move(levelValue)); 0223 } 0224 } 0225 0226 #include "moc_equipmentmodel.cpp"