File indexing completed on 2024-05-12 04:42:45
0001 /* 0002 SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "stopover.h" 0008 #include "datatypes_p.h" 0009 #include "json_p.h" 0010 #include "loadutil_p.h" 0011 #include "mergeutil_p.h" 0012 #include "notesutil_p.h" 0013 #include "platformutils_p.h" 0014 #include "stopoverutil_p.h" 0015 0016 #include <QDateTime> 0017 #include <QDebug> 0018 0019 using namespace KPublicTransport; 0020 0021 namespace KPublicTransport { 0022 class StopoverPrivate : public QSharedData { 0023 public: 0024 Disruption::Effect disruptionEffect = Disruption::NormalService; 0025 QDateTime scheduledArrivalTime; 0026 QDateTime expectedArrivalTime; 0027 QDateTime scheduledDepartureTime; 0028 QDateTime expectedDepartureTime; 0029 QString scheduledPlatform; 0030 QString expectedPlatform; 0031 Route route; 0032 Location stopPoint; 0033 QStringList notes; 0034 std::vector<LoadInfo> loadInformation; 0035 Vehicle vehicleLayout; 0036 Platform platformLayout; 0037 }; 0038 } 0039 0040 KPUBLICTRANSPORT_MAKE_GADGET(Stopover) 0041 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, QDateTime, scheduledArrivalTime, setScheduledArrivalTime) 0042 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, QDateTime, expectedArrivalTime, setExpectedArrivalTime) 0043 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, QDateTime, scheduledDepartureTime, setScheduledDepartureTime) 0044 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, QDateTime, expectedDepartureTime, setExpectedDepartureTime) 0045 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, Route, route, setRoute) 0046 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, Location, stopPoint, setStopPoint) 0047 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, Disruption::Effect, disruptionEffect, setDisruptionEffect) 0048 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, QStringList, notes, setNotes) 0049 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, Vehicle, vehicleLayout, setVehicleLayout) 0050 KPUBLICTRANSPORT_MAKE_PROPERTY(Stopover, Platform, platformLayout, setPlatformLayout) 0051 0052 bool Stopover::hasExpectedArrivalTime() const 0053 { 0054 return d->expectedArrivalTime.isValid(); 0055 } 0056 0057 int Stopover::arrivalDelay() const 0058 { 0059 if (hasExpectedArrivalTime()) { 0060 return d->scheduledArrivalTime.secsTo(d->expectedArrivalTime) / 60; 0061 } 0062 return 0; 0063 } 0064 0065 bool Stopover::hasExpectedDepartureTime() const 0066 { 0067 return d->expectedDepartureTime.isValid(); 0068 } 0069 0070 int Stopover::departureDelay() const 0071 { 0072 if (hasExpectedDepartureTime()) { 0073 return d->scheduledDepartureTime.secsTo(d->expectedDepartureTime) / 60; 0074 } 0075 return 0; 0076 } 0077 0078 QString Stopover::scheduledPlatform() const 0079 { 0080 return d->scheduledPlatform; 0081 } 0082 0083 void Stopover::setScheduledPlatform(const QString &platform) 0084 { 0085 d.detach(); 0086 d->scheduledPlatform = PlatformUtils::normalizePlatform(platform); 0087 } 0088 0089 QString Stopover::expectedPlatform() const 0090 { 0091 return d->expectedPlatform; 0092 } 0093 0094 void Stopover::setExpectedPlatform(const QString &platform) 0095 { 0096 d.detach(); 0097 d->expectedPlatform = PlatformUtils::normalizePlatform(platform); 0098 } 0099 0100 bool Stopover::hasExpectedPlatform() const 0101 { 0102 return !d->expectedPlatform.isEmpty(); 0103 } 0104 0105 bool Stopover::platformChanged() const 0106 { 0107 return PlatformUtils::platformChanged(d->scheduledPlatform, d->expectedPlatform); 0108 } 0109 0110 void Stopover::addNote(const QString ¬e) 0111 { 0112 const auto n = NotesUtil::normalizeNote(note); 0113 const auto idx = NotesUtil::needsAdding(d->notes, n); 0114 if (idx >= 0) { 0115 d.detach(); 0116 NotesUtil::performAdd(d->notes, n, idx); 0117 } 0118 } 0119 0120 void Stopover::addNotes(const QStringList ¬es) 0121 { 0122 for (const auto &n : notes) { 0123 addNote(n); 0124 } 0125 } 0126 0127 const std::vector<LoadInfo>& Stopover::loadInformation() const 0128 { 0129 return d->loadInformation; 0130 } 0131 0132 std::vector<LoadInfo>&& Stopover::takeLoadInformation() 0133 { 0134 d.detach(); 0135 return std::move(d->loadInformation); 0136 } 0137 0138 void Stopover::setLoadInformation(std::vector<LoadInfo> &&loadInfo) 0139 { 0140 d.detach(); 0141 d->loadInformation = std::move(loadInfo); 0142 } 0143 0144 QVariantList Stopover::loadInformationVariant() const 0145 { 0146 QVariantList l; 0147 l.reserve(d->loadInformation.size()); 0148 std::transform(d->loadInformation.begin(), d->loadInformation.end(), std::back_inserter(l), [](const auto &load) { return QVariant::fromValue(load); }); 0149 return l; 0150 } 0151 0152 void Stopover::applyMetaData(bool download) 0153 { 0154 auto line = d->route.line(); 0155 line.applyMetaData(stopPoint(), download); 0156 d->route.setLine(line); 0157 } 0158 0159 bool Stopover::isSame(const Stopover &lhs, const Stopover &rhs) 0160 { 0161 // same time is mandatory 0162 const auto departureTimeMatch = lhs.scheduledDepartureTime().isValid() 0163 && rhs.scheduledDepartureTime().isValid() 0164 && MergeUtil::distance(lhs.scheduledDepartureTime(), rhs.scheduledDepartureTime()) < 60; 0165 const auto arrivalTimeMatch = lhs.scheduledArrivalTime().isValid() 0166 && rhs.scheduledArrivalTime().isValid() 0167 && MergeUtil::distance(lhs.scheduledArrivalTime(), rhs.scheduledArrivalTime()) < 60; 0168 if (!departureTimeMatch && !arrivalTimeMatch) { 0169 return false; 0170 } 0171 0172 // same route would be sufficient, if that's not the case, look for other hints 0173 // this might be the same below 0174 if (Route::isSame(lhs.route(), rhs.route())) { 0175 return true; 0176 } 0177 0178 // different platform can't be the same train 0179 if (!lhs.scheduledPlatform().isEmpty() && !rhs.scheduledPlatform().isEmpty() && lhs.scheduledPlatform() != rhs.scheduledPlatform()) { 0180 return false; 0181 } 0182 0183 // same destination and departure time is likely the same route after all 0184 // TODO we should check for conflicting line names or train types here maybe? 0185 return (!lhs.route().destination().isEmpty() && !rhs.route().destination().isEmpty() && Location::isSame(lhs.route().destination(), rhs.route().destination())) 0186 || Location::isSameName(lhs.route().direction(), rhs.route().direction()); 0187 } 0188 0189 Stopover Stopover::merge(const Stopover &lhs, const Stopover &rhs) 0190 { 0191 auto stopover = lhs; 0192 0193 using namespace MergeUtil; 0194 stopover.setScheduledDepartureTime(mergeDateTimeEqual(lhs.scheduledDepartureTime(), rhs.scheduledDepartureTime())); 0195 stopover.setExpectedDepartureTime(mergeDateTimeMax(lhs.expectedDepartureTime(), rhs.expectedDepartureTime())); 0196 stopover.setScheduledArrivalTime(mergeDateTimeEqual(lhs.scheduledArrivalTime(), rhs.scheduledArrivalTime())); 0197 stopover.setExpectedArrivalTime(mergeDateTimeMax(lhs.expectedArrivalTime(), rhs.expectedArrivalTime())); 0198 0199 if (stopover.scheduledPlatform().isEmpty() && !rhs.scheduledPlatform().isEmpty()) { 0200 stopover.setScheduledPlatform(rhs.scheduledPlatform()); 0201 } 0202 if (!stopover.hasExpectedPlatform() && rhs.hasExpectedPlatform()) { 0203 stopover.setExpectedPlatform(rhs.expectedPlatform()); 0204 } 0205 0206 stopover.setRoute(Route::merge(lhs.route(), rhs.route())); 0207 stopover.setStopPoint(Location::merge(lhs.stopPoint(), rhs.stopPoint())); 0208 stopover.setDisruptionEffect(std::max(lhs.disruptionEffect(), rhs.disruptionEffect())); 0209 stopover.setNotes(NotesUtil::mergeNotes(lhs.notes(), rhs.notes())); 0210 stopover.d->loadInformation = LoadUtil::merge(lhs.d->loadInformation, rhs.d->loadInformation); 0211 stopover.d->vehicleLayout = Vehicle::merge(lhs.d->vehicleLayout, rhs.d->vehicleLayout); 0212 stopover.d->platformLayout = Platform::merge(lhs.d->platformLayout, rhs.d->platformLayout); 0213 return stopover; 0214 } 0215 0216 QJsonObject Stopover::toJson(const Stopover &stopover) 0217 { 0218 auto obj = Json::toJson(stopover); 0219 const auto routeObj = Route::toJson(stopover.route()); 0220 if (!routeObj.empty()) { 0221 obj.insert(QLatin1String("route"), routeObj); 0222 } 0223 const auto locObj = Location::toJson(stopover.stopPoint()); 0224 if (!locObj.empty()) { 0225 obj.insert(QLatin1String("stopPoint"), locObj); 0226 } 0227 if (!stopover.loadInformation().empty()) { 0228 obj.insert(QLatin1String("load"), LoadInfo::toJson(stopover.loadInformation())); 0229 } 0230 if (!stopover.vehicleLayout().isEmpty()) { 0231 obj.insert(QLatin1String("vehicleLayout"), Vehicle::toJson(stopover.vehicleLayout())); 0232 } 0233 if (!stopover.platformLayout().isEmpty()) { 0234 obj.insert(QLatin1String("platformLayout"), Platform::toJson(stopover.platformLayout())); 0235 } 0236 0237 if (obj.size() == 1) { // only the disruption enum, ie. this is an empty object 0238 return {}; 0239 } 0240 return obj; 0241 } 0242 0243 QJsonArray Stopover::toJson(const std::vector<Stopover> &deps) 0244 { 0245 return Json::toJson(deps); 0246 } 0247 0248 Stopover Stopover::fromJson(const QJsonObject &obj) 0249 { 0250 auto stopover = Json::fromJson<Stopover>(obj); 0251 stopover.setRoute(Route::fromJson(obj.value(QLatin1String("route")).toObject())); 0252 stopover.setStopPoint(Location::fromJson(obj.value(QLatin1String("stopPoint")).toObject())); 0253 stopover.setLoadInformation(LoadInfo::fromJson(obj.value(QLatin1String("load")).toArray())); 0254 stopover.setVehicleLayout(Vehicle::fromJson(obj.value(QLatin1String("vehicleLayout")).toObject())); 0255 stopover.setPlatformLayout(Platform::fromJson(obj.value(QLatin1String("platformLayout")).toObject())); 0256 stopover.applyMetaData(false); 0257 return stopover; 0258 } 0259 0260 std::vector<Stopover> Stopover::fromJson(const QJsonArray &array) 0261 { 0262 return Json::fromJson<Stopover>(array); 0263 } 0264 0265 #include "moc_stopover.cpp"