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"