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);
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
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 if (!section.
from().hasCoordinate() || !section.
to().hasCoordinate() || section.
path().
isEmpty()) {
192 if (pathDist > pointDist * 10) {
193 qCDebug(
Log) <<
"Dropping implausibly long path:" << pointDist << pathDist;
200 auto pathSecs =
path.takeSections();
201 for (
auto &pathSec : pathSecs) {
203 PathFilter::removeSpikes(p, 55.0);
206 path.setSections(std::move(pathSecs));
207 section.setPath(path);
211 journey.setSections(std::move(sections));
215 journeys.erase(std::remove_if(journeys.begin(), journeys.end(), [](
const auto &journey) {
216 return journey.sections().empty() || std::any_of(journey.sections().begin(), journey.sections().end(), isImplausibleSection);
221 :
Reply(new JourneyReplyPrivate, parent)
225 d->nextRequest = req;
226 d->prevRequest = req;
229JourneyReply::~JourneyReply() =
default;
246 return std::move(d->journeys);
252 if (d->nextRequest.contexts().empty()) {
255 return d->nextRequest;
261 if (d->prevRequest.contexts().empty()) {
264 return d->prevRequest;
267void JourneyReply::addResult(
const AbstractBackend *backend, std::vector<Journey> &&res)
270 d->postProcessJourneys(res);
276 auto context = d->nextRequest.context(backend);
277 context.type = RequestContext::Next;
278 for (
const auto &jny : res) {
279 context.dateTime = std::max(context.dateTime, jny.scheduledDepartureTime());
281 d->nextRequest.setContext(backend, std::move(context));
283 context = d->prevRequest.context(backend);
284 context.type = RequestContext::Previous;
285 context.dateTime = res[0].scheduledArrivalTime();
286 for (
const auto &jny : res) {
287 context.dateTime = std::min(context.dateTime, jny.scheduledArrivalTime());
289 d->prevRequest.setContext(backend, std::move(context));
293 if (backend->timeZone().isValid()) {
294 for (
auto &jny : res) {
295 JourneyUtil::applyTimeZone(jny, backend->timeZone());
300 for (
auto &jny : res) {
301 jny.applyMetaData(
request().downloadAssets());
306 Cache::addNegativeJourneyCacheEntry(backend->backendId(),
request().cacheKey());
310 addAttribution(backend->attribution());
314 if (d->journeys.empty()) {
315 d->journeys = std::move(res);
317 d->journeys.insert(d->journeys.end(), res.begin(), res.end());
319 d->emitUpdated(
this);
323 d->emitFinishedIfDone(
this);
326void JourneyReply::setNextContext(
const AbstractBackend *backend,
const QVariant &data)
329 auto context = d->nextRequest.context(backend);
330 context.type = RequestContext::Next;
331 context.backendData = data;
332 d->nextRequest.setContext(backend, std::move(context));
335void JourneyReply::setPreviousContext(
const AbstractBackend *backend,
const QVariant &data)
338 auto context = d->prevRequest.context(backend);
339 context.type = RequestContext::Previous;
340 context.backendData = data;
341 d->prevRequest.setContext(backend, std::move(context));
344void JourneyReply::addError(
const AbstractBackend *backend,
Reply::Error error,
const QString &errorMsg)
347 Cache::addNegativeJourneyCacheEntry(backend->backendId(),
request().cacheKey());
349 qCDebug(
Log) << backend->backendId() <<
error << errorMsg;
351 Reply::addError(
error, errorMsg);
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.
bool downloadAssets
Download graphic assets such as line logos for the data requested here.
@ 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::Route route
Route to take on this segment.
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.
KPublicTransport::Line::Mode mode
Type of transport.
static bool modeIsRailBound(KPublicTransport::Line::Mode mode)
true if mode is bounds to rail tracks.
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.
int distance
The length of this path in meters.
bool isEmpty() const
Returns true if this is an empty/not-set path.
Query response base class.
Error error() const
Error code.
@ NoError
Nothing went wrong.
@ NotFoundError
The requested journey/departure/place could not be found.
KPublicTransport::Line line
Line this route belongs to.
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...
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
void setTimeZone(const QTimeZone &toZone)
bool isEmpty() const const
qsizetype size() const const
bool isValid() const const