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(FloorLevelRole, "floorLevel");
54 n.insert(CurrentFloorRole, "isCurrentFloor");
55 return n;
56}
57
59{
60 return m_currentFloorLevel;
61}
62
64{
65 const auto it = std::find_if(m_levels.begin(), m_levels.end(), [this](const auto &level) { return level.numericLevel() == m_currentFloorLevel; });
66 return it != m_levels.end() ? (int)std::distance(m_levels.begin(), it) : -1;
67}
68
69void FloorLevelChangeModel::setCurrentFloorLevel(int level)
70{
71 if (m_currentFloorLevel == level) {
72 return;
73 }
74 m_currentFloorLevel = level;
75 if (!m_levels.empty()) {
76 Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0));
77 }
78 Q_EMIT contentChanged();
79}
80
81FloorLevelModel* FloorLevelChangeModel::floorLevelModel() const
82{
83 return m_floorLevelModel;
84}
85
86void FloorLevelChangeModel::setFloorLevelModel(FloorLevelModel *floorLevelModel)
87{
88 if (m_floorLevelModel == floorLevelModel) {
89 return;
90 }
91
92 if (m_floorLevelModel) {
93 disconnect(m_floorLevelModel, &FloorLevelModel::modelAboutToBeReset, this, nullptr);
94 }
95
96 m_floorLevelModel = floorLevelModel;
97 connect(m_floorLevelModel, &FloorLevelModel::modelAboutToBeReset, this, [this]() {
99 m_element = {};
100 m_levels.clear();
102 });
103 Q_EMIT contentChanged();
104}
105
106OSMElement FloorLevelChangeModel::element() const
107{
108 return OSMElement(m_element);
109}
110
111void FloorLevelChangeModel::setElement(const OSMElement &element)
112{
113 if (m_element == element.element()) {
114 return;
115 }
116
118 m_element = element.element();
119 m_levels.clear();
120
121 if (isLevelChangeElement(m_element)) {
122
123 // elevators are sometimes also tagged with building:level tags instead of level/repeat_on, so handle that as well
124 const auto buildingLevels = m_element.tagValue("building:levels").toUInt();
125 if (buildingLevels > 0) {
126 const auto buildingMinLevel = m_element.tagValue("building:min_level", "level").toUInt();
127 for (auto i = buildingMinLevel; i < buildingLevels; ++i) {
128 appendFullFloorLevel(i * 10);
129 }
130 }
131 const auto buildingUndergroundLevel = m_element.tagValue("building:levels:underground").toUInt();
132 for (auto i = buildingUndergroundLevel; i > 0; --i) {
133 appendFullFloorLevel(-i * 10);
134 }
135
136 LevelParser::parse(m_element.tagValue("level", "repeat_on"), m_element, [this](int level, OSM::Element e) {
137 Q_UNUSED(e);
138 appendFloorLevel(level);
139
140 });
141 std::sort(m_levels.begin(), m_levels.end());
142 m_levels.erase(std::unique(m_levels.begin(), m_levels.end()), m_levels.end());
143 }
144
146 Q_EMIT contentChanged();
147}
148
149bool FloorLevelChangeModel::isLevelChangeElement(OSM::Element element) const
150{
151 return !element.tagValue("highway").isEmpty()
152 || !element.tagValue("elevator").isEmpty()
153 || !element.tagValue("stairwell").isEmpty()
154 || element.tagValue("building:part") == "elevator"
155 || element.tagValue("building") == "elevator"
156 || element.tagValue("room") == "elevator"
157 || element.tagValue("levelpart") == "elevator_platform"
158 || (!element.tagValue("indoor").isEmpty() && element.tagValue("stairs") == "yes")
159 || element.tagValue("room") == "stairs";
160}
161
162void FloorLevelChangeModel::appendFloorLevel(int level)
163{
164 MapLevel ml(level);
165 if (ml.isFullLevel()) {
166 appendFullFloorLevel(level);
167 } else {
168 appendFullFloorLevel(ml.fullLevelBelow());
169 appendFullFloorLevel(ml.fullLevelAbove());
170 }
171}
172
173void FloorLevelChangeModel::appendFullFloorLevel(int level)
174{
175 if (!m_floorLevelModel) {
176 m_levels.push_back(MapLevel(level));
177 } else {
178 const auto row = m_floorLevelModel->rowForLevel(level);
179 if (row >= 0) {
180 const auto idx = m_floorLevelModel->index(row, 0);
181 m_levels.push_back(m_floorLevelModel->data(idx, FloorLevelModel::MapLevelRole).value<MapLevel>());
182 }
183 }
184}
185
187{
188 if (m_levels.size() != 2) {
189 return false;
190 }
191 return m_levels[0].numericLevel() == m_currentFloorLevel || m_levels[1].numericLevel() == m_currentFloorLevel;
192}
193
195{
196 if (m_levels.size() != 2) {
197 return 0;
198 }
199 return m_levels[0].numericLevel() == m_currentFloorLevel ? m_levels[1].numericLevel() : m_levels[0].numericLevel();
200}
201
202QString FloorLevelChangeModel::destinationLevelName() const
203{
204 if (m_levels.size() != 2) {
205 return {};
206 }
207 return m_levels[0].numericLevel() == m_currentFloorLevel ? m_levels[1].name() : m_levels[0].name();
208}
209
211{
212 return m_levels.size() > 1;
213}
214
216{
217 if (m_element.tagValue("highway") == "elevator"
218 || !m_element.tagValue("elevator").isEmpty()
219 || m_element.tagValue("building:part") == "elevator"
220 || m_element.tagValue("building") == "elevator"
221 || m_element.tagValue("room") == "elevator"
222 || m_element.tagValue("levelpart") == "elevator_platform")
223 {
224 return i18n("Elevator");
225 }
226
227 if (!m_element.tagValue("stairwell").isEmpty()
228 || m_element.tagValue("stairs") == "yes"
229 || m_element.tagValue("room") == "stairs")
230 {
231 return i18n("Staircase");
232 }
233
234 if (m_levels.size() > 2) {
235 qWarning() << "Unknown floor level change element type:" << m_element.url();
236 }
237 return {};
238}
239
240#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 Fri Oct 4 2024 12:02:24 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.