KOSMIndoorMap

locationqueryoverlayproxymodel.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 "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
16using namespace KOSMIndoorMap;
17using namespace KPublicTransport;
18
19struct vehicle_type {
20 const char *tagName;
22};
23static 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
31LocationQueryOverlayProxyModel::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
37LocationQueryOverlayProxyModel::~LocationQueryOverlayProxyModel() = default;
38
39MapData LocationQueryOverlayProxyModel::mapData() const
40{
41 return m_data;
42}
43
44void LocationQueryOverlayProxyModel::setMapData(const MapData &data)
45{
46 if (m_data == data) {
47 return;
48 }
49
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 m_tagKeys.addr_street = m_data.dataSet().makeTagKey("addr:street");
63 m_tagKeys.addr_city = m_data.dataSet().makeTagKey("addr:city");
64 m_tagKeys.addr_postcode = m_data.dataSet().makeTagKey("addr:postcode");
65 }
66
67 int i = 0;
68 for (const auto &v : vehicle_type_map) {
69 m_realtimeAvailableTagKeys[i++] = m_data.dataSet().makeTagKey(v.tagName);
70 }
71
72 initialize();
74 Q_EMIT mapDataChanged();
75}
76
77QObject* LocationQueryOverlayProxyModel::sourceModel() const
78{
79 return m_sourceModel;
80}
81
82void LocationQueryOverlayProxyModel::setSourceModel(QObject *sourceModel)
83{
84 if (m_sourceModel == sourceModel) {
85 return;
86 }
88 m_sourceModel = qobject_cast<QAbstractItemModel*>(sourceModel);
89 initialize();
91
92 connect(m_sourceModel, &QAbstractItemModel::modelReset, this, [this]() {
94 initialize();
96 });
97 connect(m_sourceModel, &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex &parent, int first, int last) {
98 if (parent.isValid() || m_data.isEmpty()) {
99 return;
100 }
101 beginInsertRows({}, first, last);
102 for (int i = first; i <= last; ++i) {
103 m_nodes.insert(m_nodes.begin() + i, nodeForRow(i));
104 }
106 });
107 connect(m_sourceModel, &QAbstractItemModel::rowsRemoved, this, [this](const QModelIndex &parent, int first, int last) {
108 if (parent.isValid() || m_data.isEmpty()) {
109 return;
110 }
111 beginRemoveRows({}, first, last);
112 m_nodes.erase(m_nodes.begin() + first, m_nodes.begin() + last);
114 });
115 connect(m_sourceModel, &QAbstractItemModel::dataChanged, this, [this](const QModelIndex &first, const QModelIndex &last) {
116 if (first.parent().isValid() || last.parent().isValid() || m_data.isEmpty()) {
117 return;
118 }
119 for (int i = first.row(); i <= last.row(); ++i) {
120 m_nodes[i] = nodeForRow(i);
121 }
122 Q_EMIT dataChanged(index(first.row(), 0), index(last.row(), 0));
123 });
124}
125
126int LocationQueryOverlayProxyModel::rowCount(const QModelIndex &parent) const
127{
128 if (parent.isValid()) {
129 return 0;
130 }
131 return m_nodes.size();
132}
133
134QVariant LocationQueryOverlayProxyModel::data(const QModelIndex &index, int role) const
135{
136 if (!index.isValid()) {
137 return {};
138 }
139
140 switch (role) {
141 case ElementRole:
142 return QVariant::fromValue(OSM::Element(&m_nodes[index.row()].overlayNode));
143 case LevelRole:
144 return 0;
145 case HiddenElementRole:
146 return QVariant::fromValue(m_nodes[index.row()].sourceElement);
147 }
148
149 return {};
150}
151
152QHash<int, QByteArray> LocationQueryOverlayProxyModel::roleNames() const
153{
155 n.insert(ElementRole, "osmElement");
156 n.insert(LevelRole, "level");
157 n.insert(HiddenElementRole, "hiddenElement");
158 return n;
159}
160
161void LocationQueryOverlayProxyModel::initialize()
162{
163 if (m_data.isEmpty() || !m_sourceModel) {
164 return;
165 }
166
167 m_nodes.clear();
168 const auto rows = m_sourceModel->rowCount();
169 m_nodes.reserve(rows);
170 for (int i = 0; i < rows; ++i) {
171 m_nodes.push_back(nodeForRow(i));
172 }
173}
174
175static void setTagIfMissing(OSM::Node &node, OSM::TagKey tag, const QString &value)
176{
177 if (OSM::tagValue(node, tag).isEmpty() && !value.isEmpty()) {
178 OSM::setTagValue(node, tag, value.toUtf8());
179 }
180}
181
182LocationQueryOverlayProxyModel::Info LocationQueryOverlayProxyModel::nodeForRow(int row) const
183{
184 const auto idx = m_sourceModel->index(row, 0);
185 const auto loc = idx.data(LocationQueryModel::LocationRole).value<Location>();
186
187 Info info;
188 info.overlayNode.coordinate = OSM::Coordinate(loc.latitude(), loc.longitude());
189
190 switch (loc.type()) {
191 case Location::Place:
192 case Location::Stop:
194 qDebug() << "got a location type we didn't ask for:" << loc.type() << loc.name();
195 break;
197 {
198 const auto station = loc.rentalVehicleStation();
199
200 // try to find a matching node in the base OSM data
201 for (const auto &n : m_data.dataSet().nodes) {
202 if (OSM::distance(n.coordinate, info.overlayNode.coordinate) < 10 && OSM::tagValue(n, m_tagKeys.amenity) == "bicycle_rental") {
203 qDebug() << "found matching node, cloning that!" << n.url();
204 info.sourceElement = OSM::Element(&n);
205 info.overlayNode = n;
206 OSM::setTagValue(info.overlayNode, m_tagKeys.mxoid, QByteArray::number(qlonglong(n.id)));
207 break;
208 }
209 }
210
211 info.overlayNode.id = m_data.dataSet().nextInternalId();
212 OSM::setTagValue(info.overlayNode, m_tagKeys.amenity, "bicycle_rental");
213 if (station.capacity() >= 0) {
214 OSM::setTagValue(info.overlayNode, m_tagKeys.capacity, QByteArray::number(station.capacity()));
215 }
216 OSM::setTagValue(info.overlayNode, m_tagKeys.realtimeAvailable, QByteArray::number(station.availableVehicles()));
217 setTagIfMissing(info.overlayNode, m_tagKeys.network, station.network().name());
218 setTagIfMissing(info.overlayNode, m_tagKeys.name, loc.name());
219 setTagIfMissing(info.overlayNode, m_tagKeys.addr_street, loc.streetAddress());
220 setTagIfMissing(info.overlayNode, m_tagKeys.addr_city, loc.locality());
221 setTagIfMissing(info.overlayNode, m_tagKeys.addr_postcode, loc.postalCode());
222
223 int i = 0;
224 for (const auto &v : vehicle_type_map) {
225 if (station.availableVehicles(v.vehicleType) > 0) {
226 OSM::setTagValue(info.overlayNode, m_realtimeAvailableTagKeys[i], QByteArray::number(station.availableVehicles(v.vehicleType)));
227 }
228 ++i;
229 }
230
231 break;
232 }
234 {
235 const auto vehicle = loc.data().value<RentalVehicle>();
236
237 // free floating vehicles have no matching OSM element, so no point in searching for one
238 info.overlayNode.id = m_data.dataSet().nextInternalId();
239 switch (vehicle.type()) {
240 case RentalVehicle::Unknown:
243 OSM::setTagValue(info.overlayNode, m_tagKeys.vehicle, "bicycle_rental");
244 break;
246 OSM::setTagValue(info.overlayNode, m_tagKeys.vehicle, "scooter_rental");
247 break;
249 OSM::setTagValue(info.overlayNode, m_tagKeys.vehicle, "motorcycle_rental");
250 break;
252 OSM::setTagValue(info.overlayNode, m_tagKeys.vehicle, "car_rental");
253 break;
254 }
255 OSM::setTagValue(info.overlayNode, m_tagKeys.name, loc.name().toUtf8());
256 setTagIfMissing(info.overlayNode, m_tagKeys.network, vehicle.network().name());
257 if (vehicle.remainingRange() >= 0) {
258 OSM::setTagValue(info.overlayNode, m_tagKeys.remainingRange, QByteArray::number(vehicle.remainingRange()));
259 }
260 break;
261 }
263 break;
264 }
265 return info;
266}
267
268#include "moc_locationqueryoverlayproxymodel.cpp"
Raw OSM map data, separated by levels.
Definition mapdata.h:60
Coordinate, stored as 1e7 * degree to avoid floating point precision issues, and offset to unsigned v...
Definition datatypes.h:37
Id nextInternalId() const
Create a unique id for internal use (ie.
TagKey makeTagKey(const char *keyName, StringMemory keyMemOpt=StringMemory::Transient)
Create a tag key for the given tag name.
Definition datatypes.cpp:28
A reference to any of OSM::Node/OSM::Way/OSM::Relation.
Definition element.h:24
An OSM node.
Definition datatypes.h:204
A key of an OSM tag.
Definition datatypes.h:179
OSM-based multi-floor indoor maps for buildings.
KOSM_EXPORT double distance(double lat1, double lon1, double lat2, double lon2)
Distance between two coordinates.
Definition geomath.cpp:16
void setTagValue(Elem &elem, TagKey key, QByteArray &&value)
Inserts a new tag, or updates an existing one.
Definition datatypes.h:494
QByteArray tagValue(const Elem &elem, TagKey key)
Returns the tag value for key of elem.
Definition datatypes.h:420
void beginInsertRows(const QModelIndex &parent, int first, int last)
void beginRemoveRows(const QModelIndex &parent, int first, int last)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
virtual QHash< int, QByteArray > roleNames() const const
virtual int rowCount(const QModelIndex &parent) const const=0
void rowsInserted(const QModelIndex &parent, int first, int last)
void rowsRemoved(const QModelIndex &parent, int first, int last)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
QByteArray number(double n, char format, int precision)
QVariant data(int role) const const
bool isValid() const const
QModelIndex parent() const const
int row() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
T qobject_cast(QObject *object)
bool isEmpty() const const
QByteArray toUtf8() const const
QVariant fromValue(T &&value)
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Dec 6 2024 12:11:15 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.