KOSMIndoorMap

locationqueryoverlayproxymodel.cpp
1 /*
2  SPDX-FileCopyrightText: 2020 Volker Krause <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "locationqueryoverlayproxymodel.h"
8 
9 #include <KPublicTransport/Location>
10 #include <KPublicTransport/LocationQueryModel>
11 #include <KPublicTransport/RentalVehicle>
12 
13 #include <osm/element.h>
14 #include <osm/geomath.h>
15 
16 using namespace KOSMIndoorMap;
17 using namespace KPublicTransport;
18 
19 struct vehicle_type {
20  const char *tagName;
21  RentalVehicle::VehicleType vehicleType;
22 };
23 static constexpr const vehicle_type vehicle_type_map[] = {
24  { "mx:realtime_available:bike", RentalVehicle::Bicycle },
25  { "mx:realtime_available:pedelec", RentalVehicle::Pedelec },
26  { "mx:realtime_available:scooter", RentalVehicle::ElectricKickScooter },
27  { "mx:realtime_available:motorcycle", RentalVehicle::ElectricMoped },
28  { "mx:realtime_available:car", RentalVehicle::Car },
29 };
30 
31 LocationQueryOverlayProxyModel::LocationQueryOverlayProxyModel(QObject *parent)
32  : QAbstractListModel(parent)
33 {
34  static_assert((sizeof(vehicle_type_map) / sizeof(vehicle_type)) == (sizeof(LocationQueryOverlayProxyModel::m_realtimeAvailableTagKeys) / sizeof(OSM::TagKey)));
35 }
36 
37 LocationQueryOverlayProxyModel::~LocationQueryOverlayProxyModel() = default;
38 
39 MapData LocationQueryOverlayProxyModel::mapData() const
40 {
41  return m_data;
42 }
43 
44 void LocationQueryOverlayProxyModel::setMapData(const MapData &data)
45 {
46  if (m_data == data) {
47  return;
48  }
49 
50  beginResetModel();
51  m_data = data;
52 
53  if (!m_data.isEmpty()) {
54  m_tagKeys.name = m_data.dataSet().makeTagKey("name");
55  m_tagKeys.amenity = m_data.dataSet().makeTagKey("amenity");
56  m_tagKeys.capacity = m_data.dataSet().makeTagKey("capacity");
57  m_tagKeys.realtimeAvailable = m_data.dataSet().makeTagKey("mx:realtime_available");
58  m_tagKeys.network = m_data.dataSet().makeTagKey("network");
59  m_tagKeys.mxoid = m_data.dataSet().makeTagKey("mx:oid");
60  m_tagKeys.remainingRange = m_data.dataSet().makeTagKey("mx:remaining_range");
61  m_tagKeys.vehicle = m_data.dataSet().makeTagKey("mx:vehicle");
62  }
63 
64  int i = 0;
65  for (const auto &v : vehicle_type_map) {
66  m_realtimeAvailableTagKeys[i++] = m_data.dataSet().makeTagKey(v.tagName);
67  }
68 
69  initialize();
70  endResetModel();
71  Q_EMIT mapDataChanged();
72 }
73 
74 QObject* LocationQueryOverlayProxyModel::sourceModel() const
75 {
76  return m_sourceModel;
77 }
78 
79 void LocationQueryOverlayProxyModel::setSourceModel(QObject *sourceModel)
80 {
81  if (m_sourceModel == sourceModel) {
82  return;
83  }
84  beginResetModel();
85  m_sourceModel = qobject_cast<QAbstractItemModel*>(sourceModel);
86  initialize();
87  endResetModel();
88 
89  connect(m_sourceModel, &QAbstractItemModel::modelReset, this, [this]() {
90  beginResetModel();
91  initialize();
92  endResetModel();
93  });
94  connect(m_sourceModel, &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex &parent, int first, int last) {
95  if (parent.isValid() || m_data.isEmpty()) {
96  return;
97  }
98  beginInsertRows({}, first, last);
99  for (int i = first; i <= last; ++i) {
100  m_nodes.insert(m_nodes.begin() + i, nodeForRow(i));
101  }
102  endInsertRows();
103  });
104  connect(m_sourceModel, &QAbstractItemModel::rowsRemoved, this, [this](const QModelIndex &parent, int first, int last) {
105  if (parent.isValid() || m_data.isEmpty()) {
106  return;
107  }
108  beginRemoveRows({}, first, last);
109  m_nodes.erase(m_nodes.begin() + first, m_nodes.begin() + last);
110  endRemoveRows();
111  });
112  connect(m_sourceModel, &QAbstractItemModel::dataChanged, this, [this](const QModelIndex &first, const QModelIndex &last) {
113  if (first.parent().isValid() || last.parent().isValid() || m_data.isEmpty()) {
114  return;
115  }
116  for (int i = first.row(); i <= last.row(); ++i) {
117  m_nodes[i] = nodeForRow(i);
118  }
119  Q_EMIT dataChanged(index(first.row(), 0), index(last.row(), 0));
120  });
121 }
122 
123 int LocationQueryOverlayProxyModel::rowCount(const QModelIndex &parent) const
124 {
125  if (parent.isValid()) {
126  return 0;
127  }
128  return m_nodes.size();
129 }
130 
131 QVariant LocationQueryOverlayProxyModel::data(const QModelIndex &index, int role) const
132 {
133  if (!index.isValid()) {
134  return {};
135  }
136 
137  switch (role) {
138  case ElementRole:
139  return QVariant::fromValue(OSM::Element(&m_nodes[index.row()].overlayNode));
140  case LevelRole:
141  return 0;
142  case HiddenElementRole:
143  return QVariant::fromValue(m_nodes[index.row()].sourceElement);
144  }
145 
146  return {};
147 }
148 
149 QHash<int, QByteArray> LocationQueryOverlayProxyModel::roleNames() const
150 {
152  n.insert(ElementRole, "osmElement");
153  n.insert(LevelRole, "level");
154  n.insert(HiddenElementRole, "hiddenElement");
155  return n;
156 }
157 
158 void LocationQueryOverlayProxyModel::initialize()
159 {
160  if (m_data.isEmpty() || !m_sourceModel) {
161  return;
162  }
163 
164  m_nodes.clear();
165  const auto rows = m_sourceModel->rowCount();
166  m_nodes.reserve(rows);
167  for (int i = 0; i < rows; ++i) {
168  m_nodes.push_back(nodeForRow(i));
169  }
170 }
171 
172 LocationQueryOverlayProxyModel::Info LocationQueryOverlayProxyModel::nodeForRow(int row) const
173 {
174  const auto idx = m_sourceModel->index(row, 0);
175  const auto loc = idx.data(LocationQueryModel::LocationRole).value<Location>();
176 
177  Info info;
178  info.overlayNode.coordinate = OSM::Coordinate(loc.latitude(), loc.longitude());
179 
180  switch (loc.type()) {
181  case Location::Place:
182  case Location::Stop:
183  Q_UNREACHABLE();
184  break;
185  case Location::RentedVehicleStation:
186  {
187  const auto station = loc.rentalVehicleStation();
188 
189  // try to find a matching node in the base OSM data
190  for (const auto &n : m_data.dataSet().nodes) {
191  if (OSM::distance(n.coordinate, info.overlayNode.coordinate) < 10 && OSM::tagValue(n, m_tagKeys.amenity) == "bicycle_rental") {
192  qDebug() << "found matching node, cloning that!" << n.url();
193  info.sourceElement = OSM::Element(&n);
194  info.overlayNode = n;
195  OSM::setTagValue(info.overlayNode, m_tagKeys.mxoid, QByteArray::number(qlonglong(n.id)));
196  break;
197  }
198  }
199 
200  info.overlayNode.id = m_data.dataSet().nextInternalId();
201  OSM::setTagValue(info.overlayNode, m_tagKeys.amenity, "bicycle_rental");
202  if (station.capacity() >= 0) {
203  OSM::setTagValue(info.overlayNode, m_tagKeys.capacity, QByteArray::number(station.capacity()));
204  }
205  OSM::setTagValue(info.overlayNode, m_tagKeys.realtimeAvailable, QByteArray::number(station.availableVehicles()));
206  if (OSM::tagValue(info.overlayNode, m_tagKeys.network).isEmpty() && !station.network().name().isEmpty()) {
207  OSM::setTagValue(info.overlayNode, m_tagKeys.network, station.network().name().toUtf8());
208  }
209 
210  int i = 0;
211  for (const auto &v : vehicle_type_map) {
212  if (station.availableVehicles(v.vehicleType) > 0) {
213  OSM::setTagValue(info.overlayNode, m_realtimeAvailableTagKeys[i], QByteArray::number(station.availableVehicles(v.vehicleType)));
214  }
215  ++i;
216  }
217 
218  break;
219  }
220  case Location::RentedVehicle:
221  {
222  const auto vehicle = loc.data().value<RentalVehicle>();
223 
224  // free floating vehicles have no matching OSM element, so no point in searching for one
225  info.overlayNode.id = m_data.dataSet().nextInternalId();
226  switch (vehicle.type()) {
227  case RentalVehicle::Unknown:
228  case RentalVehicle::Bicycle:
229  case RentalVehicle::Pedelec:
230  OSM::setTagValue(info.overlayNode, m_tagKeys.vehicle, "bicycle_rental");
231  break;
232  case RentalVehicle::ElectricKickScooter:
233  OSM::setTagValue(info.overlayNode, m_tagKeys.vehicle, "scooter_rental");
234  break;
235  case RentalVehicle::ElectricMoped:
236  OSM::setTagValue(info.overlayNode, m_tagKeys.vehicle, "motorcycle_rental");
237  break;
238  case RentalVehicle::Car:
239  OSM::setTagValue(info.overlayNode, m_tagKeys.vehicle, "car_rental");
240  break;
241  }
242  OSM::setTagValue(info.overlayNode, m_tagKeys.name, loc.name().toUtf8());
243  if (OSM::tagValue(info.overlayNode, m_tagKeys.network).isEmpty() && !loc.rentalVehicle().network().name().isEmpty()) {
244  OSM::setTagValue(info.overlayNode, m_tagKeys.network, loc.rentalVehicle().network().name().toUtf8());
245  }
246  if (vehicle.remainingRange() >= 0) {
247  OSM::setTagValue(info.overlayNode, m_tagKeys.remainingRange, QByteArray::number(vehicle.remainingRange()));
248  }
249  break;
250  }
251  case Location::Equipment:
252  break;
253  }
254  return info;
255 }
OSM-based multi-floor indoor maps for buildings.
virtual QHash< int, QByteArray > roleNames() const const
bool isEmpty() const const
KCRASH_EXPORT void initialize()
Coordinate, stored as 1e7 * degree to avoid floating point precision issues, and offset to unsigned v...
Definition: datatypes.h:37
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
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector< int > &roles)
QByteArray tagValue(const Elem &elem, TagKey key)
Returns the tag value for key of elem.
Definition: datatypes.h:353
Raw OSM map data, separated by levels.
Definition: mapdata.h:59
QByteArray number(int n, int base)
int row() const const
QModelIndex parent() const const
void rowsRemoved(const QModelIndex &parent, int first, int last)
QVariant fromValue(const T &value)
TagKey makeTagKey(const char *keyName, StringMemory keyMemOpt=StringMemory::Transient)
Create a tag key for the given tag name.
Definition: datatypes.cpp:17
void rowsInserted(const QModelIndex &parent, int first, int last)
KOSM_EXPORT double distance(double lat1, double lon1, double lat2, double lon2)
Distance between two coordinates.
Definition: geomath.cpp:17
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.