File indexing completed on 2024-05-12 04:42:43
0001 /* 0002 SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "journey.h" 0008 #include "journeyutil_p.h" 0009 #include "json_p.h" 0010 #include "datatypes_p.h" 0011 #include "loadutil_p.h" 0012 #include "mergeutil_p.h" 0013 #include "notesutil_p.h" 0014 #include "platformutils_p.h" 0015 #include "rentalvehicle.h" 0016 #include "rentalvehicleutil_p.h" 0017 #include "stopover.h" 0018 0019 #include <QDebug> 0020 #include <QVariant> 0021 0022 using namespace KPublicTransport; 0023 0024 namespace KPublicTransport { 0025 0026 class JourneySectionPrivate : public QSharedData 0027 { 0028 public: 0029 JourneySection::Mode mode = JourneySection::Invalid; 0030 QDateTime scheduledDepartureTime; 0031 QDateTime expectedDepartureTime; 0032 QDateTime scheduledArrivalTime; 0033 QDateTime expectedArrivalTime; 0034 Location from; 0035 Location to; 0036 Route route; 0037 QString scheduledDeparturePlatform; 0038 QString expectedDeparturePlatform; 0039 QString scheduledArrivalPlatform; 0040 QString expectedArrivalPlatform; 0041 int distance = 0; 0042 Disruption::Effect disruptionEffect = Disruption::NormalService; 0043 QStringList notes; 0044 std::vector<Stopover> intermediateStops; 0045 int co2Emission = -1; 0046 std::vector<LoadInfo> loadInformation; 0047 RentalVehicle rentalVehicle; 0048 Path path; 0049 Vehicle departureVehicleLayout; 0050 Platform departurePlatformLayout; 0051 Vehicle arrivalVehicleLayout; 0052 Platform arrivalPlatformLayout; 0053 IndividualTransport individualTransport; 0054 }; 0055 0056 class JourneyPrivate : public QSharedData 0057 { 0058 public: 0059 std::vector<JourneySection> sections; 0060 }; 0061 0062 } 0063 0064 KPUBLICTRANSPORT_MAKE_GADGET(JourneySection) 0065 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, JourneySection::Mode, mode, setMode) 0066 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, QDateTime, scheduledDepartureTime, setScheduledDepartureTime) 0067 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, QDateTime, expectedDepartureTime, setExpectedDepartureTime) 0068 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, QDateTime, scheduledArrivalTime, setScheduledArrivalTime) 0069 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, QDateTime, expectedArrivalTime, setExpectedArrivalTime) 0070 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Location, from, setFrom) 0071 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Location, to, setTo) 0072 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Route, route, setRoute) 0073 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Disruption::Effect, disruptionEffect, setDisruptionEffect) 0074 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, QStringList, notes, setNotes) 0075 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, RentalVehicle, rentalVehicle, setRentalVehicle) 0076 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Path, path, setPath) 0077 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Vehicle, departureVehicleLayout, setDepartureVehicleLayout) 0078 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Platform, departurePlatformLayout, setDeparturePlatformLayout) 0079 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Vehicle, arrivalVehicleLayout, setArrivalVehicleLayout) 0080 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, Platform, arrivalPlatformLayout, setArrivalPlatformLayout) 0081 KPUBLICTRANSPORT_MAKE_PROPERTY(JourneySection, KPublicTransport::IndividualTransport, individualTransport, setIndividualTransport) 0082 0083 bool JourneySection::hasExpectedDepartureTime() const 0084 { 0085 return d->expectedDepartureTime.isValid(); 0086 } 0087 0088 int JourneySection::departureDelay() const 0089 { 0090 if (hasExpectedDepartureTime()) { 0091 return d->scheduledDepartureTime.secsTo(d->expectedDepartureTime) / 60; 0092 } 0093 return 0; 0094 } 0095 0096 bool JourneySection::hasExpectedArrivalTime() const 0097 { 0098 return d->expectedArrivalTime.isValid(); 0099 } 0100 0101 int JourneySection::arrivalDelay() const 0102 { 0103 if (hasExpectedArrivalTime()) { 0104 return d->scheduledArrivalTime.secsTo(d->expectedArrivalTime) / 60; 0105 } 0106 return 0; 0107 } 0108 0109 int JourneySection::duration() const 0110 { 0111 return d->scheduledDepartureTime.secsTo(d->scheduledArrivalTime); 0112 } 0113 0114 int JourneySection::distance() const 0115 { 0116 if (d->mode == JourneySection::Waiting) { 0117 return 0; 0118 } 0119 0120 int dist = 0; 0121 if (d->from.hasCoordinate() && d->to.hasCoordinate()) { 0122 float startLat = d->from.latitude(); 0123 float startLon = d->from.longitude(); 0124 0125 for (const auto &stop : d->intermediateStops) { 0126 if (!stop.stopPoint().hasCoordinate()) { 0127 continue; 0128 } 0129 dist += Location::distance(startLat, startLon, stop.stopPoint().latitude(), stop.stopPoint().longitude()); 0130 startLat = stop.stopPoint().latitude(); 0131 startLon = stop.stopPoint().longitude(); 0132 } 0133 0134 dist += Location::distance(startLat, startLon, d->to.latitude(), d->to.longitude()); 0135 } 0136 dist = std::max(dist, d->path.distance()); 0137 return std::max(dist, d->distance); 0138 } 0139 0140 void JourneySection::setDistance(int value) 0141 { 0142 d.detach(); 0143 d->distance = value; 0144 } 0145 0146 QString JourneySection::scheduledDeparturePlatform() const 0147 { 0148 return d->scheduledDeparturePlatform; 0149 } 0150 0151 void JourneySection::setScheduledDeparturePlatform(const QString &platform) 0152 { 0153 d.detach(); 0154 d->scheduledDeparturePlatform = PlatformUtils::normalizePlatform(platform); 0155 } 0156 0157 QString JourneySection::expectedDeparturePlatform() const 0158 { 0159 return d->expectedDeparturePlatform; 0160 } 0161 0162 void JourneySection::setExpectedDeparturePlatform(const QString &platform) 0163 { 0164 d.detach(); 0165 d->expectedDeparturePlatform = PlatformUtils::normalizePlatform(platform); 0166 } 0167 0168 bool JourneySection::hasExpectedDeparturePlatform() const 0169 { 0170 return !d->expectedDeparturePlatform.isEmpty(); 0171 } 0172 0173 bool JourneySection::departurePlatformChanged() const 0174 { 0175 return PlatformUtils::platformChanged(d->scheduledDeparturePlatform, d->expectedDeparturePlatform); 0176 } 0177 0178 QString JourneySection::scheduledArrivalPlatform() const 0179 { 0180 return d->scheduledArrivalPlatform; 0181 } 0182 0183 void JourneySection::setScheduledArrivalPlatform(const QString &platform) 0184 { 0185 d.detach(); 0186 d->scheduledArrivalPlatform = PlatformUtils::normalizePlatform(platform); 0187 } 0188 0189 QString JourneySection::expectedArrivalPlatform() const 0190 { 0191 return d->expectedArrivalPlatform; 0192 } 0193 0194 void JourneySection::setExpectedArrivalPlatform(const QString &platform) 0195 { 0196 d.detach(); 0197 d->expectedArrivalPlatform = PlatformUtils::normalizePlatform(platform); 0198 } 0199 0200 bool JourneySection::hasExpectedArrivalPlatform() const 0201 { 0202 return !d->expectedArrivalPlatform.isEmpty(); 0203 } 0204 0205 bool JourneySection::arrivalPlatformChanged() const 0206 { 0207 return PlatformUtils::platformChanged(d->scheduledArrivalPlatform, d->expectedArrivalPlatform); 0208 } 0209 0210 void JourneySection::addNote(const QString ¬e) 0211 { 0212 const auto n = NotesUtil::normalizeNote(note); 0213 const auto idx = NotesUtil::needsAdding(d->notes, n); 0214 if (idx >= 0) { 0215 d.detach(); 0216 NotesUtil::performAdd(d->notes, n, idx); 0217 } 0218 } 0219 0220 void JourneySection::addNotes(const QStringList ¬es) 0221 { 0222 for (const auto &n : notes) { 0223 addNote(n); 0224 } 0225 } 0226 0227 const std::vector<Stopover>& JourneySection::intermediateStops() const 0228 { 0229 return d->intermediateStops; 0230 } 0231 0232 std::vector<Stopover>&& JourneySection::takeIntermediateStops() 0233 { 0234 d.detach(); 0235 return std::move(d->intermediateStops); 0236 } 0237 0238 void JourneySection::setIntermediateStops(std::vector<Stopover> &&stops) 0239 { 0240 d.detach(); 0241 d->intermediateStops = std::move(stops); 0242 } 0243 0244 QVariantList JourneySection::intermediateStopsVariant() const 0245 { 0246 QVariantList l; 0247 l.reserve(d->intermediateStops.size()); 0248 std::transform(d->intermediateStops.begin(), d->intermediateStops.end(), std::back_inserter(l), [](const auto &stop) { return QVariant::fromValue(stop); }); 0249 return l; 0250 } 0251 0252 Stopover JourneySection::departure() const 0253 { 0254 Stopover dep; 0255 dep.setStopPoint(from()); 0256 dep.setRoute(route()); 0257 dep.setScheduledDepartureTime(scheduledDepartureTime()); 0258 dep.setExpectedDepartureTime(expectedDepartureTime()); 0259 dep.setScheduledPlatform(scheduledDeparturePlatform()); 0260 dep.setExpectedPlatform(expectedDeparturePlatform()); 0261 dep.addNotes(notes()); 0262 dep.setDisruptionEffect(disruptionEffect()); 0263 dep.setVehicleLayout(departureVehicleLayout()); 0264 dep.setPlatformLayout(departurePlatformLayout()); 0265 return dep; 0266 } 0267 0268 void JourneySection::setDeparture(const Stopover &departure) 0269 { 0270 setFrom(departure.stopPoint()); 0271 setScheduledDepartureTime(departure.scheduledDepartureTime()); 0272 setExpectedDepartureTime(departure.expectedDepartureTime()); 0273 setScheduledDeparturePlatform(departure.scheduledPlatform()); 0274 setExpectedDeparturePlatform(departure.expectedPlatform()); 0275 setDeparturePlatformLayout(departure.platformLayout()); 0276 setDepartureVehicleLayout(departure.vehicleLayout()); 0277 } 0278 0279 Stopover JourneySection::arrival() const 0280 { 0281 Stopover arr; 0282 arr.setStopPoint(to()); 0283 arr.setRoute(route()); 0284 arr.setScheduledArrivalTime(scheduledArrivalTime()); 0285 arr.setExpectedArrivalTime(expectedArrivalTime()); 0286 arr.setScheduledPlatform(scheduledArrivalPlatform()); 0287 arr.setExpectedPlatform(expectedArrivalPlatform()); 0288 arr.setDisruptionEffect(disruptionEffect()); 0289 arr.setVehicleLayout(arrivalVehicleLayout()); 0290 arr.setPlatformLayout(arrivalPlatformLayout()); 0291 return arr; 0292 } 0293 0294 void JourneySection::setArrival(const Stopover &arrival) 0295 { 0296 setTo(arrival.stopPoint()); 0297 setScheduledArrivalTime(arrival.scheduledArrivalTime()); 0298 setExpectedArrivalTime(arrival.expectedArrivalTime()); 0299 setScheduledArrivalPlatform(arrival.scheduledPlatform()); 0300 setExpectedArrivalPlatform(arrival.expectedPlatform()); 0301 setArrivalPlatformLayout(arrival.platformLayout()); 0302 setArrivalVehicleLayout(arrival.vehicleLayout()); 0303 } 0304 0305 int JourneySection::co2Emission() const 0306 { 0307 // TODO handle rental vehicles and ride sharing in here! 0308 if (d->co2Emission >= 0) { 0309 return d->co2Emission; 0310 } 0311 0312 struct { 0313 Line::Mode mode; 0314 int gramPerKm; 0315 } static const emissionForModeMap[] = { 0316 { Line::Air, 285 }, 0317 { Line::Boat, 245 }, 0318 { Line::Bus, 68 }, 0319 { Line::BusRapidTransit, 68 }, 0320 { Line::Coach, 68 }, 0321 { Line::Ferry, 245 }, 0322 { Line::LocalTrain, 14 }, 0323 { Line::LongDistanceTrain, 14 }, 0324 { Line::Metro, 11 }, 0325 { Line::RapidTransit, 11 }, 0326 { Line::Taxi, 158 }, 0327 { Line::Train, 14 }, 0328 { Line::Tramway, 11 }, 0329 }; 0330 0331 const auto mode = route().line().mode(); 0332 for (const auto &map : emissionForModeMap) { 0333 if (map.mode == mode) { 0334 return (map.gramPerKm * distance()) / 1000; 0335 } 0336 } 0337 return -1; 0338 } 0339 0340 void JourneySection::setCo2Emission(int value) 0341 { 0342 d.detach(); 0343 d->co2Emission = value; 0344 } 0345 0346 const std::vector<LoadInfo>& JourneySection::loadInformation() const 0347 { 0348 return d->loadInformation; 0349 } 0350 0351 std::vector<LoadInfo>&& JourneySection::takeLoadInformation() 0352 { 0353 d.detach(); 0354 return std::move(d->loadInformation); 0355 } 0356 0357 void JourneySection::setLoadInformation(std::vector<LoadInfo> &&loadInfo) 0358 { 0359 d.detach(); 0360 d->loadInformation = std::move(loadInfo); 0361 } 0362 0363 QVariantList JourneySection::loadInformationVariant() const 0364 { 0365 QVariantList l; 0366 l.reserve(d->loadInformation.size()); 0367 std::transform(d->loadInformation.begin(), d->loadInformation.end(), std::back_inserter(l), [](const auto &load) { return QVariant::fromValue(load); }); 0368 return l; 0369 } 0370 0371 void JourneySection::applyMetaData(bool download) 0372 { 0373 if (!from().hasCoordinate() || mode() != JourneySection::PublicTransport) { 0374 return; 0375 } 0376 auto line = d->route.line(); 0377 line.applyMetaData(from(), download); 0378 d->route.setLine(line); 0379 0380 // propagate to intermediate stops 0381 for (auto &stop : d->intermediateStops) { 0382 stop.setRoute(d->route); 0383 } 0384 } 0385 0386 bool JourneySection::isSame(const JourneySection &lhs, const JourneySection &rhs) 0387 { 0388 if (lhs.d->mode != rhs.d->mode) { 0389 return false; 0390 } 0391 0392 if (lhs.d->mode == JourneySection::IndividualTransport && lhs.d->individualTransport != rhs.d->individualTransport) { 0393 return false; 0394 } 0395 0396 // we have N criteria to compare here, with 3 possible results: 0397 // - equal 0398 // - similar-ish, unknwon, or at least not conflicting 0399 // - conflicting 0400 // A single conflict results in a negative result, at least N - 1 equal comparisons lead to 0401 // in a positive result. 0402 enum { Equal = 1, Compatible = 0, Conflict = -1000 }; 0403 int result = 0; 0404 0405 const auto depTimeDist = MergeUtil::distance(lhs.d->scheduledDepartureTime, rhs.d->scheduledDepartureTime); 0406 result += depTimeDist < 60 ? Equal : depTimeDist <= 60 ? Compatible : Conflict; 0407 const auto arrTimeDist = MergeUtil::distance(lhs.d->scheduledArrivalTime, rhs.d->scheduledArrivalTime); 0408 result += arrTimeDist < 60 ? Equal : depTimeDist <= 60 ? Compatible : Conflict; 0409 0410 const auto sameFrom = Location::isSame(lhs.d->from, rhs.d->from); 0411 const auto fromDist = Location::distance(lhs.from(), rhs.from()); 0412 result += sameFrom ? Equal : fromDist < 200 ? Compatible : Conflict; 0413 0414 const auto sameTo = Location::isSame(lhs.d->to, rhs.d->to); 0415 const auto toDist = Location::distance(lhs.to(), rhs.to()); 0416 result += sameTo ? Equal : toDist < 200 ? Compatible : Conflict; 0417 0418 const auto sameRoute = Route::isSame(lhs.d->route, rhs.d->route); 0419 const auto sameDir = Location::isSameName(lhs.d->route.direction(), rhs.d->route.direction()); 0420 const auto sameLine = Line::isSame(lhs.d->route.line(), rhs.d->route.line()); 0421 result += sameRoute ? Equal : (sameDir || sameLine) ? Compatible : Conflict; 0422 0423 if (!lhs.scheduledDeparturePlatform().isEmpty() && !rhs.scheduledDeparturePlatform().isEmpty()) { 0424 result += lhs.scheduledDeparturePlatform() == rhs.scheduledDeparturePlatform() ? Equal : Conflict; 0425 } 0426 0427 return result >= 4; 0428 } 0429 0430 JourneySection JourneySection::merge(const JourneySection &lhs, const JourneySection &rhs) 0431 { 0432 using namespace MergeUtil; 0433 auto res = lhs; 0434 res.setScheduledDepartureTime(mergeDateTimeEqual(lhs.scheduledDepartureTime(), rhs.scheduledDepartureTime())); 0435 res.setExpectedDepartureTime(mergeDateTimeMax(lhs.expectedDepartureTime(), rhs.expectedDepartureTime())); 0436 res.setScheduledArrivalTime(mergeDateTimeMax(lhs.scheduledArrivalTime(), rhs.scheduledArrivalTime())); 0437 res.setExpectedArrivalTime(mergeDateTimeMax(lhs.expectedArrivalTime(), rhs.expectedArrivalTime())); 0438 0439 if (res.expectedDeparturePlatform().isEmpty()) { 0440 res.setExpectedDeparturePlatform(rhs.expectedDeparturePlatform()); 0441 } 0442 if (res.expectedArrivalPlatform().isEmpty()) { 0443 res.setExpectedArrivalPlatform(rhs.expectedArrivalPlatform()); 0444 } 0445 res.setFrom(Location::merge(lhs.from(), rhs.from())); 0446 res.setTo(Location::merge(lhs.to(), rhs.to())); 0447 res.setRoute(Route::merge(lhs.route(), rhs.route())); 0448 0449 res.setScheduledDeparturePlatform(mergeString(lhs.scheduledDeparturePlatform(), rhs.scheduledDeparturePlatform())); 0450 res.setScheduledArrivalPlatform(mergeString(lhs.scheduledArrivalPlatform(), rhs.scheduledArrivalPlatform())); 0451 0452 res.setDisruptionEffect(std::max(lhs.disruptionEffect(), rhs.disruptionEffect())); 0453 res.setNotes(NotesUtil::mergeNotes(lhs.notes(), rhs.notes())); 0454 res.setDistance(std::max(lhs.distance(), rhs.distance())); 0455 0456 if (lhs.intermediateStops().size() == rhs.intermediateStops().size()) { 0457 auto stops = res.takeIntermediateStops(); 0458 for (uint i = 0; i < stops.size(); ++i) { 0459 stops[i] = Stopover::merge(stops[i], rhs.intermediateStops()[i]); 0460 stops[i].setRoute(res.route()); 0461 } 0462 res.setIntermediateStops(std::move(stops)); 0463 } 0464 0465 res.d->co2Emission = std::max(lhs.d->co2Emission, rhs.d->co2Emission); 0466 res.d->loadInformation = LoadUtil::merge(lhs.d->loadInformation, rhs.d->loadInformation); 0467 res.d->rentalVehicle = RentalVehicleUtil::merge(lhs.d->rentalVehicle, rhs.d->rentalVehicle); 0468 0469 res.d->path = lhs.d->path.sections().size() < rhs.d->path.sections().size() ? rhs.d->path : lhs.d->path; 0470 0471 res.d->departureVehicleLayout = Vehicle::merge(lhs.d->departureVehicleLayout, rhs.d->departureVehicleLayout); 0472 res.d->departurePlatformLayout = Platform::merge(lhs.d->departurePlatformLayout, rhs.d->departurePlatformLayout); 0473 res.d->arrivalVehicleLayout = Vehicle::merge(lhs.d->arrivalVehicleLayout, rhs.d->arrivalVehicleLayout); 0474 res.d->arrivalPlatformLayout = Platform::merge(lhs.d->arrivalPlatformLayout, rhs.d->arrivalPlatformLayout); 0475 0476 return res; 0477 } 0478 0479 QJsonObject JourneySection::toJson(const JourneySection §ion) 0480 { 0481 auto obj = Json::toJson(section); 0482 if (section.mode() != Waiting) { 0483 const auto fromObj = Location::toJson(section.from()); 0484 if (!fromObj.empty()) { 0485 obj.insert(QLatin1String("from"), fromObj); 0486 } 0487 const auto toObj = Location::toJson(section.to()); 0488 if (!toObj.empty()) { 0489 obj.insert(QLatin1String("to"), toObj); 0490 } 0491 } 0492 if (section.mode() == PublicTransport) { 0493 const auto routeObj = Route::toJson(section.route()); 0494 if (!routeObj.empty()) { 0495 obj.insert(QLatin1String("route"), routeObj); 0496 } 0497 if (!section.intermediateStops().empty()) { 0498 obj.insert(QLatin1String("intermediateStops"), Stopover::toJson(section.intermediateStops())); 0499 } 0500 if (!section.loadInformation().empty()) { 0501 obj.insert(QLatin1String("load"), LoadInfo::toJson(section.loadInformation())); 0502 } 0503 } 0504 if (section.d->co2Emission < 0) { 0505 obj.remove(QLatin1String("co2Emission")); 0506 } 0507 if (section.rentalVehicle().type() != RentalVehicle::Unknown) { 0508 obj.insert(QLatin1String("rentalVehicle"), RentalVehicle::toJson(section.rentalVehicle())); 0509 } 0510 0511 if (!section.path().isEmpty()) { 0512 obj.insert(QLatin1String("path"), Path::toJson(section.path())); 0513 } 0514 0515 if (!section.departureVehicleLayout().isEmpty()) { 0516 obj.insert(QLatin1String("departureVehicleLayout"), Vehicle::toJson(section.departureVehicleLayout())); 0517 } 0518 if (!section.departurePlatformLayout().isEmpty()) { 0519 obj.insert(QLatin1String("departurePlatformLayout"), Platform::toJson(section.departurePlatformLayout())); 0520 } 0521 if (!section.arrivalVehicleLayout().isEmpty()) { 0522 obj.insert(QLatin1String("arrivalVehicleLayout"), Vehicle::toJson(section.arrivalVehicleLayout())); 0523 } 0524 if (!section.arrivalPlatformLayout().isEmpty()) { 0525 obj.insert(QLatin1String("arrivalPlatformLayout"), Platform::toJson(section.arrivalPlatformLayout())); 0526 } 0527 0528 if (section.mode() == JourneySection::IndividualTransport) { 0529 obj.insert(QLatin1String("individualTransport"), IndividualTransport::toJson(section.individualTransport())); 0530 } 0531 0532 if (obj.size() <= 3) { // only the disruption and mode enums and distance, ie. this is an empty object 0533 return {}; 0534 } 0535 return obj; 0536 } 0537 0538 QJsonArray JourneySection::toJson(const std::vector<JourneySection> §ions) 0539 { 0540 return Json::toJson(sections); 0541 } 0542 0543 JourneySection JourneySection::fromJson(const QJsonObject &obj) 0544 { 0545 auto section = Json::fromJson<JourneySection>(obj); 0546 section.setFrom(Location::fromJson(obj.value(QLatin1String("from")).toObject())); 0547 section.setTo(Location::fromJson(obj.value(QLatin1String("to")).toObject())); 0548 section.setRoute(Route::fromJson(obj.value(QLatin1String("route")).toObject())); 0549 section.setIntermediateStops(Stopover::fromJson(obj.value(QLatin1String("intermediateStops")).toArray())); 0550 section.setLoadInformation(LoadInfo::fromJson(obj.value(QLatin1String("load")).toArray())); 0551 section.setRentalVehicle(RentalVehicle::fromJson(obj.value(QLatin1String("rentalVehicle")).toObject())); 0552 section.setPath(Path::fromJson(obj.value(QLatin1String("path")).toObject())); 0553 section.setDepartureVehicleLayout(Vehicle::fromJson(obj.value(QLatin1String("departureVehicleLayout")).toObject())); 0554 section.setDeparturePlatformLayout(Platform::fromJson(obj.value(QLatin1String("departurePlatformLayout")).toObject())); 0555 section.setArrivalVehicleLayout(Vehicle::fromJson(obj.value(QLatin1String("arrivalVehicleLayout")).toObject())); 0556 section.setArrivalPlatformLayout(Platform::fromJson(obj.value(QLatin1String("arrivalPlatformLayout")).toObject())); 0557 section.setIndividualTransport(IndividualTransport::fromJson(obj.value(QLatin1String("individualTransport")).toObject())); 0558 section.applyMetaData(false); 0559 return section; 0560 } 0561 0562 std::vector<JourneySection> JourneySection::fromJson(const QJsonArray &array) 0563 { 0564 return Json::fromJson<JourneySection>(array); 0565 } 0566 0567 0568 KPUBLICTRANSPORT_MAKE_GADGET(Journey) 0569 0570 const std::vector<JourneySection>& Journey::sections() const 0571 { 0572 return d->sections; 0573 } 0574 0575 std::vector<JourneySection>&& Journey::takeSections() 0576 { 0577 d.detach(); 0578 return std::move(d->sections); 0579 } 0580 0581 void Journey::setSections(std::vector<JourneySection> &§ions) 0582 { 0583 d.detach(); 0584 d->sections = std::move(sections); 0585 } 0586 0587 QVariantList Journey::sectionsVariant() const 0588 { 0589 QVariantList l; 0590 l.reserve(d->sections.size()); 0591 std::transform(d->sections.begin(), d->sections.end(), std::back_inserter(l), [](const auto &sec) { return QVariant::fromValue(sec); }); 0592 return l; 0593 } 0594 0595 QDateTime Journey::scheduledDepartureTime() const 0596 { 0597 if (!d->sections.empty()) { 0598 return d->sections.front().scheduledDepartureTime(); 0599 } 0600 return {}; 0601 } 0602 0603 bool Journey::hasExpectedDepartureTime() const 0604 { 0605 return d->sections.empty() ? false : d->sections.front().hasExpectedDepartureTime(); 0606 } 0607 0608 QDateTime Journey::expectedDepartureTime() const 0609 { 0610 return d->sections.empty() ? QDateTime() : d->sections.front().expectedDepartureTime(); 0611 } 0612 0613 int Journey::departureDelay() const 0614 { 0615 return d->sections.empty() ? 0 : d->sections.front().departureDelay(); 0616 } 0617 0618 QDateTime Journey::scheduledArrivalTime() const 0619 { 0620 if (!d->sections.empty()) { 0621 return d->sections.back().scheduledArrivalTime(); 0622 } 0623 return {}; 0624 } 0625 0626 bool Journey::hasExpectedArrivalTime() const 0627 { 0628 return d->sections.empty() ? false : d->sections.back().hasExpectedArrivalTime(); 0629 } 0630 0631 QDateTime Journey::expectedArrivalTime() const 0632 { 0633 return d->sections.empty() ? QDateTime() : d->sections.back().expectedArrivalTime(); 0634 } 0635 0636 int Journey::arrivalDelay() const 0637 { 0638 return d->sections.empty() ? 0 : d->sections.back().arrivalDelay(); 0639 } 0640 0641 int Journey::duration() const 0642 { 0643 return scheduledDepartureTime().secsTo(scheduledArrivalTime()); 0644 } 0645 0646 int Journey::numberOfChanges() const 0647 { 0648 return std::max(0, static_cast<int>(std::count_if(d->sections.begin(), d->sections.end(), [](const auto §ion) { return section.mode() == JourneySection::PublicTransport; }) - 1)); 0649 } 0650 0651 Disruption::Effect Journey::disruptionEffect() const 0652 { 0653 Disruption::Effect effect = Disruption::NormalService; 0654 for (const auto &sec : d->sections) { 0655 effect = std::max(effect, sec.disruptionEffect()); 0656 } 0657 return effect; 0658 } 0659 0660 void Journey::applyMetaData(bool download) 0661 { 0662 for (auto &sec : d->sections) { 0663 sec.applyMetaData(download); 0664 } 0665 } 0666 0667 static bool isTransportSection(JourneySection::Mode mode) 0668 { 0669 return mode == JourneySection::PublicTransport 0670 || mode == JourneySection::RentedVehicle 0671 || mode == JourneySection::IndividualTransport; 0672 } 0673 0674 bool Journey::isSame(const Journey &lhs, const Journey &rhs) 0675 { 0676 auto lIt = lhs.sections().begin(); 0677 auto rIt = rhs.sections().begin(); 0678 0679 while (lIt != lhs.sections().end() || rIt != rhs.sections().end()) { 0680 // ignore non-transport sections 0681 if (lIt != lhs.sections().end() && !isTransportSection((*lIt).mode())) { 0682 ++lIt; 0683 continue; 0684 } 0685 if (rIt != rhs.sections().end() && !isTransportSection((*rIt).mode())) { 0686 ++rIt; 0687 continue; 0688 } 0689 0690 if (lIt == lhs.sections().end() || rIt == rhs.sections().end()) { 0691 return false; 0692 } 0693 0694 if (!JourneySection::isSame(*lIt, *rIt)) { 0695 return false; 0696 } 0697 0698 ++lIt; 0699 ++rIt; 0700 } 0701 0702 Q_ASSERT(lIt == lhs.sections().end() && rIt == rhs.sections().end()); 0703 return true; 0704 } 0705 0706 Journey Journey::merge(const Journey &lhs, const Journey &rhs) 0707 { 0708 std::vector<JourneySection> sections; 0709 sections.reserve(lhs.sections().size() + rhs.sections().size()); 0710 std::copy(lhs.sections().begin(), lhs.sections().end(), std::back_inserter(sections)); 0711 std::copy(rhs.sections().begin(), rhs.sections().end(), std::back_inserter(sections)); 0712 std::sort(sections.begin(), sections.end(), [](const auto &lSec, const auto &rSec) { 0713 if (MergeUtil::distance(lSec.scheduledDepartureTime(), rSec.scheduledDepartureTime()) == 0) { 0714 return lSec.mode() < rSec.mode(); 0715 } 0716 return MergeUtil::isBefore(lSec.scheduledDepartureTime(), rSec.scheduledDepartureTime()); 0717 }); 0718 0719 for (auto it = sections.begin(); it != sections.end(); ++it) { 0720 const auto nextIt = it + 1; 0721 if (nextIt == sections.end()) { 0722 break; 0723 } 0724 0725 if (JourneySection::isSame(*it, *nextIt) || ((*it).mode() == (*nextIt).mode() && (*it).mode() != JourneySection::PublicTransport)) { 0726 *it = JourneySection::merge(*it, *nextIt); 0727 sections.erase(nextIt); 0728 } 0729 } 0730 0731 Journey res; 0732 res.setSections(std::move(sections)); 0733 return res; 0734 } 0735 0736 QJsonObject Journey::toJson(const Journey &journey) 0737 { 0738 QJsonObject obj; 0739 obj.insert(QLatin1String("sections"), JourneySection::toJson(journey.sections())); 0740 return obj; 0741 } 0742 0743 QJsonArray Journey::toJson(const std::vector<Journey> &journeys) 0744 { 0745 return Json::toJson(journeys); 0746 } 0747 0748 Journey Journey::fromJson(const QJsonObject &obj) 0749 { 0750 Journey j; 0751 j.setSections(JourneySection::fromJson(obj.value(QLatin1String("sections")).toArray())); 0752 return j; 0753 } 0754 0755 std::vector<Journey> Journey::fromJson(const QJsonArray &array) 0756 { 0757 return Json::fromJson<Journey>(array); 0758 } 0759 0760 #include "moc_journey.cpp"