Commit af73d8f5 authored by Volker Krause's avatar Volker Krause
Browse files

Allow to specify access and egress modes in journey requests

That is, which individual transport options should be considered on the
first (access) and last (egress) leg of a journey. So far we always
implicitly assumed walking. Scenarios that are now also possible are e.g.
taking your bike and taking that with you on the train, or taking your car
to the station (and needing a place to park it there). Routing engines can
take all that into considerations, if we ask them to.

This is modeled based on OTP v2/Entur v3) but should cover the capabilities
of other engines as well.

This is only the request API so far though, support in any of the backends
is still missing, as is support for this in the result types.
parent 6d51029a
......@@ -59,6 +59,7 @@ target_sources(KPublicTransport PRIVATE
datatypes/disruption.cpp
datatypes/equipment.cpp
datatypes/equipmentutil.cpp
datatypes/individualtransport.cpp
datatypes/journey.cpp
datatypes/journeyutil.cpp
datatypes/json.cpp
......@@ -78,6 +79,7 @@ target_sources(KPublicTransport PRIVATE
datatypes/stopover.cpp
datatypes/stopoverutil.cpp
datatypes/timeutil.cpp
datatypes/variant.cpp
datatypes/vehicle.cpp
gbfs/gbfs.cpp
......@@ -172,6 +174,7 @@ ecm_generate_headers(KPublicTransport_Datatypes_FORWARDING_HEADERS
Departure
Disruption
Equipment
IndividualTransport
Journey
Line
Load
......
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "individualtransport.h"
#include "json_p.h"
#include "datatypes_p.h"
#include "variant_p.h"
#include <QDebug>
namespace KPublicTransport {
class IndividualTransportPrivate : public QSharedData
{
public:
IndividualTransport::Mode mode = IndividualTransport::Walk;
IndividualTransport::Qualifier qualifier = IndividualTransport::None;
};
}
using namespace KPublicTransport;
KPUBLICTRANSPORT_MAKE_GADGET(IndividualTransport)
KPUBLICTRANSPORT_MAKE_PROPERTY(IndividualTransport, IndividualTransport::Mode, mode, setMode)
KPUBLICTRANSPORT_MAKE_PROPERTY(IndividualTransport, IndividualTransport::Qualifier, qualifier, setQualifier)
IndividualTransport::IndividualTransport(IndividualTransport::Mode mode, IndividualTransport::Qualifier qualifier)
: d(new IndividualTransportPrivate)
{
d->mode = mode;
d->qualifier = qualifier;
}
bool IndividualTransport::operator==(const IndividualTransport &other) const
{
return d->mode == other.mode() && d->qualifier == other.qualifier();
}
QJsonObject IndividualTransport::toJson(const IndividualTransport &it)
{
return Json::toJson(it);
}
QJsonArray IndividualTransport::toJson(const std::vector<IndividualTransport> &its)
{
return Json::toJson(its);
}
IndividualTransport IndividualTransport::fromJson(const QJsonObject &obj)
{
return Json::fromJson<IndividualTransport>(obj);
}
std::vector<IndividualTransport> IndividualTransport::fromJson(const QJsonArray &array)
{
return Json::fromJson<IndividualTransport>(array);
}
std::vector<IndividualTransport> IndividualTransport::fromVariant(const QVariantList &v)
{
return Variant::fromVariantList<IndividualTransport>(v);
}
#include "moc_individualtransport.cpp"
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KPUBLICTRANSPORT_INDIVIDUALTRANSPORT_H
#define KPUBLICTRANSPORT_INDIVIDUALTRANSPORT_H
#include "kpublictransport_export.h"
#include "datatypes.h"
namespace KPublicTransport {
class IndividualTransportPrivate;
/** Individual transport mode details for a journey section, and for specifying journey requests. */
class KPUBLICTRANSPORT_EXPORT IndividualTransport
{
KPUBLICTRANSPORT_GADGET(IndividualTransport)
public:
/** Mode of (individual) transportation. */
enum Mode {
Walk,
Bike,
Car
};
Q_ENUM(Mode)
KPUBLICTRANSPORT_PROPERTY(Mode, mode, setMode)
public:
/** Qualifier on how the mode of transport is used. */
enum Qualifier {
None, ///< not applicable, or bike is taken on public transport legs.
Park, ///< for access legs, vehicle is parked before taking public transport.
Rent, ///< use a rental vehicle
Dropoff, ///< for access legs: vehicle is not taken onto public transport but also doesn't need parking
Pickup, ///< for egress legs: vehicle is available at the last public transport leg
};
Q_ENUM(Qualifier)
KPUBLICTRANSPORT_PROPERTY(Qualifier, qualifier, setQualifier)
public:
IndividualTransport(Mode mode, Qualifier qualifier = None);
bool operator==(const IndividualTransport &other) const;
/** Serializes one object to JSON. */
static QJsonObject toJson(const IndividualTransport &it);
/** Serializes a vector of objects to JSON. */
static QJsonArray toJson(const std::vector<IndividualTransport> &its);
/** Deserialize an object from JSON. */
static IndividualTransport fromJson(const QJsonObject &obj);
/** Deserialize a list of journey from JSON. */
static std::vector<IndividualTransport> fromJson(const QJsonArray &array);
/** @internal for QML bindings only */
static std::vector<IndividualTransport> fromVariant(const QVariantList &v);
};
}
Q_DECLARE_METATYPE(KPublicTransport::IndividualTransport)
#endif // KPUBLICTRANSPORT_INDIVIDUALTRANSPORT_H
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "variant_p.h"
#include <QMetaObject>
#include <QMetaProperty>
using namespace KPublicTransport;
void Variant::fromVariantMap(const QMetaObject *mo, const QVariantMap &v, void *obj)
{
for (auto it = v.begin(); it != v.end(); ++it) {
const auto idx = mo->indexOfProperty(it.key().toUtf8().constData());
if (idx < 0) {
continue;
}
const auto prop = mo->property(idx);
if (!prop.isStored()) {
continue;
}
prop.writeOnGadget(obj, it.value());
}
}
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef KPUBLICTRANSPORT_VARIANT_H
#define KPUBLICTRANSPORT_VARIANT_H
#include <QVariant>
#include <vector>
class QMetaObject;
namespace KPublicTransport {
/** Deserialization from variant maps, for QML bindings. */
namespace Variant
{
void fromVariantMap(const QMetaObject *mo, const QVariantMap &v, void *obj);
/** Deserialize variant map into an actual object based on meta-type information. */
template <typename T>
inline T fromVariant(const QVariant &v)
{
if (qMetaTypeId<T>() == v.userType()) {
return v.value<T>();
}
if (v.canConvert<QVariantMap>()) {
T obj;
fromVariantMap(&T::staticMetaObject, v.toMap(), &obj);
return obj;
}
return {};
}
/** Deserialize a list of variants. */
template <typename T>
inline std::vector<T> fromVariantList(const QVariantList &l)
{
std::vector<T> out;
out.reserve(l.size());
std::transform(l.begin(), l.end(), std::back_inserter(out), &Variant::fromVariant<T>);
return out;
}
}
}
#endif // KPUBLICTRANSPORT_VARIANT_H
......@@ -36,6 +36,9 @@ public:
bool downloadAssets = false;
bool includeIntermediateStops = true;
bool includePaths = false;
std::vector<IndividualTransport> accessModes = { {IndividualTransport::Walk} };
std::vector<IndividualTransport> egressModes = { {IndividualTransport::Walk} };
};
}
......@@ -125,8 +128,10 @@ void JourneyRequest::purgeLoops(const JourneyRequest &baseRequest)
QJsonObject JourneyRequest::toJson(const KPublicTransport::JourneyRequest &req)
{
auto obj = Json::toJson(req);
obj.insert(QStringLiteral("from"), Location::toJson(req.from()));
obj.insert(QStringLiteral("to"), Location::toJson(req.to()));
obj.insert(QLatin1String("from"), Location::toJson(req.from()));
obj.insert(QLatin1String("to"), Location::toJson(req.to()));
obj.insert(QLatin1String("accessModes"), IndividualTransport::toJson(req.accessModes()));
obj.insert(QLatin1String("egressModes"), IndividualTransport::toJson(req.egressModes()));
return obj;
}
......@@ -141,6 +146,59 @@ void JourneyRequest::setBackendIds(const QStringList &backendIds)
d->backendIds = backendIds;
}
template <typename T>
static QVariantList toVariantList(const std::vector<T> &v)
{
QVariantList l;
l.reserve(v.size());
std::transform(v.begin(), v.end(), std::back_inserter(l), &QVariant::fromValue<T>);
return l;
}
const std::vector<IndividualTransport>& JourneyRequest::accessModes() const
{
return d->accessModes;
}
QVariantList JourneyRequest::accessModesVariant() const
{
return toVariantList(d->accessModes);
}
void JourneyRequest::setAccessModes(std::vector<IndividualTransport> &&accessModes)
{
d.detach();
d->accessModes = std::move(accessModes);
}
void JourneyRequest::setAccessModes(const QVariantList &accessModesVariant)
{
d.detach();
d->accessModes = IndividualTransport::fromVariant(accessModesVariant);
}
const std::vector<IndividualTransport>& JourneyRequest::egressModes() const
{
return d->egressModes;
}
QVariantList JourneyRequest::egressModesVariant() const
{
return toVariantList(d->egressModes);
}
void JourneyRequest::setEgressModes(std::vector<IndividualTransport>&& egressModes)
{
d.detach();
d->egressModes = std::move(egressModes);
}
void JourneyRequest::setEgressModes(const QVariantList &egressModesVariant)
{
d.detach();
d->egressModes = IndividualTransport::fromVariant(egressModesVariant);
}
QString JourneyRequest::cacheKey() const
{
return QString::number(d->dateTime.toSecsSinceEpoch() / JourneyCacheTimeResolution) + QLatin1Char('_')
......
......@@ -10,10 +10,12 @@
#include "kpublictransport_export.h"
#include <KPublicTransport/Datatypes>
#include <KPublicTransport/IndividualTransport>
#include <KPublicTransport/Journey>
#include <QMetaType>
#include <QSharedDataPointer>
#include <QVariant>
#include <vector>
......@@ -63,6 +65,11 @@ class KPUBLICTRANSPORT_EXPORT JourneyRequest
*/
KPUBLICTRANSPORT_PROPERTY(bool, includePaths, setIncludePaths)
/** Access modes. */
Q_PROPERTY(QVariantList accessModes READ accessModesVariant WRITE setAccessModes)
/** Egress modes. */
Q_PROPERTY(QVariantList egressModes READ egressModesVariant WRITE setEgressModes)
public:
enum DateTimeMode {
Arrival, ///< dateTime() represents the desired arriva time.
......@@ -105,6 +112,22 @@ public:
*/
void setBackendIds(const QStringList &backendIds);
/** Requested access modes.
* That is individual transport modes on the first (access) leg of the journey.
* Default: walking
*/
const std::vector<IndividualTransport>& accessModes() const;
/** Sets the requested access modes. */
void setAccessModes(std::vector<IndividualTransport> &&accessModes);
/** Requested egress modes.
* That is, individual transport modes for the last (egress) leg of the journey.
* Default: walking
*/
const std::vector<IndividualTransport>& egressModes() const;
/** Sets the requested egress modes. */
void setEgressModes(std::vector<IndividualTransport> &&egressModes);
/** Unique string representation used for caching results. */
QString cacheKey() const;
......@@ -116,6 +139,12 @@ private:
friend class JourneyReply;
friend class JourneyReplyPrivate;
friend class Manager;
Q_DECL_HIDDEN QVariantList accessModesVariant() const;
Q_DECL_HIDDEN void setAccessModes(const QVariantList &accessModesVariant);
Q_DECL_HIDDEN QVariantList egressModesVariant() const;
Q_DECL_HIDDEN void setEgressModes(const QVariantList &egressModesVariant);
Q_DECL_HIDDEN RequestContext context(const AbstractBackend *backend) const;
Q_DECL_HIDDEN const std::vector<RequestContext>& contexts() const;
Q_DECL_HIDDEN void setContext(const AbstractBackend *backend, RequestContext &&context);
......
......@@ -12,6 +12,7 @@
#include <KPublicTransport/BackendModel>
#include <KPublicTransport/Equipment>
#include <KPublicTransport/IndividualTransport>
#include <KPublicTransport/Journey>
#include <KPublicTransport/JourneyQueryModel>
#include <KPublicTransport/JourneyRequest>
......@@ -38,6 +39,7 @@ void KPublicTransportQmlPlugin::registerTypes(const char*)
qRegisterMetaType<KPublicTransport::Platform>();
qRegisterMetaType<KPublicTransport::RentalVehicle>();
qRegisterMetaType<KPublicTransport::Stopover>();
qRegisterMetaType<KPublicTransport::IndividualTransport>();
// backward compat
qRegisterMetaType<KPublicTransport::StopoverRequest>("KPublicTransport::DepartureRequest");
......@@ -58,6 +60,7 @@ void KPublicTransportQmlPlugin::registerTypes(const char*)
qmlRegisterUncreatableType<KPublicTransport::LocationRequest>("org.kde.kpublictransport", 1, 0, "LocationRequest", {});
qmlRegisterUncreatableType<KPublicTransport::StopoverRequest>("org.kde.kpublictransport", 1, 0, "StopoverRequest", {});
qmlRegisterUncreatableType<KPublicTransport::VehicleLayoutRequest>("org.kde.kpublictransport", 1, 0, "VehicleLayoutRequery", {});
qmlRegisterUncreatableType<KPublicTransport::IndividualTransport>("org.kde.kpublictransport", 1, 0, "IndividualTransport", {});
// backward compat
qmlRegisterUncreatableType<KPublicTransport::StopoverRequest>("org.kde.kpublictransport", 1, 0, "DepartureRequest", {});
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment