7#include "journeyreply.h"
9#include "journeyrequest.h"
10#include "requestcontext_p.h"
12#include "backends/abstractbackend.h"
13#include "backends/cache.h"
14#include "datatypes/journeyutil_p.h"
15#include "geo/pathfilter_p.h"
17#include <KPublicTransport/Journey>
18#include <KPublicTransport/Location>
28constexpr inline const auto MINIMUM_WAIT_TIME = 60;
29constexpr inline const auto MINIMUM_WALK_TIME = 90;
30constexpr inline const auto MINIMUM_WALK_DISTANCE = 50;
32constexpr inline const auto MAXIMUM_TRANSFER_SPEED = 30;
33constexpr inline const auto MAXIMUM_TRANSFER_DISTANCE = 100000;
35class JourneyReplyPrivate :
public ReplyPrivate {
37 void finalizeResult()
override;
38 bool needToWaitForAssets()
const override;
39 static void postProcessJourneys(std::vector<Journey> &journeys);
41 JourneyRequest request;
42 JourneyRequest nextRequest;
43 JourneyRequest prevRequest;
44 std::vector<Journey> journeys;
48void JourneyReplyPrivate::finalizeResult()
50 if (journeys.empty()) {
58 std::sort(journeys.begin(), journeys.end(), JourneyUtil::firstTransportDepartureLessThan);
59 for (
auto it = journeys.begin(); it != journeys.end(); ++it) {
60 for (
auto mergeIt = it + 1; mergeIt != journeys.end();) {
61 if (!JourneyUtil::firstTransportDepartureEqual(*it, *mergeIt)) {
67 mergeIt = journeys.erase(mergeIt);
75 std::sort(journeys.begin(), journeys.end(), [](
const auto &lhs,
const auto &rhs) {
76 return lhs.scheduledDepartureTime() < rhs.scheduledDepartureTime();
79 nextRequest.purgeLoops(request);
80 prevRequest.purgeLoops(request);
83bool JourneyReplyPrivate::needToWaitForAssets()
const
85 return request.downloadAssets();
102 if (section.
mode() == JourneySection::Waiting) {
103 return section.
duration() < MINIMUM_WAIT_TIME;
105 if (section.
mode() == JourneySection::Walking && !hasNonTrivialPath(section)) {
106 return section.
duration() < MINIMUM_WALK_TIME || (section.
distance() > 0 && section.
distance() < MINIMUM_WALK_DISTANCE);
113 if ((section.
mode() == JourneySection::Transfer || section.
mode() == JourneySection::Walking)
114 && section.
from().hasCoordinate() && section.
to().hasCoordinate())
117 if (section.
duration() > 0 && (distance / (
float)section.
duration()) > MAXIMUM_TRANSFER_SPEED) {
118 qCDebug(Log) <<
"discarding journey based on insane transfer/walking speed:" << (
distance / (float)section.
duration()) <<
"m/s";
121 if (distance > MAXIMUM_TRANSFER_DISTANCE) {
122 qCDebug(Log) <<
"discarding journey with insane transfer/walking distance:" <<
distance <<
"m" << section.
from().
name() << section.
to().
name();
129void JourneyReplyPrivate::postProcessJourneys(std::vector<Journey> &journeys)
132 for (
auto &journey : journeys) {
133 JourneyUtil::propagateTimeZones(journey);
134 auto sections = journey.takeSections();
135 for (
auto §ion : sections) {
136 if (section.
mode() == JourneySection::Walking) {
138 auto from = section.
from();
140 section.setFrom(from);
143 section.setScheduledDepartureTime(dt);
146 auto to = section.
to();
151 section.setScheduledArrivalTime(dt);
155 journey.setSections(std::move(sections));
159 for (
auto &journey : journeys) {
160 auto sections = journey.takeSections();
163 for (
auto it = sections.begin(); it != sections.end();) {
164 if (it == sections.begin()) {
168 auto prevIt = it - 1;
169 if ((*it).mode() == JourneySection::Walking && (*prevIt).mode() == JourneySection::Walking) {
170 (*prevIt).setTo((*it).to());
171 (*prevIt).setScheduledArrivalTime((*it).scheduledArrivalTime());
172 (*prevIt).setExpectedArrivalTime((*it).expectedArrivalTime());
173 (*prevIt).setDistance((*prevIt).distance() + (*it).distance());
174 it = sections.erase(it);
182 sections.erase(std::remove_if(sections.begin(), sections.end(), isPointlessSection), sections.end());
184 for (
auto §ion : sections) {
185 JourneyUtil::postProcessPath(section);
188 journey.setSections(std::move(sections));
192 journeys.erase(std::remove_if(journeys.begin(), journeys.end(), [](
const auto &journey) {
193 return journey.sections().empty() || std::any_of(journey.sections().begin(), journey.sections().end(), isImplausibleSection);
197JourneyReply::JourneyReply(
const JourneyRequest &req, QObject *parent)
198 :
Reply(new JourneyReplyPrivate, parent)
202 d->nextRequest = req;
203 d->prevRequest = req;
206JourneyReply::~JourneyReply() =
default;
210 Q_D(
const JourneyReply);
216 Q_D(
const JourneyReply);
223 return std::move(d->journeys);
228 Q_D(
const JourneyReply);
229 if (d->nextRequest.contexts().empty()) {
232 return d->nextRequest;
237 Q_D(
const JourneyReply);
238 if (d->prevRequest.contexts().empty()) {
241 return d->prevRequest;
244void JourneyReply::addResult(
const AbstractBackend *backend, std::vector<Journey> &&res)
247 d->postProcessJourneys(res);
253 auto context = d->nextRequest.context(backend);
254 context.type = RequestContext::Next;
255 for (
const auto &jny : res) {
256 context.dateTime = std::max(context.dateTime, jny.scheduledDepartureTime());
258 d->nextRequest.setContext(backend, std::move(context));
260 context = d->prevRequest.context(backend);
261 context.type = RequestContext::Previous;
262 context.dateTime = res[0].scheduledArrivalTime();
263 for (
const auto &jny : res) {
264 context.dateTime = std::min(context.dateTime, jny.scheduledArrivalTime());
266 d->prevRequest.setContext(backend, std::move(context));
270 if (backend->timeZone().isValid()) {
271 for (
auto &jny : res) {
272 JourneyUtil::applyTimeZone(jny, backend->timeZone());
277 for (
auto &jny : res) {
278 jny.applyMetaData(
request().downloadAssets());
283 Cache::addNegativeJourneyCacheEntry(backend->backendId(),
request().cacheKey());
287 addAttribution(backend->attribution());
291 if (d->journeys.empty()) {
292 d->journeys = std::move(res);
294 d->journeys.insert(d->journeys.end(), res.begin(), res.end());
296 d->emitUpdated(
this);
300 d->emitFinishedIfDone(
this);
303void JourneyReply::setNextContext(
const AbstractBackend *backend,
const QVariant &data)
306 auto context = d->nextRequest.context(backend);
307 context.type = RequestContext::Next;
308 context.backendData = data;
309 d->nextRequest.setContext(backend, std::move(context));
312void JourneyReply::setPreviousContext(
const AbstractBackend *backend,
const QVariant &data)
315 auto context = d->prevRequest.context(backend);
316 context.type = RequestContext::Previous;
317 context.backendData = data;
318 d->prevRequest.setContext(backend, std::move(context));
321void JourneyReply::addError(
const AbstractBackend *backend,
Reply::Error error,
const QString &errorMsg)
324 Cache::addNegativeJourneyCacheEntry(backend->backendId(),
request().cacheKey());
326 qCDebug(Log) << backend->backendId() << error << errorMsg;
328 Reply::addError(error, errorMsg);
331#include "moc_journeyreply.cpp"
const std::vector< Journey > & result() const
Returns the retrieved journeys.
JourneyRequest nextRequest() const
Returns a request object for querying journeys following the ones returned by this reply.
std::vector< Journey > && takeResult()
Returns the retrieved journeys for moving elsewhere.
JourneyRequest previousRequest() const
Returns a request object for querying journeys preceding the ones returned by this reply.
JourneyRequest request() const
The request this is the reply for.
Describes a journey search.
@ Departure
dateTime() represents the desired departure time.
A segment of a journey plan.
KPublicTransport::Path path
Movement path for this journey section.
KPublicTransport::Location from
Departure location of this segment.
QDateTime scheduledDepartureTime
Planned departure time.
KPublicTransport::Location to
Arrival location of this segment.
int duration
Duration of the section in seconds.
Mode mode
Mode of transport for this section.
QDateTime scheduledArrivalTime
Planned arrival time.
int distance
Distance of the section in meter.
static bool isSame(const Journey &lhs, const Journey &rhs)
Checks if two instances refer to the same journey (which does not necessarily mean they are exactly e...
static Journey merge(const Journey &lhs, const Journey &rhs)
Merge two instances.
QTimeZone timeZone() const
The timezone this location is in, if known.
static double distance(double lat1, double lon1, double lat2, double lon2)
Compute the distance between two geo coordinates, in meters.
QString name
Human-readable name of the location.
Query response base class.
@ NoError
Nothing went wrong.
@ NotFoundError
The requested journey/departure/place could not be found.
int distance(const GeoCoordinates &coord1, const GeoCoordinates &coord2)
QString path(const QString &relativePath)
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...
void setTimeZone(const QTimeZone &toZone)
bool isEmpty() const const
qsizetype size() const const
bool isValid() const const