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
9#include "identifier_p.h"
10#include "journeyutil_p.h"
11#include "json_p.h"
12#include "datatypes_p.h"
13#include "loadutil_p.h"
14#include "logging.h"
15#include "mergeutil_p.h"
16#include "notesutil_p.h"
17#include "platformutils_p.h"
18#include "rentalvehicle.h"
19#include "rentalvehicleutil_p.h"
20#include "stopover.h"
21
22#include <KLocalizedString>
23
24#include <QDebug>
25#include <QVariant>
26
27using namespace Qt::Literals::StringLiterals;
28using namespace KPublicTransport;
29
30namespace KPublicTransport {
31
32class JourneySectionPrivate : public QSharedData
33{
34public:
35 JourneySection::Mode mode = JourneySection::Invalid;
36 QDateTime scheduledDepartureTime;
37 QDateTime expectedDepartureTime;
38 QDateTime scheduledArrivalTime;
39 QDateTime expectedArrivalTime;
40 Location from;
41 Location to;
42 Route route;
43 QString scheduledDeparturePlatform;
44 QString expectedDeparturePlatform;
45 QString scheduledArrivalPlatform;
46 QString expectedArrivalPlatform;
47 int distance = 0;
48 Disruption::Effect disruptionEffect = Disruption::NormalService;
49 QStringList notes;
50 std::vector<Stopover> intermediateStops;
51 int co2Emission = -1;
52 std::vector<LoadInfo> loadInformation;
53 RentalVehicle rentalVehicle;
54 Path path;
55 Vehicle departureVehicleLayout;
56 Platform departurePlatformLayout;
57 Vehicle arrivalVehicleLayout;
58 Platform arrivalPlatformLayout;
59 IndividualTransport individualTransport;
60 IdentifierSet ids;
61};
62
63class JourneyPrivate : public QSharedData
64{
65public:
66 std::vector<JourneySection> sections;
67};
68
69}
70
71KPUBLICTRANSPORT_MAKE_GADGET(JourneySection)
72KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, JourneySection::Mode, mode, setMode)
73KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, QDateTime, scheduledDepartureTime, setScheduledDepartureTime)
74KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, QDateTime, expectedDepartureTime, setExpectedDepartureTime)
75KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, QDateTime, scheduledArrivalTime, setScheduledArrivalTime)
76KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, QDateTime, expectedArrivalTime, setExpectedArrivalTime)
77KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Location, from, setFrom)
78KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Location, to, setTo)
79KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Route, route, setRoute)
80KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Disruption::Effect, disruptionEffect, setDisruptionEffect)
81KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, QStringList, notes, setNotes)
82KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, RentalVehicle, rentalVehicle, setRentalVehicle)
83KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Path, path, setPath)
84KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Vehicle, departureVehicleLayout, setDepartureVehicleLayout)
85KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Platform, departurePlatformLayout, setDeparturePlatformLayout)
86KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Vehicle, arrivalVehicleLayout, setArrivalVehicleLayout)
87KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Platform, arrivalPlatformLayout, setArrivalPlatformLayout)
88KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, KPublicTransport::IndividualTransport, individualTransport, setIndividualTransport)
89
91{
92 return d->expectedDepartureTime.isValid();
93}
94
96{
98 return d->scheduledDepartureTime.secsTo(d->expectedDepartureTime) / 60;
99 }
100 return 0;
101}
102
104{
105 return d->expectedArrivalTime.isValid();
106}
107
109{
111 return d->scheduledArrivalTime.secsTo(d->expectedArrivalTime) / 60;
112 }
113 return 0;
114}
115
116int JourneySection::duration() const
117{
118 return d->scheduledDepartureTime.secsTo(d->scheduledArrivalTime);
119}
120
121int JourneySection::distance() const
122{
123 if (d->mode == JourneySection::Waiting) {
124 return 0;
125 }
126
127 double dist = 0;
128 if (d->from.hasCoordinate() && d->to.hasCoordinate()) {
129 auto startLat = d->from.latitude();
130 auto startLon = d->from.longitude();
131
132 for (const auto &stop : d->intermediateStops) {
133 if (!stop.stopPoint().hasCoordinate()) {
134 continue;
135 }
136 dist += Location::distance(startLat, startLon, stop.stopPoint().latitude(), stop.stopPoint().longitude());
137 startLat = stop.stopPoint().latitude();
138 startLon = stop.stopPoint().longitude();
139 }
140
141 dist += Location::distance(startLat, startLon, d->to.latitude(), d->to.longitude());
142 }
143 dist = std::max<double>(dist, d->path.distance());
144 return std::max((int)std::round(dist), d->distance);
145}
146
147void JourneySection::setDistance(int value)
148{
149 d.detach();
150 d->distance = value;
151}
152
154{
155 return d->scheduledDeparturePlatform;
156}
157
158void JourneySection::setScheduledDeparturePlatform(const QString &platform)
159{
160 d.detach();
161 d->scheduledDeparturePlatform = PlatformUtils::normalizePlatform(platform);
162}
163
165{
166 return d->expectedDeparturePlatform;
167}
168
169void JourneySection::setExpectedDeparturePlatform(const QString &platform)
170{
171 d.detach();
172 d->expectedDeparturePlatform = PlatformUtils::normalizePlatform(platform);
173}
174
176{
177 return !d->expectedDeparturePlatform.isEmpty();
178}
179
181{
182 return PlatformUtils::platformChanged(d->scheduledDeparturePlatform, d->expectedDeparturePlatform);
183}
184
186{
187 return d->scheduledArrivalPlatform;
188}
189
190void JourneySection::setScheduledArrivalPlatform(const QString &platform)
191{
192 d.detach();
193 d->scheduledArrivalPlatform = PlatformUtils::normalizePlatform(platform);
194}
195
197{
198 return d->expectedArrivalPlatform;
199}
200
201void JourneySection::setExpectedArrivalPlatform(const QString &platform)
202{
203 d.detach();
204 d->expectedArrivalPlatform = PlatformUtils::normalizePlatform(platform);
205}
206
208{
209 return !d->expectedArrivalPlatform.isEmpty();
210}
211
213{
214 return PlatformUtils::platformChanged(d->scheduledArrivalPlatform, d->expectedArrivalPlatform);
215}
216
218{
219 const auto n = NotesUtil::normalizeNote(note);
220 const auto idx = NotesUtil::needsAdding(d->notes, n);
221 if (idx >= 0) {
222 d.detach();
223 NotesUtil::performAdd(d->notes, n, idx);
224 }
225}
226
227void JourneySection::addNotes(const QStringList &notes)
228{
229 for (const auto &n : notes) {
230 addNote(n);
231 }
232}
233
234const std::vector<Stopover>& JourneySection::intermediateStops() const
235{
236 return d->intermediateStops;
237}
238
240{
241 d.detach();
242 return std::move(d->intermediateStops);
243}
244
245void JourneySection::setIntermediateStops(std::vector<Stopover> &&stops)
246{
247 d.detach();
248 d->intermediateStops = std::move(stops);
249}
250
251QVariantList JourneySection::intermediateStopsVariant() const
252{
253 QVariantList l;
254 l.reserve(d->intermediateStops.size());
255 std::transform(d->intermediateStops.begin(), d->intermediateStops.end(), std::back_inserter(l), [](const auto &stop) { return QVariant::fromValue(stop); });
256 return l;
257}
258
260{
261 Stopover dep;
262 dep.setStopPoint(from());
263 dep.setRoute(route());
264 dep.setScheduledDepartureTime(scheduledDepartureTime());
265 dep.setExpectedDepartureTime(expectedDepartureTime());
266 dep.setScheduledPlatform(scheduledDeparturePlatform());
267 dep.setExpectedPlatform(expectedDeparturePlatform());
268 dep.addNotes(notes());
269 dep.setDisruptionEffect(disruptionEffect());
270 dep.setVehicleLayout(departureVehicleLayout());
271 dep.setPlatformLayout(departurePlatformLayout());
272 return dep;
273}
274
276{
277 setFrom(departure.stopPoint());
278 setScheduledDepartureTime(departure.scheduledDepartureTime());
279 setExpectedDepartureTime(departure.expectedDepartureTime());
280 setScheduledDeparturePlatform(departure.scheduledPlatform());
281 setExpectedDeparturePlatform(departure.expectedPlatform());
282 setDeparturePlatformLayout(departure.platformLayout());
283 setDepartureVehicleLayout(departure.vehicleLayout());
284 if (departure.disruptionEffect() == Disruption::NoService) {
285 setDisruptionEffect(departure.disruptionEffect());
286 }
287}
288
290{
291 Stopover arr;
292 arr.setStopPoint(to());
293 arr.setRoute(route());
294 arr.setScheduledArrivalTime(scheduledArrivalTime());
295 arr.setExpectedArrivalTime(expectedArrivalTime());
296 arr.setScheduledPlatform(scheduledArrivalPlatform());
297 arr.setExpectedPlatform(expectedArrivalPlatform());
298 arr.setDisruptionEffect(disruptionEffect());
299 arr.setVehicleLayout(arrivalVehicleLayout());
300 arr.setPlatformLayout(arrivalPlatformLayout());
301 return arr;
302}
303
305{
306 setTo(arrival.stopPoint());
307 setScheduledArrivalTime(arrival.scheduledArrivalTime());
308 setExpectedArrivalTime(arrival.expectedArrivalTime());
309 setScheduledArrivalPlatform(arrival.scheduledPlatform());
310 setExpectedArrivalPlatform(arrival.expectedPlatform());
311 setArrivalPlatformLayout(arrival.platformLayout());
312 setArrivalVehicleLayout(arrival.vehicleLayout());
313 if (arrival.disruptionEffect() == Disruption::NoService) {
314 setDisruptionEffect(arrival.disruptionEffect());
315 }
316}
317
318struct {
319 Line::Mode mode;
320 int gramPerKm;
321} static constexpr const emissionForModeMap[] = {
322 { Line::Air, 285 },
323 { Line::Boat, 245 },
324 { Line::Bus, 68 },
325 { Line::Coach, 68 },
326 { Line::Ferry, 245 },
327 // { Line::Funicular, -1 }, TODO
328 { Line::LocalTrain, 14 },
329 { Line::LongDistanceTrain, 14 },
330 { Line::Metro, 11 },
331 { Line::RailShuttle, 11 }, // assuming tram/rapid transit-like
332 { Line::RapidTransit, 11 },
333 { Line::Shuttle, 68 },
334 { Line::Taxi, 158 },
335 { Line::Train, 14 },
336 { Line::Tramway, 11 },
337 { Line::RideShare, 158 },
338 // { Line::AerialLift, -1 }, TODO
339};
340
341struct {
343 int gramPerKm;
344} static constexpr const emissionForIvModeMap[] = {
345 { IndividualTransport::Walk, 0 },
346 { IndividualTransport::Bike, 0 },
347 { IndividualTransport::Car, 158 }
348};
349
350struct {
352 int gramPerKm;
353} static constexpr const emissionForRvModeMap[] = {
355 // { RentalVehicle::Pedelec, -1 }, TODO
356 // { RentalVehicle::ElectricKickScooter, -1 }, TODO
357 // { RentalVehicle::ElectricMoped, -1 }, TODO
358 { RentalVehicle::Car, 158 },
359};
360
362{
363 if (d->co2Emission >= 0) {
364 return d->co2Emission;
365 }
366
367 switch (d->mode) {
368 case JourneySection::Invalid:
369 return -1;
370 case JourneySection::Walking:
371 case JourneySection::Transfer:
372 case JourneySection::Waiting:
373 return 0;
374 case JourneySection::PublicTransport:
375 {
376 const auto mode = route().line().mode();
377 for (const auto &map : emissionForModeMap) {
378 if (map.mode == mode) {
379 return (map.gramPerKm * distance()) / 1000;
380 }
381 }
382 qCDebug(Log) << "No CO2 emission estimate for mode" << mode;
383 return -1;
384 }
386 {
387 const auto mode = individualTransport().mode();
388 for (const auto &map :emissionForIvModeMap) {
389 if (map.mode == mode) {
390 return (map.gramPerKm *distance()) / 1000;
391 }
392 }
393 qCDebug(Log) << "No CO2 emission estimate for mode" << mode;
394 return -1;
395 }
397 {
398 const auto mode = rentalVehicle().type();
399 for (const auto &map :emissionForRvModeMap) {
400 if (map.mode == mode) {
401 return (map.gramPerKm *distance()) / 1000;
402 }
403 }
404 qCDebug(Log) << "No CO2 emission estimate for vehicle type" << mode;
405 return -1;
406 }
407 }
408
409 return -1;
410}
411
412void JourneySection::setCo2Emission(int value)
413{
414 d.detach();
415 d->co2Emission = value;
416}
417
418const std::vector<LoadInfo>& JourneySection::loadInformation() const
419{
420 return d->loadInformation;
421}
422
423std::vector<LoadInfo>&& JourneySection::takeLoadInformation()
424{
425 d.detach();
426 return std::move(d->loadInformation);
427}
428
429void JourneySection::setLoadInformation(std::vector<LoadInfo> &&loadInfo)
430{
431 d.detach();
432 d->loadInformation = std::move(loadInfo);
433}
434
435QList<LoadInfo> JourneySection::loadInformationList() const
436{
438 l.reserve((qsizetype)d->loadInformation.size());
439 std::copy(d->loadInformation.begin(), d->loadInformation.end(), std::back_inserter(l));
440 return l;
441}
442
443void JourneySection::setLoadInformationList(const QList<LoadInfo> &loadInfo)
444{
445 d->loadInformation.clear();
446 d->loadInformation.reserve(loadInfo.size());
447 std::copy(loadInfo.begin(), loadInfo.end(), std::back_inserter(d->loadInformation));
448}
449
450const std::vector<KPublicTransport::Feature>& JourneySection::features() const
451{
452 return d->departureVehicleLayout.features();
453}
454
455[[nodiscard]] std::vector<KPublicTransport::Feature>&& JourneySection::takeFeatures()
456{
457 return d->departureVehicleLayout.takeFeatures();
458}
459
460void JourneySection::setFeatures(std::vector<KPublicTransport::Feature> &&features)
461{
462 d.detach();
463 d->departureVehicleLayout.setFeatures(std::move(features));
464}
465
466QString JourneySection::iconName() const
467{
468 switch (d->mode) {
469 case JourneySection::Invalid:
470 return {};
471 case JourneySection::PublicTransport:
472 return d->route.line().iconName();
474 return d->rentalVehicle.vehicleTypeIconName();
476 return d->individualTransport.modeIconName();
477 case JourneySection::Transfer:
478 case JourneySection::Walking:
479 case JourneySection::Waiting:
480 break;
481 }
482
483 return modeIconName(d->mode);
484}
485
487{
488 switch (mode) {
489 case JourneySection::Invalid:
490 return {};
491 case JourneySection::PublicTransport:
492 return Line::modeIconName(Line::Train);
493 case JourneySection::Transfer:
494 return u"qrc:///org.kde.kpublictransport/assets/images/journey-mode-transfer.svg"_s;
495 case JourneySection::Walking:
496 return IndividualTransport::modeIconName(IndividualTransport::Walk);
497 case JourneySection::Waiting:
498 return u"qrc:///org.kde.kpublictransport/assets/images/journey-mode-wait.svg"_s;
502 return IndividualTransport::modeIconName(IndividualTransport::Bike);
503 }
504
505 return u"question"_s;
506}
507
509{
510 switch (mode()) {
511 case JourneySection::Invalid:
512 break;
513 case JourneySection::PublicTransport:
514 return route().line().name();
515 case JourneySection::Walking:
516 return i18nc("mode of individual transport", "Walk");
517 case JourneySection::Waiting:
518 return i18n("Wait");
519 case JourneySection::Transfer:
520 break; // ?
522 return rentalVehicle().label();
524 return individualTransport().label();
525 }
526
527 return {};
528}
529
531{
532 return std::accumulate(d->loadInformation.begin(), d->loadInformation.end(), Load::Unknown, [](auto l, const auto &info) {
533 return std::max(l, info.load());
534 });
535}
536
538{
539 return d->ids.identifier(identifierType);
540}
541
542bool JourneySection::hasIdentifier(QAnyStringView identifierType) const
543{
544 return d->ids.hasIdentifier(identifierType);
545}
546
547void JourneySection::setIdentifier(const QString &identifierType, const QString &id)
548{
549 d.detach();
550 d->ids.setIdentifier(identifierType, id);
551}
552
554{
555 return !d->ids.isEmpty();
556}
557
559{
560 if (!from().hasCoordinate() || mode() != JourneySection::PublicTransport) {
561 return;
562 }
563 auto line = d->route.line();
564 line.applyMetaData(from(), download);
565 d->route.setLine(line);
566
567 // propagate to intermediate stops
568 for (auto &stop : d->intermediateStops) {
569 stop.setRoute(d->route);
570 }
571}
572
574{
575 if (lhs.d->mode != rhs.d->mode) {
576 return false;
577 }
578
580 return false;
581 }
582
583 switch (lhs.d->ids.compare(rhs.d->ids)) {
584 case IdentifierSet::NotEqual: return false;
585 case IdentifierSet::Equal: // same trip id can still mean diffierent departure/arrival stops
586 case IdentifierSet::NoIntersection: break;
587 }
588
589 // we have N criteria to compare here, with 3 possible results:
590 // - equal
591 // - similar-ish, unknwon, or at least not conflicting
592 // - conflicting
593 // A single conflict results in a negative result, at least N - 1 equal comparisons lead to
594 // in a positive result.
595 enum { Equal = 1, Compatible = 0, Conflict = -1000 };
596 int result = 0;
597
598 const auto depTimeDist = MergeUtil::distance(lhs.d->scheduledDepartureTime, rhs.d->scheduledDepartureTime);
599 result += depTimeDist < 60 ? Equal : depTimeDist <= 60 ? Compatible : Conflict;
600 const auto arrTimeDist = MergeUtil::distance(lhs.d->scheduledArrivalTime, rhs.d->scheduledArrivalTime);
601 result += arrTimeDist < 60 ? Equal : depTimeDist <= 60 ? Compatible : Conflict;
602
603 const auto sameFrom = Location::isSame(lhs.d->from, rhs.d->from);
604 const auto fromDist = Location::distance(lhs.from(), rhs.from());
605 result += sameFrom ? Equal : fromDist < 200 ? Compatible : Conflict;
606
607 const auto sameTo = Location::isSame(lhs.d->to, rhs.d->to);
608 const auto toDist = Location::distance(lhs.to(), rhs.to());
609 result += sameTo ? Equal : toDist < 200 ? Compatible : Conflict;
610
611 const auto sameRoute = Route::isSame(lhs.d->route, rhs.d->route);
612 const auto sameDir = Location::isSameName(lhs.d->route.direction(), rhs.d->route.direction());
613 const auto sameLine = Line::isSame(lhs.d->route.line(), rhs.d->route.line());
614 result += sameRoute ? Equal : (sameDir || sameLine) ? Compatible : Conflict;
615
617 result += lhs.scheduledDeparturePlatform() == rhs.scheduledDeparturePlatform() ? Equal : Conflict;
618 }
619
620 return result >= 4;
621}
622
624{
625 using namespace MergeUtil;
626 auto res = lhs;
627 res.d->ids.merge(rhs.d->ids);
628 res.setScheduledDepartureTime(mergeDateTimeEqual(lhs.scheduledDepartureTime(), rhs.scheduledDepartureTime()));
629 res.setExpectedDepartureTime(mergeDateTimeMax(lhs.expectedDepartureTime(), rhs.expectedDepartureTime()));
630 res.setScheduledArrivalTime(mergeDateTimeMax(lhs.scheduledArrivalTime(), rhs.scheduledArrivalTime()));
631 res.setExpectedArrivalTime(mergeDateTimeMax(lhs.expectedArrivalTime(), rhs.expectedArrivalTime()));
632
633 if (res.expectedDeparturePlatform().isEmpty()) {
634 res.setExpectedDeparturePlatform(rhs.expectedDeparturePlatform());
635 }
636 if (res.expectedArrivalPlatform().isEmpty()) {
637 res.setExpectedArrivalPlatform(rhs.expectedArrivalPlatform());
638 }
639 res.setFrom(Location::merge(lhs.from(), rhs.from()));
640 res.setTo(Location::merge(lhs.to(), rhs.to()));
641 res.setRoute(Route::merge(lhs.route(), rhs.route()));
642
643 res.setScheduledDeparturePlatform(mergeString(lhs.scheduledDeparturePlatform(), rhs.scheduledDeparturePlatform()));
644 res.setScheduledArrivalPlatform(mergeString(lhs.scheduledArrivalPlatform(), rhs.scheduledArrivalPlatform()));
645
646 res.setDisruptionEffect(std::max(lhs.disruptionEffect(), rhs.disruptionEffect()));
647 res.setNotes(NotesUtil::mergeNotes(lhs.notes(), rhs.notes()));
648 res.setDistance(std::max(lhs.distance(), rhs.distance()));
649
650 if (lhs.intermediateStops().size() == rhs.intermediateStops().size()) {
651 auto stops = res.takeIntermediateStops();
652 for (uint i = 0; i < stops.size(); ++i) {
653 stops[i] = Stopover::merge(stops[i], rhs.intermediateStops()[i]);
654 stops[i].setRoute(res.route());
655 }
656 res.setIntermediateStops(std::move(stops));
657 }
658
659 res.d->co2Emission = std::max(lhs.d->co2Emission, rhs.d->co2Emission);
660 res.d->loadInformation = LoadUtil::merge(lhs.d->loadInformation, rhs.d->loadInformation);
661 res.d->rentalVehicle = RentalVehicleUtil::merge(lhs.d->rentalVehicle, rhs.d->rentalVehicle);
662
663 res.d->path = lhs.d->path.sections().size() < rhs.d->path.sections().size() ? rhs.d->path : lhs.d->path;
664
665 res.d->departureVehicleLayout = Vehicle::merge(lhs.d->departureVehicleLayout, rhs.d->departureVehicleLayout);
666 res.d->departurePlatformLayout = Platform::merge(lhs.d->departurePlatformLayout, rhs.d->departurePlatformLayout);
667 res.d->arrivalVehicleLayout = Vehicle::merge(lhs.d->arrivalVehicleLayout, rhs.d->arrivalVehicleLayout);
668 res.d->arrivalPlatformLayout = Platform::merge(lhs.d->arrivalPlatformLayout, rhs.d->arrivalPlatformLayout);
669
670 return res;
671}
672
674{
675 auto obj = Json::toJson(section);
676 if (!section.d->ids.isEmpty()) {
677 obj.insert("identifiers"_L1, section.d->ids.toJson());
678 }
679 if (section.mode() != Waiting) {
680 const auto fromObj = Location::toJson(section.from());
681 if (!fromObj.empty()) {
682 obj.insert(QLatin1String("from"), fromObj);
683 }
684 const auto toObj = Location::toJson(section.to());
685 if (!toObj.empty()) {
686 obj.insert(QLatin1String("to"), toObj);
687 }
688 }
689 if (section.mode() == PublicTransport) {
690 const auto routeObj = Route::toJson(section.route());
691 if (!routeObj.empty()) {
692 obj.insert(QLatin1String("route"), routeObj);
693 }
694 if (!section.intermediateStops().empty()) {
695 obj.insert(QLatin1String("intermediateStops"), Stopover::toJson(section.intermediateStops()));
696 }
697 if (!section.loadInformation().empty()) {
698 obj.insert(QLatin1String("load"), LoadInfo::toJson(section.loadInformation()));
699 }
700 }
701 if (section.d->co2Emission < 0) {
702 obj.remove(QLatin1String("co2Emission"));
703 }
704 if (section.rentalVehicle().type() != RentalVehicle::Unknown) {
705 obj.insert(QLatin1String("rentalVehicle"), RentalVehicle::toJson(section.rentalVehicle()));
706 }
707
708 if (!section.path().isEmpty()) {
709 obj.insert(QLatin1String("path"), Path::toJson(section.path()));
710 }
711
712 if (!section.departureVehicleLayout().isEmpty()) {
713 obj.insert(QLatin1String("departureVehicleLayout"), Vehicle::toJson(section.departureVehicleLayout()));
714 }
715 if (!section.departurePlatformLayout().isEmpty()) {
716 obj.insert(QLatin1String("departurePlatformLayout"), Platform::toJson(section.departurePlatformLayout()));
717 }
718 if (!section.arrivalVehicleLayout().isEmpty()) {
719 obj.insert(QLatin1String("arrivalVehicleLayout"), Vehicle::toJson(section.arrivalVehicleLayout()));
720 }
721 if (!section.arrivalPlatformLayout().isEmpty()) {
722 obj.insert(QLatin1String("arrivalPlatformLayout"), Platform::toJson(section.arrivalPlatformLayout()));
723 }
724
725 if (section.mode() == JourneySection::IndividualTransport) {
726 obj.insert(QLatin1String("individualTransport"), IndividualTransport::toJson(section.individualTransport()));
727 }
728
729 if (obj.size() <= 3) { // only the disruption and mode enums and distance, ie. this is an empty object
730 return {};
731 }
732 return obj;
733}
734
735QJsonArray JourneySection::toJson(const std::vector<JourneySection> &sections)
736{
737 return Json::toJson(sections);
738}
739
741{
742 auto section = Json::fromJson<JourneySection>(obj);
743 section.d->ids.fromJson(obj.value("identifiers"_L1).toObject());
744 section.setFrom(Location::fromJson(obj.value(QLatin1String("from")).toObject()));
745 section.setTo(Location::fromJson(obj.value(QLatin1String("to")).toObject()));
746 section.setRoute(Route::fromJson(obj.value(QLatin1String("route")).toObject()));
747 section.setIntermediateStops(Stopover::fromJson(obj.value(QLatin1String("intermediateStops")).toArray()));
748 section.setLoadInformation(LoadInfo::fromJson(obj.value(QLatin1String("load")).toArray()));
749 section.setRentalVehicle(RentalVehicle::fromJson(obj.value(QLatin1String("rentalVehicle")).toObject()));
750 section.setPath(Path::fromJson(obj.value(QLatin1String("path")).toObject()));
751 section.setDepartureVehicleLayout(Vehicle::fromJson(obj.value(QLatin1String("departureVehicleLayout")).toObject()));
752 section.setDeparturePlatformLayout(Platform::fromJson(obj.value(QLatin1String("departurePlatformLayout")).toObject()));
753 section.setArrivalVehicleLayout(Vehicle::fromJson(obj.value(QLatin1String("arrivalVehicleLayout")).toObject()));
754 section.setArrivalPlatformLayout(Platform::fromJson(obj.value(QLatin1String("arrivalPlatformLayout")).toObject()));
755 section.setIndividualTransport(IndividualTransport::fromJson(obj.value(QLatin1String("individualTransport")).toObject()));
756 section.applyMetaData(false);
757 return section;
758}
759
760std::vector<JourneySection> JourneySection::fromJson(const QJsonArray &array)
761{
762 return Json::fromJson<JourneySection>(array);
763}
764
765
766KPUBLICTRANSPORT_MAKE_GADGET(Journey)
767
768const std::vector<JourneySection>& Journey::sections() const
769{
770 return d->sections;
771}
772
773std::vector<JourneySection>&& Journey::takeSections()
774{
775 d.detach();
776 return std::move(d->sections);
777}
778
779void Journey::setSections(std::vector<JourneySection> &&sections)
780{
781 d.detach();
782 d->sections = std::move(sections);
783}
784
785QList<JourneySection> Journey::sectionsList() const
786{
788 l.reserve((qsizetype)d->sections.size());
789 std::copy(d->sections.begin(), d->sections.end(), std::back_inserter(l));
790 return l;
791}
792
793void Journey::setSectionsList(const QList<JourneySection> &sections)
794{
795 d->sections.clear();
796 d->sections.reserve(sections.size());
797 std::copy(sections.begin(), sections.end(), std::back_inserter(d->sections));
798}
799
800QDateTime Journey::scheduledDepartureTime() const
801{
802 if (!d->sections.empty()) {
803 return d->sections.front().scheduledDepartureTime();
804 }
805 return {};
806}
807
809{
810 return d->sections.empty() ? false : d->sections.front().hasExpectedDepartureTime();
811}
812
813QDateTime Journey::expectedDepartureTime() const
814{
815 return d->sections.empty() ? QDateTime() : d->sections.front().expectedDepartureTime();
816}
817
818int Journey::departureDelay() const
819{
820 return d->sections.empty() ? 0 : d->sections.front().departureDelay();
821}
822
823QDateTime Journey::scheduledArrivalTime() const
824{
825 if (!d->sections.empty()) {
826 return d->sections.back().scheduledArrivalTime();
827 }
828 return {};
829}
830
832{
833 return d->sections.empty() ? false : d->sections.back().hasExpectedArrivalTime();
834}
835
836QDateTime Journey::expectedArrivalTime() const
837{
838 return d->sections.empty() ? QDateTime() : d->sections.back().expectedArrivalTime();
839}
840
841int Journey::arrivalDelay() const
842{
843 return d->sections.empty() ? 0 : d->sections.back().arrivalDelay();
844}
845
846int Journey::duration() const
847{
849}
850
851int Journey::numberOfChanges() const
852{
853 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));
854}
855
857{
858 Disruption::Effect effect = Disruption::NormalService;
859 for (const auto &sec : d->sections) {
860 effect = std::max(effect, sec.disruptionEffect());
861 }
862 return effect;
863}
864
865int Journey::distance() const
866{
867 return std::accumulate(d->sections.begin(), d->sections.end(), 0, [](auto dist, const auto &jny) { return dist + jny.distance(); });
868}
869
870int Journey::co2Emission() const
871{
872 return std::accumulate(d->sections.begin(), d->sections.end(), 0, [](auto co2, const auto &jny) { return co2 + std::max(0, jny.co2Emission()); });
873}
874
876{
877 return std::accumulate(d->sections.begin(), d->sections.end(), Load::Unknown, [](auto l, const auto &jny) {
878 return std::max(l, jny.maximumOccupancy());
879 });
880}
881
882void Journey::applyMetaData(bool download)
883{
884 for (auto &sec : d->sections) {
885 sec.applyMetaData(download);
886 }
887}
888
889static bool isTransportSection(JourneySection::Mode mode)
890{
891 return mode == JourneySection::PublicTransport
894}
895
896bool Journey::isSame(const Journey &lhs, const Journey &rhs)
897{
898 auto lIt = lhs.sections().begin();
899 auto rIt = rhs.sections().begin();
900
901 while (lIt != lhs.sections().end() || rIt != rhs.sections().end()) {
902 // ignore non-transport sections
903 if (lIt != lhs.sections().end() && !isTransportSection((*lIt).mode())) {
904 ++lIt;
905 continue;
906 }
907 if (rIt != rhs.sections().end() && !isTransportSection((*rIt).mode())) {
908 ++rIt;
909 continue;
910 }
911
912 if (lIt == lhs.sections().end() || rIt == rhs.sections().end()) {
913 return false;
914 }
915
916 if (!JourneySection::isSame(*lIt, *rIt)) {
917 return false;
918 }
919
920 ++lIt;
921 ++rIt;
922 }
923
924 Q_ASSERT(lIt == lhs.sections().end() && rIt == rhs.sections().end());
925 return true;
926}
927
928Journey Journey::merge(const Journey &lhs, const Journey &rhs)
929{
930 std::vector<JourneySection> sections;
931 sections.reserve(lhs.sections().size() + rhs.sections().size());
932 std::copy(lhs.sections().begin(), lhs.sections().end(), std::back_inserter(sections));
933 std::copy(rhs.sections().begin(), rhs.sections().end(), std::back_inserter(sections));
934 std::sort(sections.begin(), sections.end(), [](const auto &lSec, const auto &rSec) {
935 if (MergeUtil::distance(lSec.scheduledDepartureTime(), rSec.scheduledDepartureTime()) == 0) {
936 return lSec.mode() < rSec.mode();
937 }
938 return MergeUtil::isBefore(lSec.scheduledDepartureTime(), rSec.scheduledDepartureTime());
939 });
940
941 for (auto it = sections.begin(); it != sections.end(); ++it) {
942 const auto nextIt = it + 1;
943 if (nextIt == sections.end()) {
944 break;
945 }
946
947 if (JourneySection::isSame(*it, *nextIt) || ((*it).mode() == (*nextIt).mode() && (*it).mode() != JourneySection::PublicTransport)) {
948 *it = JourneySection::merge(*it, *nextIt);
949 sections.erase(nextIt);
950 }
951 }
952
953 Journey res;
954 res.setSections(std::move(sections));
955 return res;
956}
957
959{
960 QJsonObject obj;
961 obj.insert(QLatin1String("sections"), JourneySection::toJson(journey.sections()));
962 return obj;
963}
964
965QJsonArray Journey::toJson(const std::vector<Journey> &journeys)
966{
967 return Json::toJson(journeys);
968}
969
971{
972 Journey j;
973 j.setSections(JourneySection::fromJson(obj.value(QLatin1String("sections")).toArray()));
974 return j;
975}
976
977std::vector<Journey> Journey::fromJson(const QJsonArray &array)
978{
979 return Json::fromJson<Journey>(array);
980}
981
982#include "moc_journey.cpp"
Individual transport mode details for a journey section, and for specifying journey requests.
QString label
Label shortly describing this transport for display.
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:558
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:623
KPublicTransport::Load::Category maximumOccupancy
Maximum occpancy over all classes.
Definition journey.h:173
void setDeparture(const Stopover &departure)
Sets all departure properties from a given Stopover.
Definition journey.cpp:275
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:573
void setLoadInformation(std::vector< LoadInfo > &&loadInfo)
Set the vehicle load information for this journey section.
Definition journey.cpp:429
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
QList< KPublicTransport::LoadInfo > loadInformation
Vehicle load information for this journey section.
Definition journey.h:132
int departureDelay
Difference to schedule in minutes.
Definition journey.h:64
void setIntermediateStops(std::vector< Stopover > &&stops)
Set the intermediate stops.
Definition journey.cpp:245
QString label
Label shortly describing this transport for display.
Definition journey.h:170
static Q_INVOKABLE QString modeIconName(KPublicTransport::JourneySection::Mode mode)
Icon representing the journey section mode mode.
Definition journey.cpp:486
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
bool hasIdentifiers() const
Returns true if there is any identifier set at all.
Definition journey.cpp:553
QString identifier(QAnyStringView identifierType) const
Backend-specific journey section identifiers.
Definition journey.cpp:537
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:304
static QJsonObject toJson(const JourneySection &section)
Serializes one journey section to JSON.
Definition journey.cpp:673
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
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:239
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:740
std::vector< LoadInfo > && takeLoadInformation()
Moves the load information out of this object for modification.
Definition journey.cpp:423
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:217
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:291
void applyMetaData(bool download)
Augment line meta data.
Definition journey.cpp:882
KPublicTransport::Disruption::Effect disruptionEffect
Worst disruption effect of any of the journey sections.
Definition journey.h:322
static Journey fromJson(const QJsonObject &obj)
Deserialize an object from JSON.
Definition journey.cpp:970
QDateTime expectedDepartureTime
Actual departure time, if available.
Definition journey.h:302
QDateTime scheduledDepartureTime
Departure time of the journey, according to schedule.
Definition journey.h:296
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:896
QDateTime expectedArrivalTime
Actual arrival time, if available.
Definition journey.h:313
int departureDelay
Difference to schedule in minutes.
Definition journey.h:304
int numberOfChanges
Number of changes on this journey.
Definition journey.h:320
void setSections(std::vector< JourneySection > &&sections)
Sets the journey sections.
Definition journey.cpp:779
KPublicTransport::Load::Category maximumOccupancy
Maximum occpancy in all journey sections, over all classes.
Definition journey.h:334
QList< KPublicTransport::JourneySection > sections
Journey sections for consumption by QML.
Definition journey.h:294
static QJsonObject toJson(const Journey &journey)
Serializes one journey object to JSON.
Definition journey.cpp:958
bool hasExpectedDepartureTime
true if this has real-time data.
Definition journey.h:298
int duration
Duration of the entire journey in seconds.
Definition journey.h:318
QDateTime scheduledArrivalTime
Arrival time of the journey, according to schedule.
Definition journey.h:307
int distance
Total travelled distance of the entire journey in meter.
Definition journey.h:327
int arrivalDelay
Difference to schedule in minutes.
Definition journey.h:315
int co2Emission
Total CO2 emissions for the entire journey in gram.
Definition journey.h:331
std::vector< JourneySection > && takeSections()
Moves the journey sections out of this object.
Definition journey.cpp:773
static Journey merge(const Journey &lhs, const Journey &rhs)
Merge two instances.
Definition journey.cpp:928
bool hasExpectedArrivalTime
true if this has real-time data.
Definition journey.h:309
KPublicTransport::Line::Mode mode
Type of transport.
Definition line.h:60
QString name
Name of the line.
Definition line.h:50
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:539
static bool isSameName(const QString &lhs, const QString &rhs)
Checks if two location names refer to the same location.
Definition location.cpp:360
static QJsonObject toJson(const Location &loc)
Serializes one Location object to JSON.
Definition location.cpp:478
static double distance(double lat1, double lon1, double lat2, double lon2)
Compute the distance between two geo coordinates, in meters.
Definition location.cpp:458
static Location merge(const Location &lhs, const Location &rhs)
Merge two departure instances.
Definition location.cpp:407
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:312
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.
QString label
Label shortly describing this transport for display.
@ 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:212
static QJsonObject toJson(const Stopover &stopover)
Serializes one object to JSON.
Definition stopover.cpp:239
static Stopover fromJson(const QJsonObject &obj)
Deserialize an object from JSON.
Definition stopover.cpp:271
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)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
Effect
Disruption effects, numerical sorted so that higher values imply more severe disruptions.
Definition disruption.h:25
Category
Vehicle load categories.
Definition load.h:20
@ Unknown
no load information are available
Definition load.h:21
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
QJsonObject toObject() const const
iterator begin()
void clear()
iterator end()
iterator erase(const_iterator begin, const_iterator end)
void reserve(qsizetype size)
qsizetype size() 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 31 2025 11:52:18 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.