KOSMIndoorMap

platformmodel.cpp
1 /*
2  SPDX-FileCopyrightText: 2020 Volker Krause <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "platformmodel.h"
8 #include "platformfinder_p.h"
9 
10 #include <QPointF>
11 #include <QRegularExpression>
12 
13 #include <limits>
14 
15 using namespace KOSMIndoorMap;
16 
17 static constexpr auto TOP_PARENT = std::numeric_limits<quintptr>::max();
18 
19 PlatformModel::PlatformModel(QObject* parent) :
20  QAbstractItemModel(parent)
21 {
22 }
23 
24 PlatformModel::~PlatformModel() = default;
25 
26 MapData PlatformModel::mapData() const
27 {
28  return m_data;
29 }
30 
31 void PlatformModel::setMapData(const MapData &data)
32 {
33  if (m_data == data) {
34  return;
35  }
36 
37  beginResetModel();
38  m_platforms.clear();
39  m_platformLabels.clear();
40  m_sectionsLabels.clear();
41 
42  m_data = data;
43  if (!m_data.isEmpty()) {
44  PlatformFinder finder;
45  m_platforms = finder.find(m_data);
46 
47  m_tagKeys.arrival = m_data.dataSet().makeTagKey("mx:arrival");
48  m_tagKeys.departure = m_data.dataSet().makeTagKey("mx:departure");
49  createLabels();
50  }
51  endResetModel();
52  Q_EMIT mapDataChanged();
53  matchPlatforms();
54 }
55 
56 bool PlatformModel::isEmpty() const
57 {
58  return rowCount() == 0;
59 }
60 
61 int PlatformModel::columnCount(const QModelIndex& parent) const
62 {
63  Q_UNUSED(parent);
64  return 1;
65 }
66 
67 int PlatformModel::rowCount(const QModelIndex &parent) const
68 {
69  if (parent.isValid()) {
70  return parent.internalId() == TOP_PARENT ? m_platforms[parent.row()].sections().size() : 0;
71  }
72 
73  return m_platforms.size();
74 }
75 
76 QVariant PlatformModel::data(const QModelIndex &index, int role) const
77 {
78  if (!index.isValid()) {
79  return {};
80  }
81 
82  if (index.internalId() == TOP_PARENT) {
83  const auto &platform = m_platforms[index.row()];
84  switch (role) {
85  case Qt::DisplayRole:
86  return platform.name();
87  case CoordinateRole:
88  return QPointF(platform.position().lonF(), platform.position().latF());
89  case ElementRole:
90  return QVariant::fromValue(OSM::Element(m_platformLabels[index.row()]));
91  case LevelRole:
92  return platform.level();
93  case TransportModeRole:
94  return platform.mode();
95  case LinesRole:
96  return platform.lines;
97  case ArrivalPlatformRole:
98  return index.row() == m_arrivalPlatformRow;
99  case DeparturePlatformRole:
100  return index.row() == m_departurePlatformRow;
101  }
102  } else {
103  const auto &platform = m_platforms[index.internalId()];
104  const auto &section = platform.sections()[index.row()];
105  switch (role) {
106  case Qt::DisplayRole:
107  return section.name;
108  case CoordinateRole:
109  return QPointF(section.position.center().lonF(), section.position.center().latF());
110  case ElementRole:
111  return QVariant::fromValue(OSM::Element(m_sectionsLabels[index.internalId()][index.row()]));
112  case LevelRole:
113  return platform.level();
114  }
115  }
116 
117  return {};
118 }
119 
120 QModelIndex PlatformModel::index(int row, int column, const QModelIndex &parent) const
121 {
122  if (!parent.isValid()) {
123  return createIndex(row, column, TOP_PARENT);
124  }
125  return createIndex(row, column, parent.row());
126 }
127 
129 {
130  if (!child.isValid() || child.internalId() == TOP_PARENT) {
131  return {};
132  }
133  return createIndex(child.internalId(), 0, TOP_PARENT);
134 }
135 
136 QHash<int, QByteArray> PlatformModel::roleNames() const
137 {
139  n.insert(CoordinateRole, "coordinate");
140  n.insert(ElementRole, "osmElement");
141  n.insert(LevelRole, "level");
142  n.insert(TransportModeRole, "mode");
143  n.insert(LinesRole, "lines");
144  n.insert(ArrivalPlatformRole, "isArrivalPlatform");
145  n.insert(DeparturePlatformRole, "isDeparturePlatform");
146  return n;
147 }
148 
150 {
151  m_arrivalPlatform.setName(name);
152  m_arrivalPlatform.setMode(mode);
153  matchPlatforms();
154 }
155 
156 void PlatformModel::setDeparturePlatform(const QString &name, Platform::Mode mode)
157 {
158  m_departurePlatform.setName(name);
159  m_departurePlatform.setMode(mode);
160  matchPlatforms();
161 }
162 
163 int PlatformModel::arrivalPlatformRow() const
164 {
165  return m_arrivalPlatformRow;
166 }
167 
168 int PlatformModel::departurePlatformRow() const
169 {
170  return m_departurePlatformRow;
171 }
172 
173 void PlatformModel::matchPlatforms()
174 {
175  setPlatformTag(m_arrivalPlatformRow, m_tagKeys.arrival, false);
176  m_arrivalPlatformRow = matchPlatform(m_arrivalPlatform);
177  setPlatformTag(m_arrivalPlatformRow, m_tagKeys.arrival, true);
178  setPlatformTag(m_departurePlatformRow, m_tagKeys.departure, false);
179  m_departurePlatformRow = matchPlatform(m_departurePlatform);
180  setPlatformTag(m_departurePlatformRow, m_tagKeys.departure, true);
181  Q_EMIT platformIndexChanged();
182  if (m_arrivalPlatformRow >= 0) {
183  Q_EMIT dataChanged(index(m_arrivalPlatformRow, 0), index(m_arrivalPlatformRow, 0));
184  }
185  if (m_departurePlatformRow >= 0) {
186  Q_EMIT dataChanged(index(m_departurePlatformRow, 0), index(m_departurePlatformRow, 0));
187  }
188 }
189 
190 static bool isPossiblySamePlatformName(const QString &name, const QString &platform)
191 {
192  // <platform>\w?<section(s)>
193  if (name.size() > platform.size()) {
194  QRegularExpression exp(QStringLiteral("(\\d+)\\s?[A-Z-]+"));
195  const auto match = exp.match(name);
196  return match.hasMatch() && match.captured(1) == platform;
197  }
198 
199  return false;
200 }
201 
202 int PlatformModel::matchPlatform(const Platform &platform) const
203 {
204  if (platform.name().isEmpty()) {
205  return -1;
206  }
207 
208  // exact match
209  int i = 0;
210  for (const auto &p : m_platforms) {
211  if (p.name() == platform.name() && p.mode() == platform.mode()) {
212  return i;
213  }
214  ++i;
215  }
216 
217  // fuzzy match
218  // TODO this likely will need to handle more scenarios
219  // TODO when we get section ranges here, we might want to use those as well?
220  i = 0;
221  for (const auto &p : m_platforms) {
222  if (p.mode() == platform.mode() && isPossiblySamePlatformName(platform.name(), p.name())) {
223  return i;
224  }
225  ++i;
226  }
227 
228  return -1;
229 }
230 
231 void PlatformModel::createLabels()
232 {
233  const auto platformTag = m_data.dataSet().makeTagKey("mx:platform");
234  const auto sectionTag = m_data.dataSet().makeTagKey("mx:platform_section");
235 
236  m_platformLabels.reserve(m_platforms.size());
237  m_sectionsLabels.resize(m_platforms.size());
238  for (std::size_t i = 0; i < m_platforms.size(); ++i) {
239  const auto &p = m_platforms[i];
240 
241  // TODO using the full edge/track path here might be better for layouting
242  auto node = new OSM::Node;
243  node->id = m_data.dataSet().nextInternalId();
244  node->coordinate = p.position();
245  OSM::setTagValue(*node, platformTag, p.name().toUtf8());
246  m_platformLabels.push_back(OSM::UniqueElement(node));
247 
248  m_sectionsLabels[i].reserve(p.sections().size());
249  for (const auto &sec : p.sections()) {
250  auto node = new OSM::Node;
251  node->id = m_data.dataSet().nextInternalId();
252  node->coordinate = sec.position.center();
253  OSM::setTagValue(*node, sectionTag, sec.name.toUtf8());
254  m_sectionsLabels[i].push_back(OSM::UniqueElement(node));
255  }
256  }
257 }
258 
259 void PlatformModel::setPlatformTag(int idx, OSM::TagKey key, bool enabled)
260 {
261  if (idx < 0) {
262  return;
263  }
264 
265  m_platformLabels[idx].setTagValue(key, enabled ? "1" : "0");
266 }
267 
268 #include "moc_platformmodel.cpp"
OSM-based multi-floor indoor maps for buildings.
QRegularExpressionMatch match(const QString &subject, int offset, QRegularExpression::MatchType matchType, QRegularExpression::MatchOptions matchOptions) const const
virtual QHash< int, QByteArray > roleNames() const const
int size() const const
quintptr internalId() const const
bool isValid() const const
A reference to any of OSM::Node/OSMWay/OSMRelation.
Definition: element.h:22
A key of an OSM tag.
Definition: datatypes.h:179
void setTagValue(Elem &elem, TagKey key, const QByteArray &value)
Inserts a new tag, or updates an existing one.
Definition: datatypes.h:433
An OSM node.
Definition: datatypes.h:194
Q_INVOKABLE void setArrivalPlatform(const QString &name, KOSMIndoorMap::Platform::Mode mode)
Match arrival/departure platform against what we found in the map data.
DisplayRole
bool isEmpty() const const
Raw OSM map data, separated by levels.
Definition: mapdata.h:59
int row() const const
QVariant fromValue(const T &value)
A railway platform/track.
Definition: platform.h:34
Mode
Transportation mode served by a platform.
Definition: platform.h:80
TagKey makeTagKey(const char *keyName, StringMemory keyMemOpt=StringMemory::Transient)
Create a tag key for the given tag name.
Definition: datatypes.cpp:17
QString name() const
User-visible name of the platform.
Definition: platform.cpp:30
void reserve(int size)
A std::unique_ptr-like object for OSM element types.
Definition: element.h:98
QObject * parent() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Oct 25 2021 23:04:00 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.