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"