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"
16#include <KPublicTransport/Journey>
17#include <KPublicTransport/Location>
27constexpr inline const auto MINIMUM_WAIT_TIME = 60;
28constexpr inline const auto MINIMUM_WALK_TIME = 90;
29constexpr inline const auto MINIMUM_WALK_DISTANCE = 50;
31constexpr inline const auto MAXIMUM_TRANSFER_SPEED = 30;
32constexpr inline const auto MAXIMUM_TRANSFER_DISTANCE = 100000;
34class JourneyReplyPrivate :
public ReplyPrivate {
36 void finalizeResult()
override;
37 bool needToWaitForAssets()
const override;
38 static void postProcessJourneys(std::vector<Journey> &journeys);
43 std::vector<Journey> journeys;
47void JourneyReplyPrivate::finalizeResult()
49 if (journeys.empty()) {
57 std::sort(journeys.begin(), journeys.end(), JourneyUtil::firstTransportDepartureLessThan);
58 for (
auto it = journeys.begin(); it != journeys.end(); ++it) {
59 for (
auto mergeIt = it + 1; mergeIt != journeys.end();) {
60 if (!JourneyUtil::firstTransportDepartureEqual(*it, *mergeIt)) {
66 mergeIt = journeys.erase(mergeIt);
74 std::sort(journeys.begin(), journeys.end(), [](
const auto &lhs,
const auto &rhs) {
75 return lhs.scheduledDepartureTime() < rhs.scheduledDepartureTime();
78 nextRequest.purgeLoops(request);
79 prevRequest.purgeLoops(request);
82bool JourneyReplyPrivate::needToWaitForAssets()
const
101 if (section.
mode() == JourneySection::Waiting) {
102 return section.
duration() < MINIMUM_WAIT_TIME;
104 if (section.
mode() == JourneySection::Walking && !hasNonTrivialPath(section)) {
105 return section.
duration() < MINIMUM_WALK_TIME || (section.
distance() > 0 && section.
distance() < MINIMUM_WALK_DISTANCE);
112 if ((section.
mode() == JourneySection::Transfer || section.
mode() == JourneySection::Walking)
113 && section.
from().hasCoordinate() && section.
to().hasCoordinate())
116 if (section.
duration() > 0 && (distance / (
float)section.
duration()) > MAXIMUM_TRANSFER_SPEED) {
117 qCDebug(
Log) <<
"discarding journey based on insane transfer/walking speed:" << (
distance / (float)section.
duration()) <<
"m/s";
120 if (distance > MAXIMUM_TRANSFER_DISTANCE) {
121 qCDebug(
Log) <<
"discarding journey with insane transfer/walking distance:" <<
distance <<
"m" << section.
from().
name() << section.
to().
name();
128void JourneyReplyPrivate::postProcessJourneys(std::vector<Journey> &journeys)
131 for (
auto &journey : journeys) {
132 JourneyUtil::propagateTimeZones(journey);
133 auto sections = journey.takeSections();
134 for (
auto §ion : sections) {
135 if (section.
mode() == JourneySection::Walking) {
137 auto from = section.
from();
139 section.setFrom(from);
142 section.setScheduledDepartureTime(dt);
145 auto to = section.
to();
150 section.setScheduledArrivalTime(dt);
154 journey.setSections(std::move(sections));
158 for (
auto &journey : journeys) {
159 auto sections = journey.takeSections();
162 for (
auto it = sections.begin(); it != sections.end();) {
163 if (it == sections.begin()) {
167 auto prevIt = it - 1;
168 if ((*it).mode() == JourneySection::Walking && (*prevIt).mode() == JourneySection::Walking) {
169 (*prevIt).setTo((*it).to());
170 (*prevIt).setScheduledArrivalTime((*it).scheduledArrivalTime());
171 (*prevIt).setExpectedArrivalTime((*it).expectedArrivalTime());
172 (*prevIt).setDistance((*prevIt).distance() + (*it).distance());
173 it = sections.erase(it);
181 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()) {
191 if (pathDist > pointDist * 10) {
192 qCDebug(
Log) <<
"Dropping implausibly long path:" << pointDist << pathDist;
197 journey.setSections(std::move(sections));
201 journeys.erase(std::remove_if(journeys.begin(), journeys.end(), [](
const auto &journey) {
202 return journey.sections().empty() || std::any_of(journey.sections().begin(), journey.sections().end(), isImplausibleSection);
207 :
Reply(new JourneyReplyPrivate, parent)
211 d->nextRequest = req;
212 d->prevRequest = req;
215JourneyReply::~JourneyReply() =
default;
232 return std::move(d->journeys);
238 if (d->nextRequest.contexts().empty()) {
241 return d->nextRequest;
247 if (d->prevRequest.contexts().empty()) {
250 return d->prevRequest;
253void JourneyReply::addResult(
const AbstractBackend *backend, std::vector<Journey> &&res)
256 d->postProcessJourneys(res);
262 auto context = d->nextRequest.context(backend);
263 context.type = RequestContext::Next;
264 for (
const auto &jny : res) {
265 context.dateTime = std::max(context.dateTime, jny.scheduledDepartureTime());
267 d->nextRequest.setContext(backend, std::move(context));
269 context = d->prevRequest.context(backend);
270 context.type = RequestContext::Previous;
271 context.dateTime = res[0].scheduledArrivalTime();
272 for (
const auto &jny : res) {
273 context.dateTime = std::min(context.dateTime, jny.scheduledArrivalTime());
275 d->prevRequest.setContext(backend, std::move(context));
279 if (backend->timeZone().isValid()) {
280 for (
auto &jny : res) {
281 JourneyUtil::applyTimeZone(jny, backend->timeZone());
286 for (
auto &jny : res) {
287 jny.applyMetaData(
request().downloadAssets());
292 Cache::addNegativeJourneyCacheEntry(backend->backendId(),
request().cacheKey());
296 addAttribution(backend->attribution());
300 if (d->journeys.empty()) {
301 d->journeys = std::move(res);
303 d->journeys.insert(d->journeys.end(), res.begin(), res.end());
305 d->emitUpdated(
this);
309 d->emitFinishedIfDone(
this);
312void JourneyReply::setNextContext(
const AbstractBackend *backend,
const QVariant &data)
315 auto context = d->nextRequest.context(backend);
316 context.type = RequestContext::Next;
317 context.backendData = data;
318 d->nextRequest.setContext(backend, std::move(context));
321void JourneyReply::setPreviousContext(
const AbstractBackend *backend,
const QVariant &data)
324 auto context = d->prevRequest.context(backend);
325 context.type = RequestContext::Previous;
326 context.backendData = data;
327 d->prevRequest.setContext(backend, std::move(context));
330void JourneyReply::addError(
const AbstractBackend *backend,
Reply::Error error,
const QString &errorMsg)
333 Cache::addNegativeJourneyCacheEntry(backend->backendId(),
request().cacheKey());
335 qCDebug(
Log) << backend->backendId() <<
error << errorMsg;
337 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::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 float distance(float lat1, float lon1, float lat2, float 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.
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