File indexing completed on 2024-05-12 04:42:40
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "opentripplannerparser.h" 0008 #include "gtfs/hvt.h" 0009 #include "geo/polylinedecoder_p.h" 0010 #include "ifopt/ifoptutil.h" 0011 0012 #include <KPublicTransport/Journey> 0013 #include <KPublicTransport/Stopover> 0014 0015 #include <QColor> 0016 #include <QDebug> 0017 #include <QJsonArray> 0018 #include <QJsonObject> 0019 #include <QTimeZone> 0020 0021 #include <bitset> 0022 0023 using namespace KPublicTransport; 0024 0025 OpenTripPlannerParser::OpenTripPlannerParser(const QString &identifierType, const QString &ifoptPrefix) 0026 : m_identifierType(identifierType) 0027 , m_ifoptPrefix(ifoptPrefix) 0028 { 0029 } 0030 0031 OpenTripPlannerParser::~OpenTripPlannerParser() = default; 0032 0033 void OpenTripPlannerParser::setKnownRentalVehicleNetworks(const QHash<QString, RentalVehicleNetwork> &networks) 0034 { 0035 m_rentalVehicleNetworks = networks; 0036 } 0037 0038 QVariant OpenTripPlannerParser::parseRentalVehicleData(const QJsonObject &obj) const 0039 { 0040 RentalVehicleNetwork network; 0041 // TODO id 0042 const auto networks = obj.value(QLatin1String("networks")).toArray(); 0043 if (!networks.empty()) { 0044 const auto it = m_rentalVehicleNetworks.find(networks.at(0).toString()); 0045 if (it != m_rentalVehicleNetworks.end()) { 0046 network = it.value(); 0047 } else { 0048 network.setName(networks.at(0).toString()); 0049 } 0050 } 0051 0052 const auto capacity = obj.value(QLatin1String("spacesAvailable")).toInt(-1); 0053 const auto available = obj.value(QLatin1String("bikesAvailable")).toInt(-1); 0054 if (capacity == 0 && available == 1) { // heuristic for distinguishing floating vehicles and docks 0055 RentalVehicle v; 0056 v.setNetwork(network); 0057 v.setType(static_cast<RentalVehicle::VehicleType>(static_cast<int>(network.vehicleTypes()))); 0058 return v; 0059 } 0060 0061 RentalVehicleStation s; 0062 s.setNetwork(network); 0063 s.setCapacity(capacity); 0064 s.setAvailableVehicles(available); 0065 return s; 0066 } 0067 0068 bool OpenTripPlannerParser::parseLocationFragment(const QJsonObject &obj, Location &loc) const 0069 { 0070 const auto parentObj = obj.value(QLatin1String("parentStation")).toObject(); 0071 if (!parentObj.isEmpty()) { 0072 loc.setType(Location::Stop); 0073 return parseLocationFragment(parentObj, loc); 0074 } 0075 0076 if (loc.name().isEmpty()) { 0077 loc.setName(obj.value(QLatin1String("name")).toString()); 0078 } 0079 loc.setLatitude(obj.value(QLatin1String("lat")).toDouble(loc.latitude())); 0080 loc.setLongitude(obj.value(QLatin1String("lon")).toDouble(loc.longitude())); 0081 0082 const auto tzId = obj.value(QLatin1String("timezone")).toString(); 0083 if (!tzId.isEmpty()) { 0084 loc.setTimeZone(QTimeZone(tzId.toUtf8())); 0085 } 0086 0087 const auto id = obj.value(QLatin1String("id")).toString(); 0088 if (!id.isEmpty()) { 0089 loc.setIdentifier(m_identifierType, id); 0090 } 0091 if (!m_ifoptPrefix.isEmpty() && id.size() > m_ifoptPrefix.size() + 1 && id.startsWith(m_ifoptPrefix) && id.at(m_ifoptPrefix.size()) == QLatin1Char(':')) { 0092 const auto ifopt = QStringView(id).mid(m_ifoptPrefix.size() + 1); 0093 if (IfoptUtil::isValid(ifopt)) { 0094 loc.setIdentifier(IfoptUtil::identifierType(), ifopt.toString()); 0095 } 0096 } 0097 0098 const auto bss = obj.value(QLatin1String("bikeRentalStation")).toObject(); 0099 if (!bss.isEmpty()) { 0100 loc.setData(parseRentalVehicleData(bss)); 0101 loc.setType(loc.data().userType() == qMetaTypeId<RentalVehicle>() ? Location::RentedVehicle : Location::RentedVehicleStation); 0102 return loc.rentalVehicleStation().network().isValid() || loc.rentalVehicle().network().isValid(); 0103 } 0104 0105 const auto mode = obj.value(QLatin1String("vehicleMode")).toString(); 0106 if (mode == QLatin1String("CARPOOL")) { 0107 loc.setType(Location::CarpoolPickupDropoff); 0108 } else if (!mode.isEmpty() && loc.type() == Location::Place) { 0109 loc.setType(Location::Stop); 0110 } 0111 0112 return true; 0113 } 0114 0115 Location OpenTripPlannerParser::parseLocation(const QJsonObject &obj) const 0116 { 0117 const auto stop = obj.value(QLatin1String("stop")).toObject(); 0118 const auto bikeRental = obj.value(QLatin1String("bikeRentalStation")).toObject(); 0119 0120 Location loc; 0121 auto valid = parseLocationFragment(bikeRental, loc); 0122 if (!stop.empty()) { 0123 loc.setType(Location::Stop); 0124 valid &= parseLocationFragment(stop, loc); 0125 } 0126 valid &= parseLocationFragment(obj, loc); 0127 return valid ? loc : Location(); 0128 } 0129 0130 std::vector<Location> OpenTripPlannerParser::parseLocationsByCoordinate(const QJsonObject &obj) const 0131 { 0132 std::vector<Location> locs; 0133 const auto stopArray = obj.value(QLatin1String("stopsByRadius")).toObject().value(QLatin1String("edges")).toArray(); 0134 locs.reserve(stopArray.size()); 0135 for (const auto &stop : stopArray) { 0136 auto l = parseLocation(stop.toObject().value(QLatin1String("node")).toObject()); 0137 if (!l.isEmpty()) { 0138 locs.push_back(std::move(l)); 0139 } 0140 } 0141 0142 // deduplicate elements, which we get due to searching for stops rather than stations 0143 std::stable_sort(locs.begin(), locs.end(), [this](const auto &lhs, const auto &rhs) { 0144 return lhs.identifier(m_identifierType) < rhs.identifier(m_identifierType); 0145 }); 0146 locs.erase(std::unique(locs.begin(), locs.end(), [this](const auto &lhs, const auto &rhs) { 0147 return lhs.identifier(m_identifierType) == rhs.identifier(m_identifierType); 0148 }), locs.end()); 0149 0150 return locs; 0151 } 0152 0153 std::vector<Location> OpenTripPlannerParser::parseLocationsByName(const QJsonObject &obj) const 0154 { 0155 std::vector<Location> locs; 0156 const auto stationArray = obj.value(QLatin1String("stations")).toArray(); 0157 locs.reserve(stationArray.size()); 0158 for (const auto &station : stationArray) { 0159 auto l = parseLocation(station.toObject()); 0160 if (!l.isEmpty()) { 0161 locs.push_back(std::move(l)); 0162 } 0163 } 0164 return locs; 0165 } 0166 0167 std::vector<Location> OpenTripPlannerParser::parseLocationsArray(const QJsonArray &array) const 0168 { 0169 std::vector<Location> locs; 0170 locs.reserve(array.size()); 0171 for (const auto &l : array) { 0172 locs.push_back(parseLocation(l.toObject())); 0173 } 0174 return locs; 0175 } 0176 0177 std::vector<Location> OpenTripPlannerParser::parseGeocodeResult(const QJsonArray &array) const 0178 { 0179 std::vector<Location> locs; 0180 locs.reserve(array.size()); 0181 for (const auto &v : array) { 0182 const auto obj = v.toObject(); 0183 Location loc; 0184 loc.setLatitude(obj.value(QLatin1String("lat")).toDouble()); 0185 loc.setLongitude(obj.value(QLatin1String("lng")).toDouble()); // sic! 0186 auto desc = obj.value(QLatin1String("description")).toString(); 0187 if (desc.startsWith(QLatin1String("stop "))) { 0188 desc = desc.mid(5); 0189 } 0190 loc.setName(desc); 0191 loc.setIdentifier(m_identifierType, obj.value(QLatin1String("id")).toString()); 0192 locs.push_back(loc); 0193 } 0194 return locs; 0195 } 0196 0197 void OpenTripPlannerParser::parseAlerts(const QJsonArray& alertsArray) const 0198 { 0199 m_alerts.reserve(alertsArray.size()); 0200 for (const auto &alertValue : alertsArray) { 0201 const auto alertObj = alertValue.toObject(); 0202 const auto descsArray = alertObj.value(QLatin1String("alertDescriptionTextTranslations")).toArray(); 0203 if (descsArray.empty()) { 0204 continue; 0205 } 0206 0207 // find the best language 0208 const auto uiLangs = QLocale().uiLanguages(); 0209 int minIdx = 0, minWeight = std::numeric_limits<int>::max(); 0210 for (int i = 0; i < descsArray.size(); ++i) { 0211 const auto lang = descsArray.at(i).toObject().value(QLatin1String("language")).toString(); 0212 for (int j = 0; j < uiLangs.size() && j < minWeight; ++j) { 0213 if (uiLangs.at(j).startsWith(lang)) { 0214 minIdx = i; 0215 minWeight = j; 0216 break; 0217 } 0218 } 0219 } 0220 0221 m_alerts.push_back(descsArray.at(minIdx).toObject().value(QLatin1String("text")).toString()); 0222 } 0223 } 0224 0225 static QColor parseColor(const QJsonValue &value) 0226 { 0227 if (value.isNull()) { 0228 return {}; 0229 } 0230 return QColor(QLatin1Char('#') + value.toString()); 0231 } 0232 0233 Line OpenTripPlannerParser::parseLine(const QJsonObject &obj) const 0234 { 0235 parseAlerts(obj.value(QLatin1String("alerts")).toArray()); 0236 0237 Line line; 0238 line.setName(obj.value(QLatin1String("shortName")).toString()); 0239 if (line.name().isEmpty()) { 0240 line.setName(obj.value(QLatin1String("longName")).toString()); 0241 } 0242 0243 const auto type = obj.value(QLatin1String("type")); 0244 if (type.isString()) { 0245 line.setMode(Gtfs::Hvt::typeToMode(type.toString())); 0246 } else if (type.isDouble()) { 0247 line.setMode(Gtfs::Hvt::typeToMode(type.toInt(-1))); 0248 } else { 0249 line.setMode(Gtfs::Hvt::typeToMode(obj.value(QLatin1String("transportMode")).toString())); 0250 } 0251 0252 auto presentation = obj.value(QLatin1String("presentation")).toObject(); 0253 if (presentation.isEmpty()) { 0254 presentation = obj; 0255 } 0256 line.setColor(parseColor(presentation.value(QLatin1String("color")))); 0257 line.setTextColor(parseColor(presentation.value(QLatin1String("textColor")))); 0258 return line; 0259 } 0260 0261 Route OpenTripPlannerParser::parseRoute(const QJsonObject &obj) const 0262 { 0263 auto line = parseLine(obj.value(QLatin1String("route")).toObject()); 0264 if (line.name().isEmpty()) { 0265 line.setName(obj.value(QLatin1String("tripShortName")).toString()); 0266 } 0267 0268 Route route; 0269 route.setLine(line); 0270 route.setDirection(obj.value(QLatin1String("tripHeadsign")).toString()); 0271 0272 return route; 0273 } 0274 0275 Route OpenTripPlannerParser::parseInlineRoute(const QJsonObject &obj) const 0276 { 0277 Line line; 0278 line.setMode(Gtfs::Hvt::typeToMode(obj.value(QLatin1String("routeType")).toInt(-1))); 0279 line.setName(obj.value(QLatin1String("tripShortName")).toString()); 0280 line.setColor(parseColor(obj.value(QLatin1String("routeColor")))); 0281 line.setTextColor(parseColor(obj.value(QLatin1String("routeTextColor")))); 0282 0283 Route route; 0284 route.setDirection(obj.value(QLatin1String("headsign")).toString()); 0285 route.setLine(line); 0286 0287 return route; 0288 } 0289 0290 Route OpenTripPlannerParser::detectAndParseRoute(const QJsonObject &obj) const 0291 { 0292 const auto trip = obj.value(QLatin1String("trip")).toObject(); 0293 if (!trip.isEmpty()) { 0294 return parseRoute(trip); 0295 } 0296 0297 const auto line = obj.value(QLatin1String("line")).toObject(); 0298 if (!line.isEmpty()) { 0299 Route route; 0300 route.setLine(parseLine(obj.value(QLatin1String("line")).toObject())); 0301 return route; 0302 } 0303 0304 return parseInlineRoute(obj); 0305 } 0306 0307 static QDateTime parseDepartureDateTime(uint64_t baseTime, const QJsonValue &value) 0308 { 0309 if (value.isDouble()) { // encoded as seconds offset to baseTime 0310 // UNIX timestamp of midnight in local timezone + UNIX timestamp of local time 0311 auto dt = QDateTime::fromSecsSinceEpoch(baseTime + value.toDouble()); 0312 dt = dt.toTimeZone(QTimeZone::UTC); 0313 return dt; 0314 } 0315 return QDateTime::fromString(value.toString(), Qt::ISODate); 0316 } 0317 0318 Stopover OpenTripPlannerParser::parseDeparture(const QJsonObject &obj) const 0319 { 0320 Stopover dep; 0321 const auto baseTime = obj.value(QLatin1String("serviceDay")).toDouble(); // ### 64bit 0322 dep.setScheduledArrivalTime(parseDepartureDateTime(baseTime, obj.value(QLatin1String("scheduledArrival")))); 0323 dep.setScheduledDepartureTime(parseDepartureDateTime(baseTime, obj.value(QLatin1String("scheduledDeparture")))); 0324 if (obj.value(QLatin1String("realtime")).toBool()) { 0325 dep.setExpectedArrivalTime(parseDepartureDateTime(baseTime, obj.value(QLatin1String("realtimeArrival")))); 0326 dep.setExpectedDepartureTime(parseDepartureDateTime(baseTime, obj.value(QLatin1String("realtimeDeparture")))); 0327 } 0328 dep.setScheduledPlatform(obj.value(QLatin1String("stop")).toObject().value(QLatin1String("platformCode")).toString()); 0329 dep.setRoute(detectAndParseRoute(obj)); 0330 dep.addNotes(m_alerts); 0331 m_alerts.clear(); 0332 0333 return dep; 0334 } 0335 0336 void OpenTripPlannerParser::parseDeparturesForStop(const QJsonObject &obj, std::vector<Stopover> &deps) const 0337 { 0338 const auto loc = parseLocation(obj); 0339 const auto stopTimes = obj.value(QLatin1String("stoptimes")).toArray(); 0340 for (const auto &stopTime : stopTimes) { 0341 auto dep = parseDeparture(stopTime.toObject()); 0342 dep.setStopPoint(loc); 0343 deps.push_back(dep); 0344 } 0345 } 0346 0347 std::vector<Stopover> OpenTripPlannerParser::parseDepartures(const QJsonObject &obj) const 0348 { 0349 std::vector<Stopover> deps; 0350 0351 const auto depsArray = obj.value(QLatin1String("nearest")).toObject().value(QLatin1String("edges")).toArray(); 0352 for (const auto &depsV : depsArray) { 0353 parseDeparturesForStop(depsV.toObject().value(QLatin1String("node")).toObject().value(QLatin1String("place")).toObject(), deps); 0354 } 0355 0356 return deps; 0357 } 0358 0359 std::vector<Stopover> OpenTripPlannerParser::parseDeparturesArray(const QJsonArray &array) const 0360 { 0361 std::vector<Stopover> deps; 0362 for (const auto &pattern : array) { 0363 const auto obj = pattern.toObject(); 0364 const auto times = obj.value(QLatin1String("times")).toArray(); 0365 for (const auto &time : times) { 0366 deps.push_back(parseDeparture(time.toObject())); 0367 } 0368 } 0369 return deps; 0370 } 0371 0372 0373 static QDateTime parseJourneyDateTime(const QJsonValue &val) 0374 { 0375 if (val.isDouble()) { 0376 // timestamp, as UTC value 0377 auto dt = QDateTime::fromMSecsSinceEpoch(val.toDouble()); // ### sic! double to get 64 bit precision... 0378 dt = dt.toTimeZone(QTimeZone::UTC); 0379 return dt; 0380 } 0381 if (val.isString()) { 0382 return QDateTime::fromString(val.toString(), Qt::ISODate); 0383 } 0384 return {}; 0385 } 0386 0387 static RentalVehicle::VehicleType vehicleTypeFromTypes(RentalVehicle::VehicleTypes types, RentalVehicle::VehicleType fallback = RentalVehicle::Unknown) 0388 { 0389 if (std::bitset<sizeof(types)>(types).count() == 1) { 0390 return static_cast<RentalVehicle::VehicleType>(static_cast<int>(types)); 0391 } 0392 return fallback; 0393 } 0394 0395 JourneySection OpenTripPlannerParser::parseJourneySection(const QJsonObject &obj) const 0396 { 0397 JourneySection section; 0398 section.setScheduledDepartureTime(parseJourneyDateTime(obj.value(QLatin1String("startTime")))); 0399 section.setScheduledArrivalTime(parseJourneyDateTime(obj.value(QLatin1String("endTime")))); 0400 if (obj.value(QLatin1String("realTime")).toBool()) { 0401 section.setExpectedDepartureTime(parseJourneyDateTime(obj.value(QLatin1String("expectedStartTime")))); 0402 if (!section.expectedDepartureTime().isValid()) { 0403 section.setExpectedDepartureTime(section.scheduledDepartureTime().addSecs(obj.value(QLatin1String("departureDelay")).toInt())); 0404 } 0405 section.setExpectedArrivalTime(parseJourneyDateTime(obj.value(QLatin1String("expectedEndTime")))); 0406 if (!section.expectedArrivalTime().isValid()) { 0407 section.setExpectedArrivalTime(section.scheduledArrivalTime().addSecs(obj.value(QLatin1String("arrivalDelay")).toInt())); 0408 } 0409 } 0410 0411 const auto fromObj = obj.value(QLatin1String("from")).toObject(); 0412 const auto fromStop = fromObj.value(QLatin1String("stop")).toObject(); 0413 const auto from = parseLocation(fromObj); 0414 section.setFrom(from); 0415 section.setScheduledDeparturePlatform(fromStop.value(QLatin1String("platformCode")).toString()); 0416 0417 const auto toObj = obj.value(QLatin1String("to")).toObject(); 0418 const auto toStop = toObj.value(QLatin1String("stop")).toObject(); 0419 const auto to = parseLocation(toObj); 0420 section.setTo(to); 0421 section.setScheduledDeparturePlatform(toStop.value(QLatin1String("platformCode")).toString()); 0422 0423 section.setDistance(obj.value(QLatin1String("distance")).toDouble()); 0424 0425 if (obj.value(QLatin1String("transitLeg")).toBool()) { 0426 section.setMode(JourneySection::PublicTransport); 0427 section.setRoute(detectAndParseRoute(obj)); 0428 } else { 0429 const auto mode = obj.value(QLatin1String("mode")).toString(); 0430 const auto isRented = obj.value(QLatin1String("rentedBike")).toBool(); 0431 0432 if (mode.compare(QLatin1String("WALK"), Qt::CaseInsensitive) == 0 || mode.compare(QLatin1String("FOOT"), Qt::CaseInsensitive) == 0) { 0433 section.setMode(JourneySection::Walking); 0434 } else if (mode.compare(QLatin1String("BICYCLE"), Qt::CaseInsensitive) == 0) { 0435 RentalVehicle v; 0436 if (from.rentalVehicleStation().network().isValid()) { 0437 v.setNetwork(from.rentalVehicleStation().network()); 0438 } else if (from.type() == Location::RentedVehicle) { 0439 v = from.data().value<RentalVehicle>(); 0440 } else if (to.rentalVehicleStation().network().isValid()) { 0441 v.setNetwork(to.rentalVehicleStation().network()); 0442 } 0443 v.setType(vehicleTypeFromTypes(v.network().vehicleTypes(), RentalVehicle::Bicycle)); 0444 if (v.network().isValid() || isRented) { 0445 section.setMode(JourneySection::RentedVehicle); 0446 section.setRentalVehicle(v); 0447 } else { 0448 section.setMode(JourneySection::IndividualTransport); 0449 section.setIndividualTransport({ IndividualTransport::Bike }); 0450 } 0451 } else if (mode.compare(QLatin1String("CAR"), Qt::CaseInsensitive) == 0) { 0452 section.setMode(JourneySection::IndividualTransport); 0453 section.setIndividualTransport({ IndividualTransport::Car }); 0454 } else { 0455 qWarning() << "Unhandled OTP mode:" << mode; 0456 } 0457 } 0458 0459 section.addNotes(m_alerts); 0460 m_alerts.clear(); 0461 0462 const auto stopsA = obj.value(QLatin1String("intermediateStops")).toArray(); 0463 std::vector<Stopover> stops; 0464 stops.reserve(stopsA.size()); 0465 for (const auto &stopV : stopsA) { 0466 const auto stopObj = stopV.toObject(); 0467 const auto loc = parseLocation(stopObj); 0468 0469 Stopover stop; 0470 stop.setStopPoint(loc); 0471 stop.setScheduledPlatform(stopObj.value(QLatin1String("platformCode")).toString()); 0472 stop.setScheduledArrivalTime(parseJourneyDateTime(stopObj.value(QLatin1String("scheduledArrivalTime")))); 0473 stop.setScheduledDepartureTime(parseJourneyDateTime(stopObj.value(QLatin1String("scheduledDepartureTime")))); 0474 stop.setExpectedArrivalTime(parseJourneyDateTime(stopObj.value(QLatin1String("expectedArrivalTime")))); 0475 stop.setExpectedDepartureTime(parseJourneyDateTime(stopObj.value(QLatin1String("expectedDepartureTime")))); 0476 0477 stops.push_back(stop); 0478 } 0479 section.setIntermediateStops(std::move(stops)); 0480 0481 const auto geometryObj = obj.value(QLatin1String("legGeometry")).toObject(); 0482 QPolygonF poly; 0483 if (!geometryObj.empty()) { 0484 poly.reserve(geometryObj.value(QLatin1String("length")).toInt()); 0485 const auto points = geometryObj.value(QLatin1String("points")).toString().toUtf8(); 0486 PolylineDecoder<2> decoder(points.constData()); 0487 decoder.readPolygon(poly); 0488 } 0489 0490 const auto stepsArray = obj.value(QLatin1String("steps")).toArray(); 0491 if (!stepsArray.empty()) { 0492 std::vector<PathSection> pathSections; 0493 pathSections.reserve(stepsArray.size()); 0494 PathSection prevPathSec; 0495 int prevPolyIdx = 0; 0496 bool isFirstSection = true; 0497 for (const auto &stepsV : stepsArray) { 0498 const auto stepsObj = stepsV.toObject(); 0499 PathSection pathSec; 0500 pathSec.setDescription(stepsObj.value(QLatin1String("legStepText")).toString()); 0501 0502 if (!isFirstSection) { 0503 const QPointF coord(stepsObj.value(QLatin1String("lon")).toDouble(), stepsObj.value(QLatin1String("lat")).toDouble()); 0504 const auto it = std::min_element(poly.begin() + prevPolyIdx, poly.end(), [coord](QPointF lhs, QPointF rhs) { 0505 return Location::distance(lhs.y(), lhs.x(), coord.y(), coord.x()) < Location::distance(rhs.y(), rhs.x(), coord.y(), coord.x()); 0506 }); 0507 int polyIdx = std::distance(poly.begin(), it); 0508 0509 QPolygonF subPoly; 0510 subPoly.reserve(polyIdx - prevPolyIdx + 1); 0511 std::copy(poly.begin() + prevPolyIdx, poly.begin() + polyIdx + 1, std::back_inserter(subPoly)); 0512 prevPathSec.setPath(std::move(subPoly)); 0513 prevPolyIdx = polyIdx; 0514 pathSections.push_back(std::move(prevPathSec)); 0515 } else { 0516 isFirstSection = false; 0517 } 0518 prevPathSec = pathSec; 0519 } 0520 0521 QPolygonF subPoly; 0522 subPoly.reserve(prevPolyIdx - poly.size() + 1); 0523 std::copy(poly.begin() + prevPolyIdx, poly.end(), std::back_inserter(subPoly)); 0524 prevPathSec.setPath(std::move(subPoly)); 0525 pathSections.push_back(std::move(prevPathSec)); 0526 0527 Path path; 0528 path.setSections(std::move(pathSections)); 0529 section.setPath(std::move(path)); 0530 } else if (!poly.isEmpty()) { 0531 PathSection pathSec; 0532 pathSec.setPath(std::move(poly)); 0533 Path path; 0534 path.setSections({std::move(pathSec)}); 0535 section.setPath(std::move(path)); 0536 } 0537 0538 return section; 0539 } 0540 0541 Journey OpenTripPlannerParser::parseJourney(const QJsonObject &obj) const 0542 { 0543 std::vector<JourneySection> sections; 0544 const auto sectionsArray = obj.value(QLatin1String("legs")).toArray(); 0545 for (const auto §ionObj : sectionsArray) { 0546 sections.push_back(parseJourneySection(sectionObj.toObject())); 0547 } 0548 0549 Journey journey; 0550 journey.setSections(std::move(sections)); 0551 return journey; 0552 } 0553 0554 std::vector<Journey> OpenTripPlannerParser::parseJourneys(const QJsonObject& obj) 0555 { 0556 std::vector<Journey> journeys; 0557 0558 const auto plan = obj.value(QLatin1String("plan")).toObject(); 0559 const auto journeysArray = plan.value(QLatin1String("itineraries")).toArray(); 0560 journeys.reserve(journeysArray.size()); 0561 for (const auto &journeyObj : journeysArray) { 0562 journeys.push_back(parseJourney(journeyObj.toObject())); 0563 } 0564 0565 m_nextJourneyContext.dateTime = parseJourneyDateTime(plan.value(QLatin1String("nextDateTime"))); 0566 m_prevJourneyContext.dateTime = parseJourneyDateTime(plan.value(QLatin1String("prevDateTime"))); 0567 m_nextJourneyContext.searchWindow = m_prevJourneyContext.searchWindow = plan.value(QLatin1String("searchWindowUsed")).toInt(); 0568 0569 return journeys; 0570 }