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

KDE's Doxygen guidelines are available online.