File indexing completed on 2024-05-12 04:42:08

0001 /*
0002     SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "floorlevelchangemodel.h"
0008 
0009 #include "loader/levelparser_p.h"
0010 #include <KOSMIndoorMap/MapData>
0011 
0012 #include <KLocalizedString>
0013 
0014 #include <QDebug>
0015 
0016 using namespace KOSMIndoorMap;
0017 
0018 FloorLevelChangeModel::FloorLevelChangeModel(QObject *parent)
0019     : QAbstractListModel(parent)
0020 {
0021 }
0022 
0023 FloorLevelChangeModel::~FloorLevelChangeModel() = default;
0024 
0025 int FloorLevelChangeModel::rowCount(const QModelIndex &parent) const
0026 {
0027     if (parent.isValid()) {
0028         return 0;
0029     }
0030     return m_levels.size();
0031 }
0032 
0033 QVariant FloorLevelChangeModel::data(const QModelIndex &index, int role) const
0034 {
0035     if (!index.isValid()) {
0036         return {};
0037     }
0038 
0039     switch (role) {
0040         case Qt::DisplayRole:
0041             return m_levels[index.row()].name();
0042         case FloorLevelRole:
0043             return m_levels[index.row()].numericLevel();
0044         case CurrentFloorRole:
0045             return m_levels[index.row()].numericLevel() == m_currentFloorLevel;
0046     }
0047     return {};
0048 }
0049 
0050 QHash<int, QByteArray> FloorLevelChangeModel::roleNames() const
0051 {
0052     auto n = QAbstractListModel::roleNames();
0053     n.insert(FloorLevelRole, "floorLevel");
0054     n.insert(CurrentFloorRole, "isCurrentFloor");
0055     return n;
0056 }
0057 
0058 int FloorLevelChangeModel::currentFloorLevel() const
0059 {
0060     return m_currentFloorLevel;
0061 }
0062 
0063 void FloorLevelChangeModel::setCurrentFloorLevel(int level)
0064 {
0065     if (m_currentFloorLevel == level) {
0066         return;
0067     }
0068     m_currentFloorLevel = level;
0069     if (!m_levels.empty()) {
0070         Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0));
0071     }
0072     Q_EMIT contentChanged();
0073 }
0074 
0075 FloorLevelModel* FloorLevelChangeModel::floorLevelModel() const
0076 {
0077     return m_floorLevelModel;
0078 }
0079 
0080 void FloorLevelChangeModel::setFloorLevelModel(FloorLevelModel *floorLevelModel)
0081 {
0082     if (m_floorLevelModel == floorLevelModel) {
0083         return;
0084     }
0085 
0086     if (m_floorLevelModel) {
0087         disconnect(m_floorLevelModel, &FloorLevelModel::modelAboutToBeReset, this, nullptr);
0088     }
0089 
0090     m_floorLevelModel = floorLevelModel;
0091     connect(m_floorLevelModel, &FloorLevelModel::modelAboutToBeReset, this, [this]() {
0092         beginResetModel();
0093         m_element = {};
0094         m_levels.clear();
0095         endResetModel();
0096     });
0097     Q_EMIT contentChanged();
0098 }
0099 
0100 OSMElement FloorLevelChangeModel::element() const
0101 {
0102     return OSMElement(m_element);
0103 }
0104 
0105 void FloorLevelChangeModel::setElement(const OSMElement &element)
0106 {
0107     if (m_element == element.element()) {
0108         return;
0109     }
0110 
0111     beginResetModel();
0112     m_element = element.element();
0113     m_levels.clear();
0114 
0115     if (isLevelChangeElement(m_element)) {
0116 
0117         // elevators are sometimes also tagged with building:level tags instead of level/repeat_on, so handle that as well
0118         const auto buildingLevels = m_element.tagValue("building:levels").toUInt();
0119         if (buildingLevels > 0) {
0120             const auto buildingMinLevel = m_element.tagValue("building:min_level", "level").toUInt();
0121             for (auto i = buildingMinLevel; i < buildingLevels; ++i) {
0122                 appendFullFloorLevel(i * 10);
0123             }
0124         }
0125         const auto buildingUndergroundLevel = m_element.tagValue("building:levels:underground").toUInt();
0126         for (auto i = buildingUndergroundLevel; i > 0; --i) {
0127             appendFullFloorLevel(-i * 10);
0128         }
0129 
0130         LevelParser::parse(m_element.tagValue("level", "repeat_on"), m_element, [this](int level, OSM::Element e) {
0131             Q_UNUSED(e);
0132             appendFloorLevel(level);
0133 
0134         });
0135         std::sort(m_levels.begin(), m_levels.end());
0136         m_levels.erase(std::unique(m_levels.begin(), m_levels.end()), m_levels.end());
0137     }
0138 
0139     endResetModel();
0140     Q_EMIT contentChanged();
0141 }
0142 
0143 bool FloorLevelChangeModel::isLevelChangeElement(OSM::Element element) const
0144 {
0145     return !element.tagValue("highway").isEmpty()
0146         || !element.tagValue("elevator").isEmpty()
0147         || !element.tagValue("stairwell").isEmpty()
0148         || element.tagValue("building:part") == "elevator"
0149         || element.tagValue("building") == "elevator"
0150         || element.tagValue("room") == "elevator"
0151         || element.tagValue("levelpart") == "elevator_platform"
0152         || (!element.tagValue("indoor").isEmpty() && element.tagValue("stairs") == "yes")
0153         || element.tagValue("room") == "stairs";
0154 }
0155 
0156 void FloorLevelChangeModel::appendFloorLevel(int level)
0157 {
0158     MapLevel ml(level);
0159     if (ml.isFullLevel()) {
0160         appendFullFloorLevel(level);
0161     } else {
0162         appendFullFloorLevel(ml.fullLevelBelow());
0163         appendFullFloorLevel(ml.fullLevelAbove());
0164     }
0165 }
0166 
0167 void FloorLevelChangeModel::appendFullFloorLevel(int level)
0168 {
0169     if (!m_floorLevelModel) {
0170         m_levels.push_back(MapLevel(level));
0171     } else {
0172         const auto row = m_floorLevelModel->rowForLevel(level);
0173         if (row >= 0) {
0174             const auto idx = m_floorLevelModel->index(row, 0);
0175             m_levels.push_back(m_floorLevelModel->data(idx, FloorLevelModel::MapLevelRole).value<MapLevel>());
0176         }
0177     }
0178 }
0179 
0180 bool FloorLevelChangeModel::hasSingleLevelChange() const
0181 {
0182     if (m_levels.size() != 2) {
0183         return false;
0184     }
0185     return m_levels[0].numericLevel() == m_currentFloorLevel || m_levels[1].numericLevel() == m_currentFloorLevel;
0186 }
0187 
0188 int FloorLevelChangeModel::destinationLevel() const
0189 {
0190     if (m_levels.size() != 2) {
0191         return 0;
0192     }
0193     return m_levels[0].numericLevel() == m_currentFloorLevel ? m_levels[1].numericLevel() : m_levels[0].numericLevel();
0194 }
0195 
0196 QString FloorLevelChangeModel::destinationLevelName() const
0197 {
0198     if (m_levels.size() != 2) {
0199         return {};
0200     }
0201     return m_levels[0].numericLevel() == m_currentFloorLevel ? m_levels[1].name() : m_levels[0].name();
0202 }
0203 
0204 bool FloorLevelChangeModel::hasMultipleLevelChanges() const
0205 {
0206     return m_levels.size() > 1;
0207 }
0208 
0209 QString FloorLevelChangeModel::title() const
0210 {
0211     if (m_element.tagValue("highway") == "elevator"
0212         || !m_element.tagValue("elevator").isEmpty()
0213         || m_element.tagValue("building:part") == "elevator"
0214         || m_element.tagValue("building") == "elevator"
0215         || m_element.tagValue("room") == "elevator"
0216         || m_element.tagValue("levelpart") == "elevator_platform")
0217     {
0218         return i18n("Elevator");
0219     }
0220 
0221     if (!m_element.tagValue("stairwell").isEmpty()
0222      || m_element.tagValue("stairs") == "yes"
0223      || m_element.tagValue("room") == "stairs")
0224     {
0225         return i18n("Staircase");
0226     }
0227 
0228     qWarning() << "Unknown floor level change element type:" << m_element.url();
0229     return {};
0230 }
0231 
0232 #include "moc_floorlevelchangemodel.cpp"