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
21#include <unordered_map>
22
23using namespace KPublicTransport;
24
25enum { JourneyCacheTimeResolution = 60 }; // in seconds
26
27namespace KPublicTransport {
28class JourneyRequestPrivate : public QSharedData {
29public:
30 Location from;
31 Location to;
32 QDateTime dateTime;
33 std::vector<RequestContext> contexts;
34 QStringList backendIds;
36 JourneySection::Modes modes = JourneySection::PublicTransport | JourneySection::RentedVehicle;
37 int maximumResults = 12;
38 bool downloadAssets = false;
39 bool includeIntermediateStops = true;
40 bool includePaths = false;
41
42 std::vector<IndividualTransport> accessModes = { {IndividualTransport::Walk} };
43 std::vector<IndividualTransport> egressModes = { {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(QLatin1String("from"), Location::toJson(req.from()));
135 obj.insert(QLatin1String("to"), Location::toJson(req.to()));
136 obj.insert(QLatin1String("accessModes"), IndividualTransport::toJson(req.accessModes()));
137 obj.insert(QLatin1String("egressModes"), IndividualTransport::toJson(req.egressModes()));
138 return obj;
139}
140
142{
143 return d->backendIds;
144}
145
147{
148 d.detach();
149 d->backendIds = backendIds;
150}
151
152template <typename T>
153static QVariantList toVariantList(const std::vector<T> &v)
154{
155 QVariantList l;
156 l.reserve(v.size());
157 std::transform(v.begin(), v.end(), std::back_inserter(l), [](const T &value) {
158 return QVariant::fromValue<T>(value);
159 });
160 return l;
161}
162
163const std::vector<IndividualTransport>& JourneyRequest::accessModes() const
164{
165 return d->accessModes;
166}
167
168QVariantList JourneyRequest::accessModesVariant() const
169{
170 return toVariantList(d->accessModes);
171}
172
173void JourneyRequest::setAccessModes(std::vector<IndividualTransport> &&accessModes)
174{
175 d.detach();
176 d->accessModes = std::move(accessModes);
177}
178
179void JourneyRequest::setAccessModesVariant(const QVariantList &accessModesVariant)
180{
181 d.detach();
182 d->accessModes = IndividualTransport::fromVariant(accessModesVariant);
183}
184
185const std::vector<IndividualTransport>& JourneyRequest::egressModes() const
186{
187 return d->egressModes;
188}
189
190QVariantList JourneyRequest::egressModesVariant() const
191{
192 return toVariantList(d->egressModes);
193}
194
195void JourneyRequest::setEgressModes(std::vector<IndividualTransport>&& egressModes)
196{
197 d.detach();
198 d->egressModes = std::move(egressModes);
199}
200
201void JourneyRequest::setEgressModesVariant(const QVariantList &egressModesVariant)
202{
203 d.detach();
204 d->egressModes = IndividualTransport::fromVariant(egressModesVariant);
205}
206
207const std::vector<Line::Mode>& JourneyRequest::lineModes() const
208{
209 return d->lineModes;
210}
211
212void JourneyRequest::setLineModes(std::vector<Line::Mode> &&lineModes)
213{
214 d.detach();
215 d->lineModes = std::move(lineModes);
216 std::sort(d->lineModes.begin(), d->lineModes.end());
217 d->lineModes.erase(std::unique(d->lineModes.begin(), d->lineModes.end()), d->lineModes.end());
218}
219
220QVariantList JourneyRequest::lineModesVariant() const
221{
222 return toVariantList(d->lineModes);
223}
224
225void JourneyRequest::setLineModesVariant(const QVariantList &lineModes)
226{
227 auto l = std::move(d->lineModes);
228 l.clear();
229 l.reserve(lineModes.size());
230 std::transform(lineModes.begin(), lineModes.end(), std::back_inserter(l), [](const auto &mode) { return static_cast<Line::Mode>(mode.toInt()); });
231 setLineModes(std::move(l));
232}
233
235{
237 hash.addData(QByteArray::number(d->dateTime.toSecsSinceEpoch() / JourneyCacheTimeResolution));
238 hash.addData(LocationUtil::cacheKey(d->from).toUtf8());
239 hash.addData(LocationUtil::cacheKey(d->to).toUtf8());
240 hash.addData(QByteArrayView(d->dateTimeMode == JourneyRequest::Arrival ? "A" : "D", 1));
241 hash.addData(QMetaEnum::fromType<JourneySection::Mode>().valueToKeys(d->modes));
242 hash.addData(QByteArray::number(d->maximumResults));
243 hash.addData(d->includeIntermediateStops ? "I" : "-");
244 hash.addData(d->includePaths ? "P" : "-");
245
246 hash.addData("ACCESS");
247 for (const auto &it : d->accessModes) {
248 hash.addData(QMetaEnum::fromType<IndividualTransport::Mode>().valueToKey(it.mode()));
249 hash.addData(QMetaEnum::fromType<IndividualTransport::Qualifier>().valueToKey(it.qualifier()));
250 }
251
252 hash.addData("EGRESS");
253 for (const auto &it : d->accessModes) {
254 hash.addData(QMetaEnum::fromType<IndividualTransport::Mode>().valueToKey(it.mode()));
255 hash.addData(QMetaEnum::fromType<IndividualTransport::Qualifier>().valueToKey(it.qualifier()));
256 }
257
258 hash.addData("MODES");
259 for (const auto &mode : d->lineModes) {
260 hash.addData(QMetaEnum::fromType<Line::Mode>().valueToKey(mode));
261 }
262
263 return QString::fromUtf8(hash.result().toHex());
264}
265
266static bool hasTakeBikeMode(const std::vector<IndividualTransport> &modes)
267{
268 return std::any_of(modes.begin(), modes.end(), [](const auto &it) {
269 return it.mode() == IndividualTransport::Bike && it.qualifier() == IndividualTransport::None;
270 });
271}
272
273void JourneyRequest::validate() const
274{
275 // remove invalid access/egress modes
276 d->accessModes.erase(std::remove_if(d->accessModes.begin(), d->accessModes.end(), [](const auto &it) {
277 return (it.mode() == IndividualTransport::Car && it.qualifier() == IndividualTransport::None)
278 || it.qualifier() == IndividualTransport::Pickup;
279 }), d->accessModes.end());
280 d->egressModes.erase(std::remove_if(d->egressModes.begin(), d->egressModes.end(), [](const auto &it) {
281 return (it.mode() == IndividualTransport::Car && it.qualifier() == IndividualTransport::None)
282 || it.qualifier() == IndividualTransport::Dropoff
283 || it.qualifier() == IndividualTransport::Park;
284 }), d->egressModes.end());
285
286 // taking a bike on public transport needs to be symmetric
287 const auto hasTakeBikeAccess = hasTakeBikeMode(d->accessModes);
288 const auto hasTakeBikeEgress = hasTakeBikeMode(d->egressModes);
289 if (hasTakeBikeAccess && !hasTakeBikeEgress) {
290 d->egressModes.push_back({ IndividualTransport::Bike });
291 } else if (!hasTakeBikeAccess && hasTakeBikeEgress) {
292 d->egressModes.erase(std::remove_if(d->egressModes.begin(), d->egressModes.end(), [](const auto &it) {
293 return it.mode() == IndividualTransport::Bike && it.qualifier() == IndividualTransport::None;
294 }), d->egressModes.end());
295 }
296
297 // access/egress modes must not be empty
298 if (d->accessModes.empty()) {
299 d->accessModes = {{IndividualTransport::Walk}};
300 }
301 if (d->egressModes.empty()) {
302 d->egressModes = {{IndividualTransport::Walk}};
303 }
304}
305
306#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.
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.
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 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:456
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()
QString fromUtf8(QByteArrayView str)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 17 2024 11:47:54 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.