KOSMIndoorMap

floorlevelchangemodel.cpp
1/*
2 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "floorlevelchangemodel.h"
8
9#include "loader/levelparser_p.h"
10#include <KOSMIndoorMap/MapData>
11
12#include <KLocalizedString>
13
14#include <QDebug>
15
16using namespace KOSMIndoorMap;
17
18FloorLevelChangeModel::FloorLevelChangeModel(QObject *parent)
19 : QAbstractListModel(parent)
20{
21}
22
23FloorLevelChangeModel::~FloorLevelChangeModel() = default;
24
25int FloorLevelChangeModel::rowCount(const QModelIndex &parent) const
26{
27 if (parent.isValid()) {
28 return 0;
29 }
30 return m_levels.size();
31}
32
33QVariant FloorLevelChangeModel::data(const QModelIndex &index, int role) const
34{
35 if (!index.isValid()) {
36 return {};
37 }
38
39 switch (role) {
40 case Qt::DisplayRole:
41 return m_levels[index.row()].name();
42 case FloorLevelRole:
43 return m_levels[index.row()].numericLevel();
44 case CurrentFloorRole:
45 return m_levels[index.row()].numericLevel() == m_currentFloorLevel;
46 }
47 return {};
48}
49
50QHash<int, QByteArray> FloorLevelChangeModel::roleNames() const
51{
53 n.insert(NameRole, "name");
54 n.insert(FloorLevelRole, "floorLevel");
55 n.insert(CurrentFloorRole, "isCurrentFloor");
56 return n;
57}
58
60{
61 return m_currentFloorLevel;
62}
63
65{
66 const auto it = std::find_if(m_levels.begin(), m_levels.end(), [this](const auto &level) { return level.numericLevel() == m_currentFloorLevel; });
67 return it != m_levels.end() ? (int)std::distance(m_levels.begin(), it) : -1;
68}
69
70void FloorLevelChangeModel::setCurrentFloorLevel(int level)
71{
72 if (m_currentFloorLevel == level) {
73 return;
74 }
75 m_currentFloorLevel = level;
76 if (!m_levels.empty()) {
77 Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0));
78 }
79 Q_EMIT contentChanged();
80}
81
82FloorLevelModel* FloorLevelChangeModel::floorLevelModel() const
83{
84 return m_floorLevelModel;
85}
86
87void FloorLevelChangeModel::setFloorLevelModel(FloorLevelModel *floorLevelModel)
88{
89 if (m_floorLevelModel == floorLevelModel) {
90 return;
91 }
92
93 if (m_floorLevelModel) {
94 disconnect(m_floorLevelModel, &FloorLevelModel::modelAboutToBeReset, this, nullptr);
95 }
96
97 m_floorLevelModel = floorLevelModel;
98 connect(m_floorLevelModel, &FloorLevelModel::modelAboutToBeReset, this, [this]() {
100 m_element = {};
101 m_levels.clear();
103 });
104 Q_EMIT contentChanged();
105}
106
107OSMElement FloorLevelChangeModel::element() const
108{
109 return OSMElement(m_element);
110}
111
112void FloorLevelChangeModel::setElement(const OSMElement &element)
113{
114 if (m_element == element.element()) {
115 return;
116 }
117
119 m_element = element.element();
120 m_levels.clear();
121
122 if (isLevelChangeElement(m_element)) {
123
124 // elevators are sometimes also tagged with building:level tags instead of level/repeat_on, so handle that as well
125 const auto buildingLevels = m_element.tagValue("building:levels").toUInt();
126 if (buildingLevels > 0) {
127 const auto buildingMinLevel = m_element.tagValue("building:min_level", "level").toUInt();
128 for (auto i = buildingMinLevel; i < buildingLevels; ++i) {
129 appendFullFloorLevel(i * 10);
130 }
131 }
132 const auto buildingUndergroundLevel = m_element.tagValue("building:levels:underground").toUInt();
133 for (auto i = buildingUndergroundLevel; i > 0; --i) {
134 appendFullFloorLevel(-i * 10);
135 }
136
137 LevelParser::parse(m_element.tagValue("level", "repeat_on"), m_element, [this](int level, OSM::Element e) {
138 Q_UNUSED(e);
139 appendFloorLevel(level);
140
141 });
142 std::sort(m_levels.begin(), m_levels.end());
143 m_levels.erase(std::unique(m_levels.begin(), m_levels.end()), m_levels.end());
144 }
145
147 Q_EMIT contentChanged();
148}
149
150bool FloorLevelChangeModel::isLevelChangeElement(OSM::Element element) const
151{
152 return !element.tagValue("highway").isEmpty()
153 || !element.tagValue("elevator").isEmpty()
154 || !element.tagValue("stairwell").isEmpty()
155 || element.tagValue("building:part") == "elevator"
156 || element.tagValue("building") == "elevator"
157 || element.tagValue("room") == "elevator"
158 || element.tagValue("levelpart") == "elevator_platform"
159 || (!element.tagValue("indoor").isEmpty() && element.tagValue("stairs") == "yes")
160 || element.tagValue("room") == "stairs";
161}
162
163void FloorLevelChangeModel::appendFloorLevel(int level)
164{
165 MapLevel ml(level);
166 if (ml.isFullLevel()) {
167 appendFullFloorLevel(level);
168 } else {
169 appendFullFloorLevel(ml.fullLevelBelow());
170 appendFullFloorLevel(ml.fullLevelAbove());
171 }
172}
173
174void FloorLevelChangeModel::appendFullFloorLevel(int level)
175{
176 if (!m_floorLevelModel) {
177 m_levels.push_back(MapLevel(level));
178 } else {
179 const auto row = m_floorLevelModel->rowForLevel(level);
180 if (row >= 0) {
181 const auto idx = m_floorLevelModel->index(row, 0);
182 m_levels.push_back(m_floorLevelModel->data(idx, FloorLevelModel::MapLevelRole).value<MapLevel>());
183 }
184 }
185}
186
188{
189 if (m_levels.size() != 2) {
190 return false;
191 }
192 return m_levels[0].numericLevel() == m_currentFloorLevel || m_levels[1].numericLevel() == m_currentFloorLevel;
193}
194
196{
197 if (m_levels.size() != 2) {
198 return 0;
199 }
200 return m_levels[0].numericLevel() == m_currentFloorLevel ? m_levels[1].numericLevel() : m_levels[0].numericLevel();
201}
202
203QString FloorLevelChangeModel::destinationLevelName() const
204{
205 if (m_levels.size() != 2) {
206 return {};
207 }
208 return m_levels[0].numericLevel() == m_currentFloorLevel ? m_levels[1].name() : m_levels[0].name();
209}
210
212{
213 return m_levels.size() > 1;
214}
215
217{
218 if (m_element.tagValue("highway") == "elevator"
219 || !m_element.tagValue("elevator").isEmpty()
220 || m_element.tagValue("building:part") == "elevator"
221 || m_element.tagValue("building") == "elevator"
222 || m_element.tagValue("room") == "elevator"
223 || m_element.tagValue("levelpart") == "elevator_platform")
224 {
225 return i18n("Elevator");
226 }
227
228 if (!m_element.tagValue("stairwell").isEmpty()
229 || m_element.tagValue("stairs") == "yes"
230 || m_element.tagValue("room") == "stairs")
231 {
232 return i18n("Staircase");
233 }
234
235 if (m_levels.size() > 2) {
236 qWarning() << "Unknown floor level change element type:" << m_element.url();
237 }
238 return {};
239}
240
241#include "moc_floorlevelchangemodel.cpp"
int destinationLevel
The destination level for a single level change.
QString title
Human-readable title of the thing enabling a floor level change here.
int currentFloorLevelRow
The model row representing the current floor level.
bool hasSingleLevelChange
The current element changes to a single other floor, ie.
int currentFloorLevel
The current floor level.
bool hasMultipleLevelChanges
The current element changes to multiple levels based on users choice, ie.
Q_INVOKABLE int rowForLevel(int level) const
Maps a floor level to a model row index.
A floor level.
Definition mapdata.h:28
QML wrapper around an OSM element.
Definition osmelement.h:21
A reference to any of OSM::Node/OSM::Way/OSM::Relation.
Definition element.h:24
QString i18n(const char *text, const TYPE &arg...)
OSM-based multi-floor indoor maps for buildings.
QStringView level(QStringView ifopt)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
void modelAboutToBeReset()
virtual QHash< int, QByteArray > roleNames() const const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
bool isEmpty() const const
uint toUInt(bool *ok, int base) const const
bool isValid() const const
int row() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QObject * parent() const const
DisplayRole
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:17:55 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.