KPublicTransport

journey.cpp
1/*
2 SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "journey.h"
8#include "journeyutil_p.h"
9#include "json_p.h"
10#include "datatypes_p.h"
11#include "loadutil_p.h"
12#include "logging.h"
13#include "mergeutil_p.h"
14#include "notesutil_p.h"
15#include "platformutils_p.h"
16#include "rentalvehicle.h"
17#include "rentalvehicleutil_p.h"
18#include "stopover.h"
19
20#include <QDebug>
21#include <QVariant>
22
23using namespace Qt::Literals::StringLiterals;
24using namespace KPublicTransport;
25
26namespace KPublicTransport {
27
28class JourneySectionPrivate : public QSharedData
29{
30public:
31 JourneySection::Mode mode = JourneySection::Invalid;
32 QDateTime scheduledDepartureTime;
33 QDateTime expectedDepartureTime;
34 QDateTime scheduledArrivalTime;
35 QDateTime expectedArrivalTime;
36 Location from;
37 Location to;
38 Route route;
39 QString scheduledDeparturePlatform;
40 QString expectedDeparturePlatform;
41 QString scheduledArrivalPlatform;
42 QString expectedArrivalPlatform;
43 int distance = 0;
44 Disruption::Effect disruptionEffect = Disruption::NormalService;
45 QStringList notes;
46 std::vector<Stopover> intermediateStops;
47 int co2Emission = -1;
48 std::vector<LoadInfo> loadInformation;
49 RentalVehicle rentalVehicle;
50 Path path;
51 Vehicle departureVehicleLayout;
52 Platform departurePlatformLayout;
53 Vehicle arrivalVehicleLayout;
54 Platform arrivalPlatformLayout;
55 IndividualTransport individualTransport;
56};
57
58class JourneyPrivate : public QSharedData
59{
60public:
61 std::vector<JourneySection> sections;
62};
63
64}
65
66KPUBLICTRANSPORT_MAKE_GADGET(JourneySection)
67KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, JourneySection::Mode, mode, setMode)
68KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, QDateTime, scheduledDepartureTime, setScheduledDepartureTime)
69KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, QDateTime, expectedDepartureTime, setExpectedDepartureTime)
70KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, QDateTime, scheduledArrivalTime, setScheduledArrivalTime)
71KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, QDateTime, expectedArrivalTime, setExpectedArrivalTime)
72KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Location, from, setFrom)
73KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Location, to, setTo)
74KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Route, route, setRoute)
75KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Disruption::Effect, disruptionEffect, setDisruptionEffect)
76KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, QStringList, notes, setNotes)
77KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, RentalVehicle, rentalVehicle, setRentalVehicle)
78KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Path, path, setPath)
79KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Vehicle, departureVehicleLayout, setDepartureVehicleLayout)
80KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Platform, departurePlatformLayout, setDeparturePlatformLayout)
81KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Vehicle, arrivalVehicleLayout, setArrivalVehicleLayout)
82KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Platform, arrivalPlatformLayout, setArrivalPlatformLayout)
83KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, KPublicTransport::IndividualTransport, individualTransport, setIndividualTransport)
84
86{
87 return d->expectedDepartureTime.isValid();
88}
89
91{
93 return d->scheduledDepartureTime.secsTo(d->expectedDepartureTime) / 60;
94 }
95 return 0;
96}
97
99{
100 return d->expectedArrivalTime.isValid();
101}
102
104{
106 return d->scheduledArrivalTime.secsTo(d->expectedArrivalTime) / 60;
107 }
108 return 0;
109}
110
111int JourneySection::duration() const
112{
113 return d->scheduledDepartureTime.secsTo(d->scheduledArrivalTime);
114}
115
116int JourneySection::distance() const
117{
118 if (d->mode == JourneySection::Waiting) {
119 return 0;
120 }
121
122 double dist = 0;
123 if (d->from.hasCoordinate() && d->to.hasCoordinate()) {
124 auto startLat = d->from.latitude();
125 auto startLon = d->from.longitude();
126
127 for (const auto &stop : d->intermediateStops) {
128 if (!stop.stopPoint().hasCoordinate()) {
129 continue;
130 }
131 dist += Location::distance(startLat, startLon, stop.stopPoint().latitude(), stop.stopPoint().longitude());
132 startLat = stop.stopPoint().latitude();
133 startLon = stop.stopPoint().longitude();
134 }
135
136 dist += Location::distance(startLat, startLon, d->to.latitude(), d->to.longitude());
137 }
138 dist = std::max<double>(dist, d->path.distance());
139 return std::max((int)std::round(dist), d->distance);
140}
141
142void JourneySection::setDistance(int value)
143{
144 d.detach();
145 d->distance = value;
146}
147
149{
150 return d->scheduledDeparturePlatform;
151}
152
153void JourneySection::setScheduledDeparturePlatform(const QString &platform)
154{
155 d.detach();
156 d->scheduledDeparturePlatform = PlatformUtils::normalizePlatform(platform);
157}
158
160{
161 return d->expectedDeparturePlatform;
162}
163
164void JourneySection::setExpectedDeparturePlatform(const QString &platform)
165{
166 d.detach();
167 d->expectedDeparturePlatform = PlatformUtils::normalizePlatform(platform);
168}
169
171{
172 return !d->expectedDeparturePlatform.isEmpty();
173}
174
176{
177 return PlatformUtils::platformChanged(d->scheduledDeparturePlatform, d->expectedDeparturePlatform);
178}
179
181{
182 return d->scheduledArrivalPlatform;
183}
184
185void JourneySection::setScheduledArrivalPlatform(const QString &platform)
186{
187 d.detach();
188 d->scheduledArrivalPlatform = PlatformUtils::normalizePlatform(platform);
189}
190
192{
193 return d->expectedArrivalPlatform;
194}
195
196void JourneySection::setExpectedArrivalPlatform(const QString &platform)
197{
198 d.detach();
199 d->expectedArrivalPlatform = PlatformUtils::normalizePlatform(platform);
200}
201
203{
204 return !d->expectedArrivalPlatform.isEmpty();
205}
206
208{
209 return PlatformUtils::platformChanged(d->scheduledArrivalPlatform, d->expectedArrivalPlatform);
210}
211
213{
214 const auto n = NotesUtil::normalizeNote(note);
215 const auto idx = NotesUtil::needsAdding(d->notes, n);
216 if (idx >= 0) {
217 d.detach();
218 NotesUtil::performAdd(d->notes, n, idx);
219 }
220}
221
222void JourneySection::addNotes(const QStringList &notes)
223{
224 for (const auto &n : notes) {
225 addNote(n);
226 }
227}
228
229const std::vector<Stopover>& JourneySection::intermediateStops() const
230{
231 return d->intermediateStops;
232}
233
235{
236 d.detach();
237 return std::move(d->intermediateStops);
238}
239
240void JourneySection::setIntermediateStops(std::vector<Stopover> &&stops)
241{
242 d.detach();
243 d->intermediateStops = std::move(stops);
244}
245
246QVariantList JourneySection::intermediateStopsVariant() const
247{
248 QVariantList l;
249 l.reserve(d->intermediateStops.size());
250 std::transform(d->intermediateStops.begin(), d->intermediateStops.end(), std::back_inserter(l), [](const auto &stop) { return QVariant::fromValue(stop); });
251 return l;
252}
253
255{
256 Stopover dep;
257 dep.setStopPoint(from());
258 dep.setRoute(route());
259 dep.setScheduledDepartureTime(scheduledDepartureTime());
260 dep.setExpectedDepartureTime(expectedDepartureTime());
261 dep.setScheduledPlatform(scheduledDeparturePlatform());
262 dep.setExpectedPlatform(expectedDeparturePlatform());
263 dep.addNotes(notes());
264 dep.setDisruptionEffect(disruptionEffect());
265 dep.setVehicleLayout(departureVehicleLayout());
266 dep.setPlatformLayout(departurePlatformLayout());
267 return dep;
268}
269
271{
272 setFrom(departure.stopPoint());
273 setScheduledDepartureTime(departure.scheduledDepartureTime());
274 setExpectedDepartureTime(departure.expectedDepartureTime());
275 setScheduledDeparturePlatform(departure.scheduledPlatform());
276 setExpectedDeparturePlatform(departure.expectedPlatform());
277 setDeparturePlatformLayout(departure.platformLayout());
278 setDepartureVehicleLayout(departure.vehicleLayout());
279 if (departure.disruptionEffect() == Disruption::NoService) {
280 setDisruptionEffect(departure.disruptionEffect());
281 }
282}
283
285{
286 Stopover arr;
287 arr.setStopPoint(to());
288 arr.setRoute(route());
289 arr.setScheduledArrivalTime(scheduledArrivalTime());
290 arr.setExpectedArrivalTime(expectedArrivalTime());
291 arr.setScheduledPlatform(scheduledArrivalPlatform());
292 arr.setExpectedPlatform(expectedArrivalPlatform());
293 arr.setDisruptionEffect(disruptionEffect());
294 arr.setVehicleLayout(arrivalVehicleLayout());
295 arr.setPlatformLayout(arrivalPlatformLayout());
296 return arr;
297}
298
300{
301 setTo(arrival.stopPoint());
302 setScheduledArrivalTime(arrival.scheduledArrivalTime());
303 setExpectedArrivalTime(arrival.expectedArrivalTime());
304 setScheduledArrivalPlatform(arrival.scheduledPlatform());
305 setExpectedArrivalPlatform(arrival.expectedPlatform());
306 setArrivalPlatformLayout(arrival.platformLayout());
307 setArrivalVehicleLayout(arrival.vehicleLayout());
308 if (arrival.disruptionEffect() == Disruption::NoService) {
309 setDisruptionEffect(arrival.disruptionEffect());
310 }
311}
312
313struct {
314 Line::Mode mode;
315 int gramPerKm;
316} static constexpr const emissionForModeMap[] = {
317 { Line::Air, 285 },
318 { Line::Boat, 245 },
319 { Line::Bus, 68 },
320 { Line::Coach, 68 },
321 { Line::Ferry, 245 },
322 // { Line::Funicular, -1 }, TODO
323 { Line::LocalTrain, 14 },
324 { Line::LongDistanceTrain, 14 },
325 { Line::Metro, 11 },
326 { Line::RailShuttle, 11 }, // assuming tram/rapid transit-like
327 { Line::RapidTransit, 11 },
328 { Line::Shuttle, 68 },
329 { Line::Taxi, 158 },
330 { Line::Train, 14 },
331 { Line::Tramway, 11 },
332 { Line::RideShare, 158 },
333 // { Line::AerialLift, -1 }, TODO
334};
335
336struct {
338 int gramPerKm;
339} static constexpr const emissionForIvModeMap[] = {
340 { IndividualTransport::Walk, 0 },
341 { IndividualTransport::Bike, 0 },
342 { IndividualTransport::Car, 158 }
343};
344
345struct {
347 int gramPerKm;
348} static constexpr const emissionForRvModeMap[] = {
350 // { RentalVehicle::Pedelec, -1 }, TODO
351 // { RentalVehicle::ElectricKickScooter, -1 }, TODO
352 // { RentalVehicle::ElectricMoped, -1 }, TODO
353 { RentalVehicle::Car, 158 },
354};
355
357{
358 if (d->co2Emission >= 0) {
359 return d->co2Emission;
360 }
361
362 switch (d->mode) {
363 case JourneySection::Invalid:
364 return -1;
365 case JourneySection::Walking:
366 case JourneySection::Transfer:
367 case JourneySection::Waiting:
368 return 0;
369 case JourneySection::PublicTransport:
370 {
371 const auto mode = route().line().mode();
372 for (const auto &map : emissionForModeMap) {
373 if (map.mode == mode) {
374 return (map.gramPerKm * distance()) / 1000;
375 }
376 }
377 qCDebug(Log) << "No CO2 emission estimate for mode" << mode;
378 return -1;
379 }
381 {
382 const auto mode = individualTransport().mode();
383 for (const auto &map :emissionForIvModeMap) {
384 if (map.mode == mode) {
385 return (map.gramPerKm *distance()) / 1000;
386 }
387 }
388 qCDebug(Log) << "No CO2 emission estimate for mode" << mode;
389 return -1;
390 }
392 {
393 const auto mode = rentalVehicle().type();
394 for (const auto &map :emissionForRvModeMap) {
395 if (map.mode == mode) {
396 return (map.gramPerKm *distance()) / 1000;
397 }
398 }
399 qCDebug(Log) << "No CO2 emission estimate for vehicle type" << mode;
400 return -1;
401 }
402 }
403
404 return -1;
405}
406
407void JourneySection::setCo2Emission(int value)
408{
409 d.detach();
410 d->co2Emission = value;
411}
412
413const std::vector<LoadInfo>& JourneySection::loadInformation() const
414{
415 return d->loadInformation;
416}
417
418std::vector<LoadInfo>&& JourneySection::takeLoadInformation()
419{
420 d.detach();
421 return std::move(d->loadInformation);
422}
423
424void JourneySection::setLoadInformation(std::vector<LoadInfo> &&loadInfo)
425{
426 d.detach();
427 d->loadInformation = std::move(loadInfo);
428}
429
430QVariantList JourneySection::loadInformationVariant() const
431{
432 QVariantList l;
433 l.reserve(d->loadInformation.size());
434 std::transform(d->loadInformation.begin(), d->loadInformation.end(), std::back_inserter(l), [](const auto &load) { return QVariant::fromValue(load); });
435 return l;
436}
437
438const std::vector<KPublicTransport::Feature>& JourneySection::features() const
439{
440 return d->departureVehicleLayout.features();
441}
442
443[[nodiscard]] std::vector<KPublicTransport::Feature>&& JourneySection::takeFeatures()
444{
445 return d->departureVehicleLayout.takeFeatures();
446}
447
448void JourneySection::setFeatures(std::vector<KPublicTransport::Feature> &&features)
449{
450 d.detach();
451 d->departureVehicleLayout.setFeatures(std::move(features));
452}
453
455{
456 switch (d->mode) {
457 case JourneySection::Invalid:
458 return {};
459 case JourneySection::PublicTransport:
460 return d->route.line().iconName();
462 return d->rentalVehicle.vehicleTypeIconName();
464 return d->individualTransport.modeIconName();
465 case JourneySection::Transfer:
466 case JourneySection::Walking:
467 case JourneySection::Waiting:
468 break;
469 }
470
471 return modeIconName(d->mode);
472}
473
475{
476 switch (mode) {
477 case JourneySection::Invalid:
478 return {};
479 case JourneySection::PublicTransport:
480 return Line::modeIconName(Line::Train);
481 case JourneySection::Transfer:
482 return u"qrc:///org.kde.kpublictransport/assets/images/journey-mode-transfer.svg"_s;
483 case JourneySection::Walking:
484 return IndividualTransport::modeIconName(IndividualTransport::Walk);
485 case JourneySection::Waiting:
486 return u"qrc:///org.kde.kpublictransport/assets/images/journey-mode-wait.svg"_s;
490 return IndividualTransport::modeIconName(IndividualTransport::Bike);
491 }
492
493 return u"question"_s;
494}
495
497{
498 if (!from().hasCoordinate() || mode() != JourneySection::PublicTransport) {
499 return;
500 }
501 auto line = d->route.line();
502 line.applyMetaData(from(), download);
503 d->route.setLine(line);
504
505 // propagate to intermediate stops
506 for (auto &stop : d->intermediateStops) {
507 stop.setRoute(d->route);
508 }
509}
510
512{
513 if (lhs.d->mode != rhs.d->mode) {
514 return false;
515 }
516
518 return false;
519 }
520
521 // we have N criteria to compare here, with 3 possible results:
522 // - equal
523 // - similar-ish, unknwon, or at least not conflicting
524 // - conflicting
525 // A single conflict results in a negative result, at least N - 1 equal comparisons lead to
526 // in a positive result.
527 enum { Equal = 1, Compatible = 0, Conflict = -1000 };
528 int result = 0;
529
530 const auto depTimeDist = MergeUtil::distance(lhs.d->scheduledDepartureTime, rhs.d->scheduledDepartureTime);
531 result += depTimeDist < 60 ? Equal : depTimeDist <= 60 ? Compatible : Conflict;
532 const auto arrTimeDist = MergeUtil::distance(lhs.d->scheduledArrivalTime, rhs.d->scheduledArrivalTime);
533 result += arrTimeDist < 60 ? Equal : depTimeDist <= 60 ? Compatible : Conflict;
534
535 const auto sameFrom = Location::isSame(lhs.d->from, rhs.d->from);
536 const auto fromDist = Location::distance(lhs.from(), rhs.from());
537 result += sameFrom ? Equal : fromDist < 200 ? Compatible : Conflict;
538
539 const auto sameTo = Location::isSame(lhs.d->to, rhs.d->to);
540 const auto toDist = Location::distance(lhs.to(), rhs.to());
541 result += sameTo ? Equal : toDist < 200 ? Compatible : Conflict;
542
543 const auto sameRoute = Route::isSame(lhs.d->route, rhs.d->route);
544 const auto sameDir = Location::isSameName(lhs.d->route.direction(), rhs.d->route.direction());
545 const auto sameLine = Line::isSame(lhs.d->route.line(), rhs.d->route.line());
546 result += sameRoute ? Equal : (sameDir || sameLine) ? Compatible : Conflict;
547
549 result += lhs.scheduledDeparturePlatform() == rhs.scheduledDeparturePlatform() ? Equal : Conflict;
550 }
551
552 return result >= 4;
553}
554
556{
557 using namespace MergeUtil;
558 auto res = lhs;
559 res.setScheduledDepartureTime(mergeDateTimeEqual(lhs.scheduledDepartureTime(), rhs.scheduledDepartureTime()));
560 res.setExpectedDepartureTime(mergeDateTimeMax(lhs.expectedDepartureTime(), rhs.expectedDepartureTime()));
561 res.setScheduledArrivalTime(mergeDateTimeMax(lhs.scheduledArrivalTime(), rhs.scheduledArrivalTime()));
562 res.setExpectedArrivalTime(mergeDateTimeMax(lhs.expectedArrivalTime(), rhs.expectedArrivalTime()));
563
564 if (res.expectedDeparturePlatform().isEmpty()) {
565 res.setExpectedDeparturePlatform(rhs.expectedDeparturePlatform());
566 }
567 if (res.expectedArrivalPlatform().isEmpty()) {
568 res.setExpectedArrivalPlatform(rhs.expectedArrivalPlatform());
569 }
570 res.setFrom(Location::merge(lhs.from(), rhs.from()));
571 res.setTo(Location::merge(lhs.to(), rhs.to()));
572 res.setRoute(Route::merge(lhs.route(), rhs.route()));
573
574 res.setScheduledDeparturePlatform(mergeString(lhs.scheduledDeparturePlatform(), rhs.scheduledDeparturePlatform()));
575 res.setScheduledArrivalPlatform(mergeString(lhs.scheduledArrivalPlatform(), rhs.scheduledArrivalPlatform()));
576
577 res.setDisruptionEffect(std::max(lhs.disruptionEffect(), rhs.disruptionEffect()));
578 res.setNotes(NotesUtil::mergeNotes(lhs.notes(), rhs.notes()));
579 res.setDistance(std::max(lhs.distance(), rhs.distance()));
580
581 if (lhs.intermediateStops().size() == rhs.intermediateStops().size()) {
582 auto stops = res.takeIntermediateStops();
583 for (uint i = 0; i < stops.size(); ++i) {
584 stops[i] = Stopover::merge(stops[i], rhs.intermediateStops()[i]);
585 stops[i].setRoute(res.route());
586 }
587 res.setIntermediateStops(std::move(stops));
588 }
589
590 res.d->co2Emission = std::max(lhs.d->co2Emission, rhs.d->co2Emission);
591 res.d->loadInformation = LoadUtil::merge(lhs.d->loadInformation, rhs.d->loadInformation);
592 res.d->rentalVehicle = RentalVehicleUtil::merge(lhs.d->rentalVehicle, rhs.d->rentalVehicle);
593
594 res.d->path = lhs.d->path.sections().size() < rhs.d->path.sections().size() ? rhs.d->path : lhs.d->path;
595
596 res.d->departureVehicleLayout = Vehicle::merge(lhs.d->departureVehicleLayout, rhs.d->departureVehicleLayout);
597 res.d->departurePlatformLayout = Platform::merge(lhs.d->departurePlatformLayout, rhs.d->departurePlatformLayout);
598 res.d->arrivalVehicleLayout = Vehicle::merge(lhs.d->arrivalVehicleLayout, rhs.d->arrivalVehicleLayout);
599 res.d->arrivalPlatformLayout = Platform::merge(lhs.d->arrivalPlatformLayout, rhs.d->arrivalPlatformLayout);
600
601 return res;
602}
603
605{
606 auto obj = Json::toJson(section);
607 if (section.mode() != Waiting) {
608 const auto fromObj = Location::toJson(section.from());
609 if (!fromObj.empty()) {
610 obj.insert(QLatin1String("from"), fromObj);
611 }
612 const auto toObj = Location::toJson(section.to());
613 if (!toObj.empty()) {
614 obj.insert(QLatin1String("to"), toObj);
615 }
616 }
617 if (section.mode() == PublicTransport) {
618 const auto routeObj = Route::toJson(section.route());
619 if (!routeObj.empty()) {
620 obj.insert(QLatin1String("route"), routeObj);
621 }
622 if (!section.intermediateStops().empty()) {
623 obj.insert(QLatin1String("intermediateStops"), Stopover::toJson(section.intermediateStops()));
624 }
625 if (!section.loadInformation().empty()) {
626 obj.insert(QLatin1String("load"), LoadInfo::toJson(section.loadInformation()));
627 }
628 }
629 if (section.d->co2Emission < 0) {
630 obj.remove(QLatin1String("co2Emission"));
631 }
632 if (section.rentalVehicle().type() != RentalVehicle::Unknown) {
633 obj.insert(QLatin1String("rentalVehicle"), RentalVehicle::toJson(section.rentalVehicle()));
634 }
635
636 if (!section.path().isEmpty()) {
637 obj.insert(QLatin1String("path"), Path::toJson(section.path()));
638 }
639
640 if (!section.departureVehicleLayout().isEmpty()) {
641 obj.insert(QLatin1String("departureVehicleLayout"), Vehicle::toJson(section.departureVehicleLayout()));
642 }
643 if (!section.departurePlatformLayout().isEmpty()) {
644 obj.insert(QLatin1String("departurePlatformLayout"), Platform::toJson(section.departurePlatformLayout()));
645 }
646 if (!section.arrivalVehicleLayout().isEmpty()) {
647 obj.insert(QLatin1String("arrivalVehicleLayout"), Vehicle::toJson(section.arrivalVehicleLayout()));
648 }
649 if (!section.arrivalPlatformLayout().isEmpty()) {
650 obj.insert(QLatin1String("arrivalPlatformLayout"), Platform::toJson(section.arrivalPlatformLayout()));
651 }
652
653 if (section.mode() == JourneySection::IndividualTransport) {
654 obj.insert(QLatin1String("individualTransport"), IndividualTransport::toJson(section.individualTransport()));
655 }
656
657 if (obj.size() <= 3) { // only the disruption and mode enums and distance, ie. this is an empty object
658 return {};
659 }
660 return obj;
661}
662
663QJsonArray JourneySection::toJson(const std::vector<JourneySection> &sections)
664{
665 return Json::toJson(sections);
666}
667
669{
670 auto section = Json::fromJson<JourneySection>(obj);
671 section.setFrom(Location::fromJson(obj.value(QLatin1String("from")).toObject()));
672 section.setTo(Location::fromJson(obj.value(QLatin1String("to")).toObject()));
673 section.setRoute(Route::fromJson(obj.value(QLatin1String("route")).toObject()));
674 section.setIntermediateStops(Stopover::fromJson(obj.value(QLatin1String("intermediateStops")).toArray()));
675 section.setLoadInformation(LoadInfo::fromJson(obj.value(QLatin1String("load")).toArray()));
676 section.setRentalVehicle(RentalVehicle::fromJson(obj.value(QLatin1String("rentalVehicle")).toObject()));
677 section.setPath(Path::fromJson(obj.value(QLatin1String("path")).toObject()));
678 section.setDepartureVehicleLayout(Vehicle::fromJson(obj.value(QLatin1String("departureVehicleLayout")).toObject()));
679 section.setDeparturePlatformLayout(Platform::fromJson(obj.value(QLatin1String("departurePlatformLayout")).toObject()));
680 section.setArrivalVehicleLayout(Vehicle::fromJson(obj.value(QLatin1String("arrivalVehicleLayout")).toObject()));
681 section.setArrivalPlatformLayout(Platform::fromJson(obj.value(QLatin1String("arrivalPlatformLayout")).toObject()));
682 section.setIndividualTransport(IndividualTransport::fromJson(obj.value(QLatin1String("individualTransport")).toObject()));
683 section.applyMetaData(false);
684 return section;
685}
686
687std::vector<JourneySection> JourneySection::fromJson(const QJsonArray &array)
688{
689 return Json::fromJson<JourneySection>(array);
690}
691
692
693KPUBLICTRANSPORT_MAKE_GADGET(Journey)
694
695const std::vector<JourneySection>& Journey::sections() const
696{
697 return d->sections;
698}
699
700std::vector<JourneySection>&& Journey::takeSections()
701{
702 d.detach();
703 return std::move(d->sections);
704}
705
706void Journey::setSections(std::vector<JourneySection> &&sections)
707{
708 d.detach();
709 d->sections = std::move(sections);
710}
711
712QVariantList Journey::sectionsVariant() const
713{
714 QVariantList l;
715 l.reserve(d->sections.size());
716 std::transform(d->sections.begin(), d->sections.end(), std::back_inserter(l), [](const auto &sec) { return QVariant::fromValue(sec); });
717 return l;
718}
719
721{
722 if (!d->sections.empty()) {
723 return d->sections.front().scheduledDepartureTime();
724 }
725 return {};
726}
727
729{
730 return d->sections.empty() ? false : d->sections.front().hasExpectedDepartureTime();
731}
732
734{
735 return d->sections.empty() ? QDateTime() : d->sections.front().expectedDepartureTime();
736}
737
738int Journey::departureDelay() const
739{
740 return d->sections.empty() ? 0 : d->sections.front().departureDelay();
741}
742
744{
745 if (!d->sections.empty()) {
746 return d->sections.back().scheduledArrivalTime();
747 }
748 return {};
749}
750
752{
753 return d->sections.empty() ? false : d->sections.back().hasExpectedArrivalTime();
754}
755
757{
758 return d->sections.empty() ? QDateTime() : d->sections.back().expectedArrivalTime();
759}
760
761int Journey::arrivalDelay() const
762{
763 return d->sections.empty() ? 0 : d->sections.back().arrivalDelay();
764}
765
766int Journey::duration() const
767{
769}
770
771int Journey::numberOfChanges() const
772{
773 return std::max(0, static_cast<int>(std::count_if(d->sections.begin(), d->sections.end(), [](const auto &section) { return section.mode() == JourneySection::PublicTransport; }) - 1));
774}
775
777{
778 Disruption::Effect effect = Disruption::NormalService;
779 for (const auto &sec : d->sections) {
780 effect = std::max(effect, sec.disruptionEffect());
781 }
782 return effect;
783}
784
785int Journey::distance() const
786{
787 return std::accumulate(d->sections.begin(), d->sections.end(), 0, [](auto dist, const auto &jny) { return dist + jny.distance(); });
788}
789
790int Journey::co2Emission() const
791{
792 return std::accumulate(d->sections.begin(), d->sections.end(), 0, [](auto co2, const auto &jny) { return co2 + std::max(0, jny.co2Emission()); });
793}
794
795void Journey::applyMetaData(bool download)
796{
797 for (auto &sec : d->sections) {
798 sec.applyMetaData(download);
799 }
800}
801
802static bool isTransportSection(JourneySection::Mode mode)
803{
804 return mode == JourneySection::PublicTransport
807}
808
809bool Journey::isSame(const Journey &lhs, const Journey &rhs)
810{
811 auto lIt = lhs.sections().begin();
812 auto rIt = rhs.sections().begin();
813
814 while (lIt != lhs.sections().end() || rIt != rhs.sections().end()) {
815 // ignore non-transport sections
816 if (lIt != lhs.sections().end() && !isTransportSection((*lIt).mode())) {
817 ++lIt;
818 continue;
819 }
820 if (rIt != rhs.sections().end() && !isTransportSection((*rIt).mode())) {
821 ++rIt;
822 continue;
823 }
824
825 if (lIt == lhs.sections().end() || rIt == rhs.sections().end()) {
826 return false;
827 }
828
829 if (!JourneySection::isSame(*lIt, *rIt)) {
830 return false;
831 }
832
833 ++lIt;
834 ++rIt;
835 }
836
837 Q_ASSERT(lIt == lhs.sections().end() && rIt == rhs.sections().end());
838 return true;
839}
840
841Journey Journey::merge(const Journey &lhs, const Journey &rhs)
842{
843 std::vector<JourneySection> sections;
844 sections.reserve(lhs.sections().size() + rhs.sections().size());
845 std::copy(lhs.sections().begin(), lhs.sections().end(), std::back_inserter(sections));
846 std::copy(rhs.sections().begin(), rhs.sections().end(), std::back_inserter(sections));
847 std::sort(sections.begin(), sections.end(), [](const auto &lSec, const auto &rSec) {
848 if (MergeUtil::distance(lSec.scheduledDepartureTime(), rSec.scheduledDepartureTime()) == 0) {
849 return lSec.mode() < rSec.mode();
850 }
851 return MergeUtil::isBefore(lSec.scheduledDepartureTime(), rSec.scheduledDepartureTime());
852 });
853
854 for (auto it = sections.begin(); it != sections.end(); ++it) {
855 const auto nextIt = it + 1;
856 if (nextIt == sections.end()) {
857 break;
858 }
859
860 if (JourneySection::isSame(*it, *nextIt) || ((*it).mode() == (*nextIt).mode() && (*it).mode() != JourneySection::PublicTransport)) {
861 *it = JourneySection::merge(*it, *nextIt);
862 sections.erase(nextIt);
863 }
864 }
865
866 Journey res;
867 res.setSections(std::move(sections));
868 return res;
869}
870
872{
873 QJsonObject obj;
874 obj.insert(QLatin1String("sections"), JourneySection::toJson(journey.sections()));
875 return obj;
876}
877
878QJsonArray Journey::toJson(const std::vector<Journey> &journeys)
879{
880 return Json::toJson(journeys);
881}
882
884{
885 Journey j;
886 j.setSections(JourneySection::fromJson(obj.value(QLatin1String("sections")).toArray()));
887 return j;
888}
889
890std::vector<Journey> Journey::fromJson(const QJsonArray &array)
891{
892 return Json::fromJson<Journey>(array);
893}
894
895#include "moc_journey.cpp"
Individual transport mode details for a journey section, and for specifying journey requests.
Mode
Mode of (individual) transportation.
static QJsonObject toJson(const IndividualTransport &it)
Serializes one object to JSON.
static IndividualTransport fromJson(const QJsonObject &obj)
Deserialize an object from JSON.
QString modeIconName
Name of an icon to represent this transport mode.
A segment of a journey plan.
Definition journey.h:32
KPublicTransport::Path path
Movement path for this journey section.
Definition journey.h:141
void applyMetaData(bool download)
Augment line meta data.
Definition journey.cpp:496
bool arrivalPlatformChanged
true if we have real-time platform information and the platform changed.
Definition journey.h:105
QString scheduledDeparturePlatform
Planned departure platform.
Definition journey.h:90
static JourneySection merge(const JourneySection &lhs, const JourneySection &rhs)
Merge two instances.
Definition journey.cpp:555
void setDeparture(const Stopover &departure)
Sets all departure properties from a given Stopover.
Definition journey.cpp:270
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...
Definition journey.cpp:511
void setLoadInformation(std::vector< LoadInfo > &&loadInfo)
Set the vehicle load information for this journey section.
Definition journey.cpp:424
KPublicTransport::Location from
Departure location of this segment.
Definition journey.h:83
QDateTime expectedArrivalTime
Actual arrival time, if available.
Definition journey.h:71
QString expectedArrivalPlatform
Actual arrival platform, in case real-time information are available.
Definition journey.h:101
QString iconName
The best available icon to represent this journey section.
Definition journey.h:167
int departureDelay
Difference to schedule in minutes.
Definition journey.h:64
void setIntermediateStops(std::vector< Stopover > &&stops)
Set the intermediate stops.
Definition journey.cpp:240
static Q_INVOKABLE QString modeIconName(KPublicTransport::JourneySection::Mode mode)
Icon representing the journey section mode mode.
Definition journey.cpp:474
KPublicTransport::Platform arrivalPlatformLayout
Platform layout information at arrival.
Definition journey.h:153
int co2Emission
CO₂ emission during this journey section, in gram.
Definition journey.h:127
KPublicTransport::RentalVehicle rentalVehicle
Information about a rental vehicle, for sections using one.
Definition journey.h:135
QStringList notes
General human-readable notes on this service, e.g.
Definition journey.h:110
void setArrival(const Stopover &arrival)
Sets all arrival properties from a given Stopover.
Definition journey.cpp:299
static QJsonObject toJson(const JourneySection &section)
Serializes one journey section to JSON.
Definition journey.cpp:604
QDateTime scheduledDepartureTime
Planned departure time.
Definition journey.h:56
bool hasExpectedArrivalPlatform
true if real-time platform information are available.
Definition journey.h:103
@ RentedVehicle
free floating or dock-based rental bike service, electric scooters, car sharing services,...
Definition journey.h:45
@ IndividualTransport
using your own vehicle (bike, car, etc).
Definition journey.h:46
KPublicTransport::Route route
Route to take on this segment.
Definition journey.h:87
KPublicTransport::Disruption::Effect disruptionEffect
Disruption effect on this section, if any.
Definition journey.h:108
KPublicTransport::Stopover departure
All departure information represented as Stopover object.
Definition journey.h:116
QVariantList loadInformation
Vehicle load information for this journey section.
Definition journey.h:132
KPublicTransport::Vehicle arrivalVehicleLayout
Vehicle coach layout information at arrival.
Definition journey.h:151
QVariantList intermediateStops
Intermediate stops for consumption by QML.
Definition journey.h:113
KPublicTransport::Platform departurePlatformLayout
Platform layout information at departure.
Definition journey.h:146
int arrivalDelay
Difference to schedule in minutes.
Definition journey.h:75
KPublicTransport::Location to
Arrival location of this segment.
Definition journey.h:85
std::vector< Stopover > && takeIntermediateStops()
Moves the intermediate stops out of this object.
Definition journey.cpp:234
bool hasExpectedDeparturePlatform
true if real-time platform information are available.
Definition journey.h:94
QString scheduledArrivalPlatform
Planned arrival platform.
Definition journey.h:99
bool hasExpectedDepartureTime
true if this has real-time data.
Definition journey.h:62
bool hasExpectedArrivalTime
true if this has real-time data.
Definition journey.h:73
QString expectedDeparturePlatform
Actual departure platform, in case real-time information are available.
Definition journey.h:92
static JourneySection fromJson(const QJsonObject &obj)
Deserialize an object from JSON.
Definition journey.cpp:668
std::vector< LoadInfo > && takeLoadInformation()
Moves the load information out of this object for modification.
Definition journey.cpp:418
KPublicTransport::Vehicle departureVehicleLayout
Vehicle coach layout information at departure.
Definition journey.h:144
std::vector< KPublicTransport::Feature > features
Features of the vehicle used on this section.
Definition journey.h:162
void addNote(const QString &note)
Adds a note.
Definition journey.cpp:212
int duration
Duration of the section in seconds.
Definition journey.h:78
Mode mode
Mode of transport for this section.
Definition journey.h:53
QDateTime scheduledArrivalTime
Planned arrival time.
Definition journey.h:67
KPublicTransport::IndividualTransport individualTransport
Individual transport details for sections using your own vehicle.
Definition journey.h:156
bool departurePlatformChanged
true if we have real-time platform information and the platform changed.
Definition journey.h:96
QDateTime expectedDepartureTime
Actual departure time, if available.
Definition journey.h:60
KPublicTransport::Stopover arrival
All arrival information represented as Stopover object.
Definition journey.h:118
int distance
Distance of the section in meter.
Definition journey.h:80
A journey plan.
Definition journey.h:272
void applyMetaData(bool download)
Augment line meta data.
Definition journey.cpp:795
KPublicTransport::Disruption::Effect disruptionEffect
Worst disruption effect of any of the journey sections.
Definition journey.h:303
static Journey fromJson(const QJsonObject &obj)
Deserialize an object from JSON.
Definition journey.cpp:883
QDateTime expectedDepartureTime
Actual departure time, if available.
Definition journey.h:283
QDateTime scheduledDepartureTime
Departure time of the journey, according to schedule.
Definition journey.h:277
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...
Definition journey.cpp:809
QVariantList sections
Journey sections for consumption by QML.
Definition journey.h:275
QDateTime expectedArrivalTime
Actual arrival time, if available.
Definition journey.h:294
int departureDelay
Difference to schedule in minutes.
Definition journey.h:285
int numberOfChanges
Number of changes on this journey.
Definition journey.h:301
void setSections(std::vector< JourneySection > &&sections)
Sets the journey sections.
Definition journey.cpp:706
static QJsonObject toJson(const Journey &journey)
Serializes one journey object to JSON.
Definition journey.cpp:871
bool hasExpectedDepartureTime
true if this has real-time data.
Definition journey.h:279
int duration
Duration of the entire journey in seconds.
Definition journey.h:299
QDateTime scheduledArrivalTime
Arrival time of the journey, according to schedule.
Definition journey.h:288
int distance
Total travelled distance of the entire journey in meter.
Definition journey.h:308
int arrivalDelay
Difference to schedule in minutes.
Definition journey.h:296
int co2Emission
Total CO2 emissions for the entire journey in gram.
Definition journey.h:312
std::vector< JourneySection > && takeSections()
Moves the journey sections out of this object.
Definition journey.cpp:700
static Journey merge(const Journey &lhs, const Journey &rhs)
Merge two instances.
Definition journey.cpp:841
bool hasExpectedArrivalTime
true if this has real-time data.
Definition journey.h:290
KPublicTransport::Line::Mode mode
Type of transport.
Definition line.h:60
QString modeIconName
Generic icon for the line mode.
Definition line.h:93
Mode
Mode of transportation.
Definition line.h:27
@ RideShare
peer-to-peer ride sharing/car pooling
Definition line.h:44
@ RailShuttle
rail shuttle service within a complex, as e.g. found at or around airports
Definition line.h:38
@ Shuttle
shuttle bus/coach services, e.g. to/from an airport
Definition line.h:40
static bool isSame(const Line &lhs, const Line &rhs)
Checks if to instances refer to the same line (which does not necessarily mean they are exactly equal...
Definition line.cpp:167
static QJsonObject toJson(const LoadInfo &info)
Serializes one load information object to JSON.
Definition load.cpp:26
static LoadInfo fromJson(const QJsonObject &obj)
Deserialize an object from JSON.
Definition load.cpp:36
static Location fromJson(const QJsonObject &obj)
Deserialize a Location object from JSON.
Definition location.cpp:559
static bool isSameName(const QString &lhs, const QString &rhs)
Checks if two location names refer to the same location.
Definition location.cpp:370
static QJsonObject toJson(const Location &loc)
Serializes one Location object to JSON.
Definition location.cpp:494
static double distance(double lat1, double lon1, double lat2, double lon2)
Compute the distance between two geo coordinates, in meters.
Definition location.cpp:474
static Location merge(const Location &lhs, const Location &rhs)
Merge two departure instances.
Definition location.cpp:417
static bool isSame(const Location &lhs, const Location &rhs)
Checks if to instances refer to the same location (which does not necessarily mean they are exactly e...
Definition location.cpp:311
A path followed by any kind of location change.
Definition path.h:113
bool isEmpty() const
Returns true if this is an empty/not-set path.
Definition path.cpp:143
static Path fromJson(const QJsonObject &obj)
Deserialize an object from JSON.
Definition path.cpp:187
static QJsonObject toJson(const Path &path)
Serializes one path object to JSON.
Definition path.cpp:180
std::vector< KPublicTransport::PathSection > sections
Access to path sections for QML.
Definition path.h:117
Information about the layout of a station platform.
Definition platform.h:45
static Platform fromJson(const QJsonObject &obj)
Deserialize an object from JSON.
Definition platform.cpp:113
bool isEmpty() const
Returns true if this object contains no information beyond default values.
Definition platform.cpp:66
static Platform merge(const Platform &lhs, const Platform &rhs)
Merge two platform instances.
Definition platform.cpp:93
static QJsonObject toJson(const Platform &platform)
Serializes one platform object to JSON.
Definition platform.cpp:99
An individual rental vehicle used on a JourneySection, ie.
@ Car
electrical- or combustion-powered car
@ ElectricKickScooter
"e scooter", electrically assisted kick scooters, not to be confused with motorcycle-like scooters
@ Bicycle
human-powered bicylce
static RentalVehicle fromJson(const QJsonObject &obj)
Deserialize an object from JSON.
static QJsonObject toJson(const RentalVehicle &vehicle)
Serializes one object to JSON.
VehicleType type
Vehicle type.
QString vehicleTypeIconName
Icon representing the vehicle type.
A route of a public transport line.
Definition line.h:148
QString direction
Direction of the route.
Definition line.h:157
static bool isSame(const Route &lhs, const Route &rhs)
Checks if to instances refer to the same route (which does not necessarily mean they are exactly equa...
Definition line.cpp:266
static QJsonObject toJson(const Route &r)
Serializes one object to JSON.
Definition line.cpp:286
static Route merge(const Route &lhs, const Route &rhs)
Merge two Route instances.
Definition line.cpp:276
KPublicTransport::Line line
Line this route belongs to.
Definition line.h:151
static Route fromJson(const QJsonObject &obj)
Deserialize an object from JSON.
Definition line.cpp:299
Information about an arrival and/or departure of a vehicle at a stop area.
Definition stopover.h:26
static Stopover merge(const Stopover &lhs, const Stopover &rhs)
Merge two departure instances.
Definition stopover.cpp:205
QDateTime expectedDepartureTime
Actual departure time, if available.
Definition stopover.h:45
QString expectedPlatform
Actual departure platform, in case real-time information are available.
Definition stopover.h:54
static QJsonObject toJson(const Stopover &stopover)
Serializes one object to JSON.
Definition stopover.cpp:232
KPublicTransport::Location stopPoint
The stop point of this departure.
Definition stopover.h:64
QDateTime expectedArrivalTime
Actual arrival time, if available.
Definition stopover.h:34
QDateTime scheduledArrivalTime
Planned arrival time.
Definition stopover.h:30
static Stopover fromJson(const QJsonObject &obj)
Deserialize an object from JSON.
Definition stopover.cpp:264
KPublicTransport::Disruption::Effect disruptionEffect
Disruption effect on this arrival or departure, if any.
Definition stopover.h:67
KPublicTransport::Vehicle vehicleLayout
Vehicle coach layout information at this stopover.
Definition stopover.h:77
QDateTime scheduledDepartureTime
Planned departure time.
Definition stopover.h:41
KPublicTransport::Platform platformLayout
Platform layout information.
Definition stopover.h:79
QString scheduledPlatform
Planned departure platform.
Definition stopover.h:52
Information about the vehicle used on a journey.
Definition vehicle.h:159
static QJsonObject toJson(const Vehicle &vehicle)
Serializes one vehicle object to JSON.
Definition vehicle.cpp:264
static Vehicle fromJson(const QJsonObject &obj)
Deserialize an object from JSON.
Definition vehicle.cpp:281
bool isEmpty() const
Returns true if this object contains no information beyond the default values.
Definition vehicle.cpp:176
static Vehicle merge(const Vehicle &lhs, const Vehicle &rhs)
Merge two Vehicle instances.
Definition vehicle.cpp:240
void stop(Ekos::AlignState mode)
Effect
Disruption effects, numerical sorted so that higher values imply more severe disruptions.
Definition disruption.h:25
Query operations and data types for accessing realtime public transport information from online servi...
QAction * back(const QObject *recvr, const char *slot, QObject *parent)
qint64 secsTo(const QDateTime &other) const const
iterator insert(QLatin1StringView key, const QJsonValue &value)
QJsonValue value(QLatin1StringView key) const const
bool isEmpty() const const
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 3 2025 11:46:40 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.