8#include "assetrepository_p.h"
9#include "backends/srbijavozbackend.h"
10#include "journeyreply.h"
11#include "journeyrequest.h"
12#include "requestcontext_p.h"
13#include "locationreply.h"
14#include "locationrequest.h"
16#include "stopoverreply.h"
17#include "stopoverrequest.h"
19#include "triprequest.h"
20#include "vehiclelayoutrequest.h"
21#include "vehiclelayoutreply.h"
22#include "datatypes/attributionutil_p.h"
23#include "datatypes/backend.h"
24#include "datatypes/backend_p.h"
25#include "datatypes/disruption.h"
26#include "datatypes/json_p.h"
27#include "geo/geojson_p.h"
29#include <KPublicTransport/Journey>
30#include <KPublicTransport/Location>
31#include <KPublicTransport/Stopover>
33#include "backends/accessibilitycloudbackend.h"
34#include "backends/cache.h"
35#include "backends/deutschebahnbackend.h"
36#include "backends/efabackend.h"
37#include "backends/hafasmgatebackend.h"
38#include "backends/hafasquerybackend.h"
39#include "backends/ivvassbackend.h"
40#include "backends/motis2backend.h"
41#include "backends/navitiabackend.h"
42#include "backends/oebbbackend.h"
43#include "backends/openjourneyplannerbackend.h"
44#include "backends/opentripplannergraphqlbackend.h"
45#include "backends/opentripplannerrestbackend.h"
46#include "backends/ltglinkbackend.h"
47#include "gbfs/gbfsbackend.h"
49#include <QCoreApplication>
50#include <QDirIterator>
52#include <QJsonDocument>
54#include <QMetaProperty>
55#include <QNetworkAccessManager>
56#include <QStandardPaths>
65static inline void initResources() {
66 Q_INIT_RESOURCE(asset_attributions);
67 Q_INIT_RESOURCE(gbfs);
68 Q_INIT_RESOURCE(geometry);
69 Q_INIT_RESOURCE(images);
70 Q_INIT_RESOURCE(networks);
71 Q_INIT_RESOURCE(network_certs);
73 Q_INIT_RESOURCE(stations);
79 [[nodiscard]] QNetworkAccessManager* nam();
81 [[nodiscard]] std::unique_ptr<AbstractBackend> loadNetwork(
const QJsonObject &obj);
82 template <
typename Backend,
typename Backend2,
typename ...Backends>
83 [[nodiscard]]
static std::unique_ptr<AbstractBackend> loadNetwork(
const QJsonObject &backendType,
const QJsonObject &obj);
84 template <
typename Backend>
85 [[nodiscard]]
static std::unique_ptr<AbstractBackend> loadNetwork(
const QJsonObject &backendType,
const QJsonObject &obj);
87 [[nodiscard]]
static std::unique_ptr<AbstractBackend> loadNetwork(
const QJsonObject &obj);
89 template <
typename RequestT>
90 [[nodiscard]]
bool shouldSkipBackend(
const Backend &backend,
const RequestT &req)
const;
92 void resolveLocation(LocationRequest &&locReq,
const AbstractBackend *backend,
const std::function<
void(
const Location &loc)> &callback);
93 [[nodiscard]]
bool queryJourney(
const AbstractBackend *backend,
const JourneyRequest &req, JourneyReply *reply);
94 [[nodiscard]]
bool queryStopover(
const AbstractBackend *backend,
const StopoverRequest &req, StopoverReply *reply);
96 template <
typename RepT,
typename ReqT>
97 [[nodiscard]] RepT* makeReply(
const ReqT &request);
99 void readCachedAttributions();
101 [[nodiscard]]
int queryLocationOnBackend(
const LocationRequest &req, LocationReply *reply,
const Backend &backend);
103 Manager *q =
nullptr;
104 QNetworkAccessManager *m_nam =
nullptr;
105 std::vector<Backend> m_backends;
106 std::vector<Attribution> m_attributions;
109 QStringList m_enabledBackends;
110 QStringList m_disabledBackends;
112 bool m_allowInsecure =
false;
113 bool m_hasReadCachedAttributions =
false;
114 bool m_backendsEnabledByDefault =
true;
117 [[nodiscard]]
bool shouldSkipBackend(
const Backend &backend)
const;
124 m_nam =
new QNetworkAccessManager(q);
126 m_nam->setStrictTransportSecurityEnabled(
true);
133void ManagerPrivate::loadNetworks()
135 if (!m_backends.empty()) {
139 QStringList searchDirs;
145 std::vector<Attribution> attributions;
146 for (
const auto &searchDir : searchDirs) {
147 QDirIterator it(searchDir +
"/org.kde.kpublictransport/networks"_L1, {u
"*.json"_s},
QDir::Files);
148 while (it.hasNext()) {
150 const auto id = it.fileInfo().baseName();
151 if (std::any_of(m_backends.begin(), m_backends.end(), [&
id](
const auto &backend) { return backend.identifier() == id; })) {
156 QFile f(it.filePath());
158 qCWarning(Log) <<
"Failed to open public transport network configuration:" << f.errorString();
162 QJsonParseError
error;
165 qCWarning(Log) <<
"Failed to parse public transport network configuration:" <<
error.errorString() << it.fileName();
169 auto net = loadNetwork(doc.object());
171 net->setBackendId(
id);
173 if (!net->attribution().isEmpty()) {
174 attributions.push_back(net->attribution());
177 auto b = BackendPrivate::fromJson(doc.object());
178 BackendPrivate::setImpl(b, std::move(net));
179 m_backends.push_back(std::move(b));
181 qCWarning(Log) <<
"Failed to load public transport network configuration config:" << it.fileName();
186 std::stable_sort(m_backends.begin(), m_backends.end(), [](
const auto &lhs,
const auto &rhs) {
187 return lhs.identifier() < rhs.identifier();
190 AttributionUtil::sort(attributions);
191 if (m_attributions.empty()) {
193 m_attributions = std::move(attributions);
196 AttributionUtil::merge(m_attributions, attributions);
199 qCDebug(Log) << m_backends.size() <<
"public transport network configurations loaded";
202std::unique_ptr<AbstractBackend> ManagerPrivate::loadNetwork(
const QJsonObject &obj)
208 OpenTripPlannerGraphQLBackend,
209 OpenTripPlannerRestBackend,
216 OpenJourneyPlannerBackend,
219 AccessibilityCloudBackend,
225template <
typename Backend,
typename Backend2,
typename ...Backends>
226std::unique_ptr<AbstractBackend> ManagerPrivate::loadNetwork(
const QJsonObject &backendType,
const QJsonObject &obj)
228 if (backendType.
value(QLatin1String(Backend::type())).toBool()) {
229 return loadNetwork<Backend>(obj);
231 return loadNetwork<Backend2, Backends...>(backendType, obj);
234template <
typename Backend>
235std::unique_ptr<AbstractBackend> ManagerPrivate::loadNetwork(
const QJsonObject &backendType,
const QJsonObject &obj)
237 if (backendType.
value(QLatin1String(Backend::type())).toBool()) {
238 return ManagerPrivate::loadNetwork<Backend>(obj);
240 qCWarning(Log) <<
"Unknown backend type:" << backendType;
244static void applyBackendOptions(AbstractBackend *backend,
const QMetaObject *mo,
const QJsonObject &obj)
247 for (
auto it = opts.begin(); it != opts.end(); ++it) {
250 qCWarning(Log) <<
"Unknown backend setting:" << it.key();
254 if (it.value().isObject()) {
255 mp.writeOnGadget(backend, it.value().toObject());
256 }
else if (it.value().isArray()) {
257 const auto a = it.value().toArray();
261 std::transform(a.begin(), a.end(), std::back_inserter(l), [](
const auto &v) { return v.toString(); });
262 mp.writeOnGadget(backend, l);
264 mp.writeOnGadget(backend, it.value().toArray());
267 mp.writeOnGadget(backend, it.value().toVariant());
273 backend->setAttribution(attr);
276 if (!tzId.isEmpty()) {
277 QTimeZone tz(tzId.toUtf8());
279 backend->setTimeZone(tz);
281 qCWarning(Log) <<
"Invalid timezone:" << tzId;
285 const auto langArray = obj.
value(
"supportedLanguages"_L1).
toArray();
287 langs.
reserve(langArray.size());
288 std::transform(langArray.begin(), langArray.end(), std::back_inserter(langs), [](
const auto &v) { return v.toString(); });
289 backend->setSupportedLanguages(langs);
292template<
typename T> std::unique_ptr<AbstractBackend> ManagerPrivate::loadNetwork(
const QJsonObject &obj)
294 std::unique_ptr<AbstractBackend> backend(
new T);
295 applyBackendOptions(backend.get(), &T::staticMetaObject, obj);
299bool ManagerPrivate::shouldSkipBackend(
const Backend &backend)
const
301 if (!backend.
isSecure() && !m_allowInsecure) {
302 qCDebug(Log) <<
"Skipping insecure backend:" << backend.
identifier();
305 return !q->isBackendEnabled(backend.
identifier());
308template <
typename RequestT>
309bool ManagerPrivate::shouldSkipBackend(
const Backend &backend,
const RequestT &req)
const
311 if (!req.backendIds().isEmpty() && !req.backendIds().contains(backend.
identifier())) {
315 return shouldSkipBackend(backend);
320void ManagerPrivate::resolveLocation(
LocationRequest &&locReq,
const AbstractBackend *backend,
const std::function<
void(
const Location&)> &callback)
323 locReq.setMaximumResults(1);
326 const auto cacheEntry = Cache::lookupLocation(backend->backendId(), locReq.cacheKey());
327 switch (cacheEntry.type) {
328 case CacheHitType::Negative:
331 case CacheHitType::Positive:
332 if (!cacheEntry.data.empty()) {
333 const auto loc = cacheEntry.data[0];
338 case CacheHitType::Miss:
343 auto locReply =
new LocationReply(locReq, q);
344 if (backend->queryLocation(locReq, locReply, nam())) {
345 locReply->setPendingOps(1);
347 locReply->setPendingOps(0);
350 locReply->deleteLater();
351 if (locReply->result().empty()) {
354 callback(locReply->result()[0]);
359static Location::Types locationTypesForJourneyRequest(
const JourneyRequest &req)
362 if (req.
modes() & JourneySection::PublicTransport) {
373 auto cache = Cache::lookupJourney(backend->backendId(), req.
cacheKey());
374 switch (cache.type) {
375 case CacheHitType::Negative:
376 qCDebug(Log) <<
"Negative cache hit for backend" << backend->backendId();
378 case CacheHitType::Positive:
379 qCDebug(Log) <<
"Positive cache hit for backend" << backend->backendId();
380 reply->addAttributions(std::move(cache.attributions));
381 reply->addResult(backend, std::move(cache.data));
383 case CacheHitType::Miss:
384 qCDebug(Log) <<
"Cache miss for backend" << backend->backendId();
389 if (backend->needsLocationQuery(req.
from(), AbstractBackend::QueryType::Journey)) {
390 LocationRequest fromReq(req.
from());
391 fromReq.setTypes(locationTypesForJourneyRequest(req));
392 resolveLocation(std::move(fromReq), backend, [reply, backend, req,
this](
const Location &loc) {
393 auto jnyRequest = req;
395 jnyRequest.setFrom(fromLoc);
397 if (backend->needsLocationQuery(jnyRequest.to(), AbstractBackend::QueryType::Journey)) {
398 LocationRequest toReq(jnyRequest.to());
399 toReq.setTypes(locationTypesForJourneyRequest(req));
400 resolveLocation(std::move(toReq), backend, [jnyRequest, reply, backend,
this](
const Location &loc) {
401 auto jnyReq = jnyRequest;
404 if (!backend->queryJourney(jnyReq, reply, nam())) {
412 if (!backend->queryJourney(jnyRequest, reply, nam())) {
420 if (backend->needsLocationQuery(req.
to(), AbstractBackend::QueryType::Journey)) {
421 LocationRequest toReq(req.
to());
422 toReq.setTypes(locationTypesForJourneyRequest(req));
423 resolveLocation(std::move(toReq), backend, [req, toReq, reply, backend,
this](
const Location &loc) {
425 auto jnyRequest = req;
426 jnyRequest.setTo(toLoc);
427 if (!backend->queryJourney(jnyRequest, reply, nam())) {
434 return backend->queryJourney(req, reply, nam());
439 auto cache = Cache::lookupStopover(backend->backendId(), req.
cacheKey());
440 switch (cache.type) {
441 case CacheHitType::Negative:
442 qCDebug(Log) <<
"Negative cache hit for backend" << backend->backendId();
444 case CacheHitType::Positive:
445 qCDebug(Log) <<
"Positive cache hit for backend" << backend->backendId();
446 reply->addAttributions(std::move(cache.attributions));
447 reply->addResult(backend, std::move(cache.data));
449 case CacheHitType::Miss:
450 qCDebug(Log) <<
"Cache miss for backend" << backend->backendId();
455 if (backend->needsLocationQuery(req.
stop(), AbstractBackend::QueryType::Departure)) {
456 qCDebug(Log) <<
"Backend needs location query first:" << backend->backendId();
457 LocationRequest locReq(req.
stop());
459 locReq.setMaximumDistance(250);
460 resolveLocation(std::move(locReq), backend, [reply, req, backend,
this](
const Location &loc) {
462 auto depRequest = req;
463 depRequest.setStop(depLoc);
464 if (!backend->queryStopover(depRequest, reply, nam())) {
471 return backend->queryStopover(req, reply, nam());
474void ManagerPrivate::readCachedAttributions()
476 if (m_hasReadCachedAttributions) {
480 Cache::allCachedAttributions(m_attributions);
481 m_hasReadCachedAttributions =
true;
484template<
typename RepT,
typename ReqT>
485RepT* ManagerPrivate::makeReply(
const ReqT &request)
487 auto reply =
new RepT(request, q);
489 AttributionUtil::merge(m_attributions, reply->
attributions());
496Manager::Manager(QObject *parent)
498 , d(new ManagerPrivate)
501 qRegisterMetaType<Disruption::Effect>();
504 if (!AssetRepository::instance()) {
505 auto assetRepo =
new AssetRepository(
this);
506 assetRepo->setNetworkAccessManagerProvider(std::bind(&ManagerPrivate::nam, d.get()));
514Manager::~Manager() =
default;
518 if (d->m_nam == nam) {
522 if (d->m_nam && d->m_nam->parent() ==
this) {
531 return d->m_allowInsecure;
536 if (d->m_allowInsecure == insecure) {
539 d->m_allowInsecure = insecure;
540 Q_EMIT configurationChanged();
552 reply->setPendingOps(pendingOps);
559 if (req.contexts().empty()) {
561 bool foundNonGlobalCoverage =
false;
562 for (
const auto coverageType : { CoverageArea::Realtime, CoverageArea::Regular, CoverageArea::Any }) {
563 const auto checkBackend = [&](
const Backend &backend,
bool bothLocationMatch) {
564 if (triedBackends.
contains(backend.
identifier()) || d->shouldSkipBackend(backend, req)) {
567 const auto coverage = backend.coverageArea(coverageType);
568 if (coverage.isEmpty()) {
572 if (bothLocationMatch) {
573 if (!coverage.coversLocation(req.
from()) || !coverage.coversLocation(req.
to())) {
577 if (!coverage.coversLocation(req.
from()) && !coverage.coversLocation(req.
to())) {
583 foundNonGlobalCoverage |= !coverage.isGlobal();
585 if (d->queryJourney(BackendPrivate::impl(backend), req, reply)) {
591 for (
const auto &backend: d->m_backends) {
592 checkBackend(backend,
true);
594 if (pendingOps && foundNonGlobalCoverage) {
599 for (
const auto &backend: d->m_backends) {
600 checkBackend(backend,
false);
602 if (pendingOps && foundNonGlobalCoverage) {
609 for (
const auto &context : req.contexts()) {
611 if ((context.type == RequestContext::Next && context.backend->hasCapability(AbstractBackend::CanQueryNextJourney))
612 ||(context.type == RequestContext::Previous && context.backend->hasCapability(AbstractBackend::CanQueryPreviousJourney)))
614 if (d->queryJourney(context.backend, req, reply)) {
624 if (d->queryJourney(context.backend, r, reply)) {
631 if (d->queryJourney(context.backend, r, reply)) {
640 reply->addAttributions(AssetRepository::instance()->
attributions());
647 reply->setPendingOps(pendingOps);
659 reply->setPendingOps(pendingOps);
666 if (req.contexts().empty()) {
668 bool foundNonGlobalCoverage =
false;
669 for (
const auto coverageType : { CoverageArea::Realtime, CoverageArea::Regular, CoverageArea::Any }) {
670 for (
const auto &backend: d->m_backends) {
671 if (triedBackends.
contains(backend.identifier()) || d->shouldSkipBackend(backend, req)) {
675 qCDebug(
Log) <<
"Skipping backend due to not supporting arrival queries:" << backend.identifier();
678 const auto coverage = backend.coverageArea(coverageType);
679 if (coverage.isEmpty() || !coverage.coversLocation(req.
stop())) {
682 triedBackends.
insert(backend.identifier());
683 foundNonGlobalCoverage |= !coverage.isGlobal();
685 if (d->queryStopover(BackendPrivate::impl(backend), req, reply)) {
690 if (pendingOps && foundNonGlobalCoverage) {
697 for (
const auto &context : req.contexts()) {
699 if ((context.type == RequestContext::Next && context.backend->hasCapability(AbstractBackend::CanQueryNextDeparture))
700 ||(context.type == RequestContext::Previous && context.backend->hasCapability(AbstractBackend::CanQueryPreviousDeparture)))
702 if (d->queryStopover(context.backend, req, reply)) {
709 if (context.type == RequestContext::Next) {
711 r.setDateTime(context.dateTime);
712 if (d->queryStopover(context.backend, r, reply)) {
721 reply->addAttributions(AssetRepository::instance()->
attributions());
728 reply->setPendingOps(pendingOps);
735 switch (cache.type) {
736 case CacheHitType::Negative:
737 qCDebug(
Log) <<
"Negative cache hit for backend" << backend.
identifier();
739 case CacheHitType::Positive:
740 qCDebug(
Log) <<
"Positive cache hit for backend" << backend.
identifier();
741 reply->addAttributions(std::move(cache.attributions));
742 reply->addResult(std::move(cache.data));
744 case CacheHitType::Miss:
745 qCDebug(
Log) <<
"Cache miss for backend" << backend.
identifier();
746 reply->addAttribution(BackendPrivate::impl(backend)->attribution());
747 if (BackendPrivate::impl(backend)->queryLocation(req, reply, nam())) {
764 reply->setPendingOps(pendingOps);
771 bool foundNonGlobalCoverage =
false;
774 for (
const auto coverageType : { CoverageArea::Realtime, CoverageArea::Regular, CoverageArea::Any }) {
776 for (
const auto &backend : d->m_backends) {
777 if (triedBackends.
contains(backend.
identifier()) || d->shouldSkipBackend(backend, req)) {
780 const auto coverage = backend.coverageArea(coverageType);
781 if (coverage.isEmpty() || !coverage.coversLocation(loc)) {
784 if (isCountryOnly && !coverage.hasNationWideCoverage(loc.
country())) {
789 foundNonGlobalCoverage |= !coverage.isGlobal();
790 pendingOps += d->queryLocationOnBackend(req, reply, backend);
792 if (pendingOps && foundNonGlobalCoverage) {
797 for (
const auto &backend : d->m_backends) {
798 if (triedBackends.
contains(backend.
identifier()) || d->shouldSkipBackend(backend, req)) {
801 const auto coverage = backend.coverageArea(coverageType);
802 if (coverage.isEmpty() || !coverage.coversLocation(loc)) {
807 foundNonGlobalCoverage |= !coverage.isGlobal();
808 pendingOps += d->queryLocationOnBackend(req, reply, backend);
810 if (pendingOps && foundNonGlobalCoverage) {
819 reply->setPendingOps(pendingOps);
830 auto route = req.
route();
831 auto line = route.
line();
841 auto reply = d->makeReply<
TripReply>(req);
847 reply->setPendingOps(pendingOps);
855 for (
const auto coverageType : { CoverageArea::Realtime, CoverageArea::Regular, CoverageArea::Any }) {
856 const auto checkBackend = [&](
const Backend &backend,
bool bothLocationMatch) {
857 if (triedBackends.
contains(backend.
identifier()) || d->shouldSkipBackend(backend, req)) {
860 const auto coverage = backend.coverageArea(coverageType);
861 if (coverage.isEmpty()) {
865 if (bothLocationMatch) {
877 if (BackendPrivate::impl(backend)->
queryTrip(req, reply, d->nam())) {
883 for (
const auto &backend: d->m_backends) {
884 checkBackend(backend,
true);
891 for (
const auto &backend: d->m_backends) {
892 checkBackend(backend,
false);
900 if (pendingOps == 0) {
907 jnyReq.setIncludeIntermediateStops(
true);
908 jnyReq.setIncludePaths(
true);
909 jnyReq.setModes(JourneySection::PublicTransport);
912 jnyReply->setParent(reply);
914 jnyReply->deleteLater();
916 reply->addError(jnyReply->error(), jnyReply->errorString());
919 for (
const auto &journey : jnyReply->result()) {
920 if (std::ranges::count_if(journey.sections(), [](
const auto &sec) { return sec.mode() == JourneySection::PublicTransport; }) != 1) {
923 const auto it = std::ranges::find_if(journey.sections(), [](
const auto &sec) {
924 return sec.mode() == JourneySection::PublicTransport;
926 assert(it != journey.sections().end());
927 qCDebug(
Log) <<
"Got journey information:" << (*it).route().line().name() << (*it).scheduledDepartureTime();
928 if (isSameTrip(reply->
request().journeySection(), *it)) {
929 qCDebug(
Log) <<
"Found journey information:" << (*it).route().line().name() << (*it).expectedDeparturePlatform() << (*it).expectedDepartureTime();
930 reply->addAttributions(jnyReply->attributions());
939 reply->setPendingOps(1);
941 reply->setPendingOps(pendingOps);
951 int negativeCacheHit = 0;
956 reply->setPendingOps(pendingOps);
962 for (
const auto coverageType : { CoverageArea::Realtime, CoverageArea::Regular }) {
963 for (
const auto &backend : d->m_backends) {
964 if (d->shouldSkipBackend(backend, req)) {
967 const auto coverage = backend.coverageArea(coverageType);
968 if (coverage.isEmpty() || !coverage.coversLocation(req.
stopover().
stopPoint())) {
971 reply->addAttribution(BackendPrivate::impl(backend)->attribution());
974 switch (cache.type) {
975 case CacheHitType::Negative:
977 qCDebug(
Log) <<
"Negative cache hit for backend" << backend.
identifier();
979 case CacheHitType::Positive:
980 qCDebug(
Log) <<
"Positive cache hit for backend" << backend.
identifier();
981 if (cache.data.size() == 1) {
982 reply->addAttributions(std::move(cache.attributions));
983 reply->addResult(cache.data[0]);
987 case CacheHitType::Miss:
988 qCDebug(
Log) <<
"Cache miss for backend" << backend.
identifier();
1000 if (pendingOps == 0 && negativeCacheHit == 0) {
1003 reply->setPendingOps(pendingOps);
1009 if (d->m_backends.empty()) {
1012 d->m_backends.clear();
1014 Q_EMIT backendsChanged();
1020 d->readCachedAttributions();
1021 return d->m_attributions;
1024QVariantList Manager::attributionsVariant()
const
1027 d->readCachedAttributions();
1029 l.
reserve(d->m_attributions.size());
1030 std::transform(d->m_attributions.begin(), d->m_attributions.end(), std::back_inserter(l), [](
const auto &attr) { return QVariant::fromValue(attr); });
1037 return d->m_backends;
1042 if (std::binary_search(d->m_disabledBackends.cbegin(), d->m_disabledBackends.cend(), backendId)) {
1045 if (std::binary_search(d->m_enabledBackends.cbegin(), d->m_enabledBackends.cend(), backendId)) {
1049 return d->m_backendsEnabledByDefault;
1054 const auto it = std::lower_bound(l.
begin(), l.
end(), value);
1055 if (it == l.
end() || (*it) != value) {
1060static void sortedRemove(QStringList &l,
const QString &value)
1062 const auto it = std::lower_bound(l.
begin(), l.
end(), value);
1063 if (it != l.
end() && (*it) == value) {
1071 sortedInsert(d->m_enabledBackends, backendId);
1072 sortedRemove(d->m_disabledBackends, backendId);
1074 sortedRemove(d->m_enabledBackends, backendId);
1075 sortedInsert(d->m_disabledBackends, backendId);
1077 Q_EMIT configurationChanged();
1082 return d->m_enabledBackends;
1088 for (
const auto &backendId : backendIds) {
1095 return d->m_disabledBackends;
1101 for (
const auto &backendId : backendIds) {
1108 return d->m_backendsEnabledByDefault;
1113 d->m_backendsEnabledByDefault = byDefault;
1115 Q_EMIT configurationChanged();
1118QVariantList Manager::backendsVariant()
const
1122 l.
reserve(d->m_backends.size());
1123 std::transform(d->m_backends.begin(), d->m_backends.end(), std::back_inserter(l), [](
const auto &b) { return QVariant::fromValue(b); });
1136#include "moc_manager.cpp"
static Attribution fromJson(const QJsonObject &obj)
Deserialize an Attribution object from JSON.
Information about a backend service queried for location/departure/journey data.
bool isSecure
Supports secrure network access.
QString identifier
Internal identifier of this backend.
Describes a journey search.
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 downloadAssets
Download graphic assets such as line logos for the data requested here.
@ 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.
KPublicTransport::Location from
The starting point of the journey search.
void setBackendIds(const QStringList &backendIds)
Set identifiers of backends that should be queried.
KPublicTransport::JourneySection::Modes modes
Modes of transportation that should be considered for this query.
DateTimeMode dateTimeMode
Controls whether to search for journeys starting or ending at the given time.
A segment of a journey plan.
static bool isSame(const JourneySection &lhs, const JourneySection &rhs)
Checks if two instances refer to the same journey section (which does not necessarily mean they are e...
KPublicTransport::Location from
Departure location of this segment.
QDateTime scheduledDepartureTime
Planned departure time.
@ RentedVehicle
free floating or dock-based rental bike service, electric scooters, car sharing services,...
KPublicTransport::Route route
Route to take on this segment.
KPublicTransport::Location to
Arrival location of this segment.
KPublicTransport::Line::Mode mode
Type of transport.
static bool modeIsRailBound(KPublicTransport::Line::Mode mode)
true if mode is bounds to rail tracks.
LocationRequest request() const
The request this is the reply for.
Describes a location search.
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.
KPublicTransport::Location location
Location object containing the search parameters.
QString region
Region (as in ISO 3166-2) of the location, if known.
@ RentedVehicleStation
a pick-up/drop-off point for dock-based rental bike/scooter systems
@ Place
a location that isn't of any specific type
@ Stop
a public transport stop (train station, bus stop, etc)
static Location merge(const Location &lhs, const Location &rhs)
Merge two departure instances.
QString country
Country of the location as ISO 3166-1 alpha 2 code, if known.
LocationReply * queryLocation(const LocationRequest &req) const
Query location information based on coordinates or (parts of) the name.
JourneyReply * queryJourney(const JourneyRequest &req) const
Query a journey.
void setBackendsEnabledByDefault(bool byDefault)
Set wheter backends are enabled by default.
void setEnabledBackends(const QStringList &backendIds)
Sets the explicitly enabled backends.
bool backendsEnabledByDefault
void setBackendEnabled(const QString &backendId, bool enabled)
Sets whether the backend with the given identifier should be used.
void reload()
Reload backend configuration.
StopoverReply * queryStopover(const StopoverRequest &req) const
Query arrivals or departures from a specific station.
QStringList disabledBackends
Q_INVOKABLE bool isBackendEnabled(const QString &backendId) const
Returns whether the use of the backend with a given identifier is enabled.
Q_INVOKABLE KPublicTransport::TripReply * queryTrip(const TripRequest &req) const
Query trip information.
void setDisabledBackends(const QStringList &backendIds)
Sets the explicitly disabled backends.
VehicleLayoutReply * queryVehicleLayout(const VehicleLayoutRequest &req) const
Query vehicle and platform layout information.
QVariantList backends
QML-compatible access to backends().
bool allowInsecureBackends
Allow usage of insecure backends (default: off).
void setNetworkAccessManager(QNetworkAccessManager *nam)
Set the network access manager to use for network operations.
void setAllowInsecureBackends(bool insecure)
Allow usage of insecure backends, that is services not using transport encryption.
QStringList enabledBackends
QVariantList attributions
QML-compatible access to attributions().
void finished()
Emitted whenever the corresponding search has been completed.
const std::vector< Attribution > & attributions() const
Returns the attributions for the provided data.
@ InvalidRequest
Incomplete or otherwise invalid request.
@ NoBackend
No backend was found to satisfy this request, e.g. due to no backend covering the requested area.
@ NoError
Nothing went wrong.
@ NotFoundError
The requested journey/departure/place could not be found.
KPublicTransport::Line line
Line this route belongs to.
Departure or arrival query reply.
Describes an arrival or departure search.
@ QueryArrival
Search for arrivals.
bool downloadAssets
Enable downloading of graphic assets such as line logos for the data requested here.
bool isValid() const
Returns true if this is a valid request, ie.
QString cacheKey() const
Unique string representation used for caching results.
KPublicTransport::Location stop
The location at which to search for departures/arrivals.
Mode mode
Controls whether to search for arrivals or departures.
KPublicTransport::Location stopPoint
The stop point of this departure.
Request for a single trip.
bool isValid() const
Returns true if this is a valid request, that is it has enough parameters set to perform a query.
QStringList backendIds
Identifiers of backends that should be queried.
KPublicTransport::JourneySection journeySection
A JourneySection for which the full trip is requested.
Reply to a vehicle layout query.
Describes a query for vehicle layout information.
QString cacheKey() const
Unique string representation used for caching results.
bool isValid() const
Returns true if this is a valid request, that is it has enough parameters set to perform a query.
KPublicTransport::Stopover stopover
The stopover vehicle and platform layout information are requested for.
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
Query operations and data types for accessing realtime public transport information from online servi...
QCoreApplication * instance()
QDateTime addSecs(qint64 s) const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonValue value(QLatin1StringView key) const const
QJsonArray toArray() const const
QJsonObject toObject() const const
QString toString() const const
iterator erase(const_iterator begin, const_iterator end)
iterator insert(const_iterator before, parameter_type value)
void push_back(parameter_type value)
void reserve(qsizetype size)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual bool event(QEvent *e)
virtual bool eventFilter(QObject *watched, QEvent *event)
void installEventFilter(QObject *filterObj)
bool contains(const QSet< T > &other) const const
iterator insert(const T &value)
QStringList standardLocations(StandardLocation type)
QString writableLocation(StandardLocation type)
bool isEmpty() const const