KPublicTransport

journeyrequest.cpp
1/*
2 SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "journeyrequest.h"
8#include "requestcontext_p.h"
9#include "datatypes/datatypes_p.h"
10#include "datatypes/json_p.h"
11#include "datatypes/locationutil_p.h"
12
13#include <KPublicTransport/Location>
14
15#include <QCryptographicHash>
16#include <QDateTime>
17#include <QDebug>
18#include <QMetaEnum>
19#include <QSharedData>
20
21using namespace Qt::Literals;
22using namespace KPublicTransport;
23
24enum { JourneyCacheTimeResolution = 60 }; // in seconds
25
26namespace KPublicTransport {
27class JourneyRequestPrivate : public QSharedData {
28public:
29 Location from;
30 Location to;
31 QDateTime dateTime;
32 std::vector<RequestContext> contexts;
33 QStringList backendIds;
35 JourneySection::Modes modes = JourneySection::PublicTransport | JourneySection::RentedVehicle | JourneySection::Walking;
36 int maximumResults = 12;
37 bool downloadAssets = false;
38 bool includeIntermediateStops = true;
39 bool includePaths = false;
40
41 std::vector<IndividualTransport> accessModes = { {IndividualTransport::Walk} };
42 std::vector<IndividualTransport> egressModes = { {IndividualTransport::Walk} };
43 std::vector<IndividualTransport> individualTransportModes = { {IndividualTransport::Walk} };
44 std::vector<Line::Mode> lineModes;
45};
46}
47
48KPUBLICTRANSPORT_MAKE_GADGET(JourneyRequest)
49KPUBLICTRANSPORT_MAKE_PROPERTY(JourneyRequest, Location, from, setFrom)
50KPUBLICTRANSPORT_MAKE_PROPERTY(JourneyRequest, Location, to, setTo)
51KPUBLICTRANSPORT_MAKE_PROPERTY(JourneyRequest, JourneyRequest::DateTimeMode, dateTimeMode, setDateTimeMode)
52KPUBLICTRANSPORT_MAKE_PROPERTY(JourneyRequest, bool, downloadAssets, setDownloadAssets)
53KPUBLICTRANSPORT_MAKE_PROPERTY(JourneyRequest, JourneySection::Modes, modes, setModes)
54KPUBLICTRANSPORT_MAKE_PROPERTY(JourneyRequest, int, maximumResults, setMaximumResults)
55KPUBLICTRANSPORT_MAKE_PROPERTY(JourneyRequest, bool, includeIntermediateStops, setIncludeIntermediateStops)
56KPUBLICTRANSPORT_MAKE_PROPERTY(JourneyRequest, bool, includePaths, setIncludePaths)
57
59 : d(new JourneyRequestPrivate)
60{
61 d->from = from;
62 d->to = to;
63}
64
66{
67 return !d->to.isEmpty() && !d->from.isEmpty();
68}
69
71{
72 if (!d->dateTime.isValid()) {
73 d->dateTime = QDateTime::currentDateTime();
74 }
75 return d->dateTime;
76}
77
78void JourneyRequest::setDateTime(const QDateTime& dt)
79{
80 d.detach();
81 d->dateTime = dt;
82}
83
85{
86 d.detach();
87 d->dateTime = dt;
88 d->dateTimeMode = Departure;
89}
90
92{
93 d.detach();
94 d->dateTime = dt;
95 d->dateTimeMode = Arrival;
96}
97
98RequestContext JourneyRequest::context(const AbstractBackend *backend) const
99{
100 const auto it = std::lower_bound(d->contexts.begin(), d->contexts.end(), backend);
101 if (it != d->contexts.end() && (*it).backend == backend) {
102 return *it;
103 }
104
105 RequestContext context;
106 context.backend = backend;
107 return context;
108}
109
110const std::vector<RequestContext>& JourneyRequest::contexts() const
111{
112 return d->contexts;
113}
114
115void JourneyRequest::setContext(const AbstractBackend *backend, RequestContext &&context)
116{
117 d.detach();
118 const auto it = std::lower_bound(d->contexts.begin(), d->contexts.end(), backend);
119 if (it != d->contexts.end() && (*it).backend == backend) {
120 (*it) = std::move(context);
121 } else {
122 d->contexts.insert(it, std::move(context));
123 }
124}
125
126void JourneyRequest::purgeLoops(const JourneyRequest &baseRequest)
127{
128 RequestContext::purgeLoops(d->contexts, baseRequest.contexts());
129}
130
131QJsonObject JourneyRequest::toJson(const KPublicTransport::JourneyRequest &req)
132{
133 auto obj = Json::toJson(req);
134 obj.insert("from"_L1, Location::toJson(req.from()));
135 obj.insert("to"_L1, Location::toJson(req.to()));
136 obj.insert("accessModes"_L1, IndividualTransport::toJson(req.accessModes()));
137 obj.insert("egressModes"_L1, IndividualTransport::toJson(req.egressModes()));
138 obj.insert("individualTransportModes"_L1, IndividualTransport::toJson(req.individualTransportModes()));
139 return obj;
140}
141
143{
144 return d->backendIds;
145}
146
148{
149 d.detach();
150 d->backendIds = backendIds;
151}
152
153template <typename T>
154static QVariantList toVariantList(const std::vector<T> &v)
155{
156 QVariantList l;
157 l.reserve(v.size());
158 std::transform(v.begin(), v.end(), std::back_inserter(l), [](const T &value) {
159 return QVariant::fromValue<T>(value);
160 });
161 return l;
162}
163
164const std::vector<IndividualTransport>& JourneyRequest::accessModes() const
165{
166 return d->accessModes;
167}
168
169QVariantList JourneyRequest::accessModesVariant() const
170{
171 return toVariantList(d->accessModes);
172}
173
174void JourneyRequest::setAccessModes(std::vector<IndividualTransport> &&accessModes)
175{
176 d.detach();
177 d->accessModes = std::move(accessModes);
178}
179
180void JourneyRequest::setAccessModesVariant(const QVariantList &accessModesVariant)
181{
182 d.detach();
183 d->accessModes = IndividualTransport::fromVariant(accessModesVariant);
184}
185
186const std::vector<IndividualTransport>& JourneyRequest::egressModes() const
187{
188 return d->egressModes;
189}
190
191QVariantList JourneyRequest::egressModesVariant() const
192{
193 return toVariantList(d->egressModes);
194}
195
196void JourneyRequest::setEgressModes(std::vector<IndividualTransport>&& egressModes)
197{
198 d.detach();
199 d->egressModes = std::move(egressModes);
200}
201
202void JourneyRequest::setEgressModesVariant(const QVariantList &egressModesVariant)
203{
204 d.detach();
205 d->egressModes = IndividualTransport::fromVariant(egressModesVariant);
206}
207
208const std::vector<Line::Mode>& JourneyRequest::lineModes() const
209{
210 return d->lineModes;
211}
212
213static bool hasTakeBikeMode(const std::vector<IndividualTransport> &modes)
214{
215 return std::any_of(modes.begin(), modes.end(), [](const auto &it) {
216 return it.mode() == IndividualTransport::Bike && it.qualifier() == IndividualTransport::None;
217 });
218}
219
221{
222 return hasTakeBikeMode(d->accessModes) && hasTakeBikeMode(d->egressModes);
223}
224
225void JourneyRequest::setLineModes(std::vector<Line::Mode> &&lineModes)
226{
227 d.detach();
228 d->lineModes = std::move(lineModes);
229 std::sort(d->lineModes.begin(), d->lineModes.end());
230 d->lineModes.erase(std::unique(d->lineModes.begin(), d->lineModes.end()), d->lineModes.end());
231}
232
233QVariantList JourneyRequest::lineModesVariant() const
234{
235 return toVariantList(d->lineModes);
236}
237
238void JourneyRequest::setLineModesVariant(const QVariantList &lineModes)
239{
240 auto l = std::move(d->lineModes);
241 l.clear();
242 l.reserve(lineModes.size());
243 std::transform(lineModes.begin(), lineModes.end(), std::back_inserter(l), [](const auto &mode) { return static_cast<Line::Mode>(mode.toInt()); });
244 setLineModes(std::move(l));
245}
246
247const std::vector<IndividualTransport>& JourneyRequest::individualTransportModes() const
248{
249 return d->individualTransportModes;
250}
251
252void JourneyRequest::setIndividualTransportModes(std::vector<IndividualTransport> &&modes)
253{
254 d.detach();
255 d->individualTransportModes = std::move(modes);
256}
257
258QVariantList JourneyRequest::individualTransportModesVariant() const
259{
260 return toVariantList(d->individualTransportModes);
261}
262
263void JourneyRequest::setIndividualTransportModesVariant(const QVariantList &modes)
264{
265 d.detach();
266 d->individualTransportModes = IndividualTransport::fromVariant(modes);
267}
268
270{
272 hash.addData(QByteArray::number(d->dateTime.toSecsSinceEpoch() / JourneyCacheTimeResolution));
273 hash.addData(LocationUtil::cacheKey(d->from).toUtf8());
274 hash.addData(LocationUtil::cacheKey(d->to).toUtf8());
275 hash.addData(QByteArrayView(d->dateTimeMode == JourneyRequest::Arrival ? "A" : "D", 1));
276 hash.addData(QMetaEnum::fromType<JourneySection::Mode>().valueToKeys(d->modes));
277 hash.addData(QByteArray::number(d->maximumResults));
278 hash.addData(d->includeIntermediateStops ? "I" : "-");
279 hash.addData(d->includePaths ? "P" : "-");
280
281 hash.addData("ACCESS");
282 for (const auto &it : d->accessModes) {
283 hash.addData(QMetaEnum::fromType<IndividualTransport::Mode>().valueToKey(it.mode()));
284 hash.addData(QMetaEnum::fromType<IndividualTransport::Qualifier>().valueToKey(it.qualifier()));
285 }
286
287 hash.addData("EGRESS");
288 for (const auto &it : d->accessModes) {
289 hash.addData(QMetaEnum::fromType<IndividualTransport::Mode>().valueToKey(it.mode()));
290 hash.addData(QMetaEnum::fromType<IndividualTransport::Qualifier>().valueToKey(it.qualifier()));
291 }
292
293 hash.addData("MODES");
294 for (const auto &mode : d->lineModes) {
295 hash.addData(QMetaEnum::fromType<Line::Mode>().valueToKey(mode));
296 }
297
298 hash.addData("IVMODES");
299 for (const auto &it : d->individualTransportModes) {
300 hash.addData(QMetaEnum::fromType<IndividualTransport::Mode>().valueToKey(it.mode()));
301 hash.addData(QMetaEnum::fromType<IndividualTransport::Qualifier>().valueToKey(it.qualifier()));
302 }
303
304 return QString::fromUtf8(hash.result().toHex());
305}
306
307void JourneyRequest::validate() const
308{
309 // remove invalid access/egress modes
310 d->accessModes.erase(std::remove_if(d->accessModes.begin(), d->accessModes.end(), [](const auto &it) {
311 return (it.mode() == IndividualTransport::Car && it.qualifier() == IndividualTransport::None)
312 || it.qualifier() == IndividualTransport::Pickup;
313 }), d->accessModes.end());
314 d->egressModes.erase(std::remove_if(d->egressModes.begin(), d->egressModes.end(), [](const auto &it) {
315 return (it.mode() == IndividualTransport::Car && it.qualifier() == IndividualTransport::None)
316 || it.qualifier() == IndividualTransport::Dropoff
317 || it.qualifier() == IndividualTransport::Park;
318 }), d->egressModes.end());
319
320 // taking a bike on public transport needs to be symmetric
321 const auto hasTakeBikeAccess = hasTakeBikeMode(d->accessModes);
322 const auto hasTakeBikeEgress = hasTakeBikeMode(d->egressModes);
323 if (hasTakeBikeAccess && !hasTakeBikeEgress) {
324 d->egressModes.emplace_back(IndividualTransport::Bike);
325 } else if (!hasTakeBikeAccess && hasTakeBikeEgress) {
326 d->egressModes.erase(std::remove_if(d->egressModes.begin(), d->egressModes.end(), [](const auto &it) {
327 return it.mode() == IndividualTransport::Bike && it.qualifier() == IndividualTransport::None;
328 }), d->egressModes.end());
329 }
330
331 // access/egress modes must not be empty
332 if (d->accessModes.empty()) {
333 d->accessModes = {{IndividualTransport::Walk}};
334 }
335 if (d->egressModes.empty()) {
336 d->egressModes = {{IndividualTransport::Walk}};
337 }
338}
339
340#include "moc_journeyrequest.cpp"
static std::vector< IndividualTransport > fromVariant(const QVariantList &v)
static QJsonObject toJson(const IndividualTransport &it)
Serializes one object to JSON.
Describes a journey search.
void setIndividualTransportModes(std::vector< IndividualTransport > &&modes)
Sets individual transport modes considered for direct journeys.
QDateTime dateTime
Date/time at which the journey should start/end.
KPublicTransport::Location to
The journey destination.
void setArrivalTime(const QDateTime &dt)
Sets the desired arrival time.
void setDepartureTime(const QDateTime &dt)
Set the desired departure time.
bool requiresBikeTransport() const
Returns true if the specified access/egress modes require bike transportation on public transport.
void setAccessModes(std::vector< IndividualTransport > &&accessModes)
Sets the requested access modes.
QStringList backendIds() const
Identifiers of the backends that should be queried.
void setEgressModes(std::vector< IndividualTransport > &&egressModes)
Sets the requested egress modes.
@ Arrival
dateTime() represents the desired arriva time.
@ Departure
dateTime() represents the desired departure time.
bool isValid() const
Returns true if this is a valid request, that is, it has enough parameters set to perform a query.
QString cacheKey() const
Unique string representation used for caching results.
QVariantList egressModes
Egress modes.
KPublicTransport::Location from
The starting point of the journey search.
void setBackendIds(const QStringList &backendIds)
Set identifiers of backends that should be queried.
QVariantList accessModes
Access modes.
void setLineModes(std::vector< Line::Mode > &&modes)
Sets the requested line modes.
QVariantList individualTransportModes
Individual transport modes for direct connections.
KPublicTransport::JourneySection::Modes modes
Modes of transportation that should be considered for this query.
QVariantList lineModes
Line modes.
@ RentedVehicle
free floating or dock-based rental bike service, electric scooters, car sharing services,...
Definition journey.h:45
static QJsonObject toJson(const Location &loc)
Serializes one Location object to JSON.
Definition location.cpp:494
Query operations and data types for accessing realtime public transport information from online servi...
QByteArray number(double n, char format, int precision)
QByteArray toHex(char separator) const const
bool addData(QIODevice *device)
QByteArray result() const const
QDateTime currentDateTime()
QMetaEnum fromType()
QString fromUtf8(QByteArrayView str)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:07:52 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.