From e96b5266be29d8b6e067d6120fd3da1d8ab8d08c Mon Sep 17 00:00:00 2001 From: Volker Krause Date: Thu, 3 Jan 2019 18:15:34 +0100 Subject: [PATCH] Support real-time journey data --- autotests/navitiaparsertest.cpp | 22 ++--- .../backends/hafasmgateparser.cpp | 13 ++- .../backends/navitiaparser.cpp | 5 +- src/publictransport/datatypes/journey.cpp | 82 +++++++++++++++---- src/publictransport/datatypes/journey.h | 62 ++++++++++---- src/publictransport/journeyreply.cpp | 10 +-- tests/journeyquery.cpp | 4 +- tests/journeyquery.qml | 24 ++++-- 8 files changed, 163 insertions(+), 59 deletions(-) diff --git a/autotests/navitiaparsertest.cpp b/autotests/navitiaparsertest.cpp index a186f33..ffc5124 100644 --- a/autotests/navitiaparsertest.cpp +++ b/autotests/navitiaparsertest.cpp @@ -49,12 +49,12 @@ private Q_SLOTS: { const auto journey = res[0]; QCOMPARE(journey.sections().size(), 6); - QCOMPARE(journey.departureTime(), QDateTime({2018, 12, 2}, {22, 4, 51})); + QCOMPARE(journey.scheduledDepartureTime(), QDateTime({2018, 12, 2}, {22, 4, 51})); QEXPECT_FAIL("", "tz propagation not implemented yet", Continue); - QCOMPARE(journey.departureTime().timeZone().id(), "Europe/Paris"); - QCOMPARE(journey.arrivalTime(), QDateTime({2018, 12, 2}, {23, 0, 15})); + QCOMPARE(journey.scheduledDepartureTime().timeZone().id(), "Europe/Paris"); + QCOMPARE(journey.scheduledArrivalTime(), QDateTime({2018, 12, 2}, {23, 0, 15})); QEXPECT_FAIL("", "tz propagation not implemented yet", Continue); - QCOMPARE(journey.arrivalTime().timeZone().id(), "Europe/Paris"); + QCOMPARE(journey.scheduledArrivalTime().timeZone().id(), "Europe/Paris"); QCOMPARE(journey.duration(), 3324); auto sec = journey.sections()[0]; @@ -64,10 +64,12 @@ private Q_SLOTS: sec = journey.sections()[1]; QCOMPARE(sec.mode(), KPublicTransport::JourneySection::PublicTransport); - QCOMPARE(sec.departureTime(), QDateTime({2018, 12, 2}, {22, 6}, QTimeZone("Europe/Paris"))); - QCOMPARE(sec.departureTime().timeZone().id(), "Europe/Paris"); - QCOMPARE(sec.arrivalTime(), QDateTime({2018, 12, 2}, {22, 41}, QTimeZone("Europe/Paris"))); - QCOMPARE(sec.arrivalTime().timeZone().id(), "Europe/Paris"); + QCOMPARE(sec.scheduledDepartureTime(), QDateTime({2018, 12, 2}, {22, 6}, QTimeZone("Europe/Paris"))); + QCOMPARE(sec.scheduledDepartureTime().timeZone().id(), "Europe/Paris"); + QCOMPARE(sec.hasExpectedDepartureTime(), false); + QCOMPARE(sec.scheduledArrivalTime(), QDateTime({2018, 12, 2}, {22, 41}, QTimeZone("Europe/Paris"))); + QCOMPARE(sec.scheduledArrivalTime().timeZone().id(), "Europe/Paris"); + QCOMPARE(sec.hasExpectedArrivalTime(), false); QCOMPARE(sec.duration(), 2100); QCOMPARE(sec.from().name(), QStringLiteral("AĆ©roport CDG 2 TGV (Le Mesnil-Amelot)")); QCOMPARE(sec.from().latitude(), 49.0047f); @@ -88,8 +90,8 @@ private Q_SLOTS: QCOMPARE(sec.mode(), KPublicTransport::JourneySection::Waiting); sec = journey.sections()[4]; - QCOMPARE(sec.departureTime(), QDateTime({2018, 12, 2}, {22, 49}, QTimeZone("Europe/Paris"))); - QCOMPARE(sec.arrivalTime(), QDateTime({2018, 12, 2}, {22, 51}, QTimeZone("Europe/Paris"))); + QCOMPARE(sec.scheduledDepartureTime(), QDateTime({2018, 12, 2}, {22, 49}, QTimeZone("Europe/Paris"))); + QCOMPARE(sec.scheduledArrivalTime(), QDateTime({2018, 12, 2}, {22, 51}, QTimeZone("Europe/Paris"))); QCOMPARE(sec.duration(), 120); QCOMPARE(sec.route().line().name(), QStringLiteral("A")); QCOMPARE(sec.route().line().color(), QColor(QStringLiteral("#D1302F"))); diff --git a/src/publictransport/backends/hafasmgateparser.cpp b/src/publictransport/backends/hafasmgateparser.cpp index 68fb43f..1344da8 100644 --- a/src/publictransport/backends/hafasmgateparser.cpp +++ b/src/publictransport/backends/hafasmgateparser.cpp @@ -270,16 +270,23 @@ std::vector HafasMgateParser::parseTripSearch(const QJsonObject &obj) c const auto secObj = secV.toObject(); JourneySection section; - // TODO add real-time data const auto dep = secObj.value(QLatin1String("dep")).toObject(); - section.setDepartureTime(QDateTime::fromString(dateStr + dep.value(QLatin1String("dTimeS")).toString(), QLatin1String("yyyyMMddhhmmss"))); + section.setScheduledDepartureTime(QDateTime::fromString(dateStr + dep.value(QLatin1String("dTimeS")).toString(), QLatin1String("yyyyMMddhhmmss"))); + const auto dTimeR = dep.value(QLatin1String("dTimeR")).toString(); + if (!dTimeR.isEmpty()) { + section.setExpectedDepartureTime(QDateTime::fromString(dateStr + dTimeR, QLatin1String("yyyyMMddhhmmss"))); + } auto locIdx = dep.value(QLatin1String("locX")).toInt(); if ((unsigned int)locIdx < locs.size()) { section.setFrom(locs[locIdx]); } const auto arr = secObj.value(QLatin1String("arr")).toObject(); - section.setArrivalTime(QDateTime::fromString(dateStr + arr.value(QLatin1String("aTimeS")).toString(), QLatin1String("yyyyMMddhhmmss"))); + section.setScheduledArrivalTime(QDateTime::fromString(dateStr + arr.value(QLatin1String("aTimeS")).toString(), QLatin1String("yyyyMMddhhmmss"))); + const auto aTimeR = dep.value(QLatin1String("aTimeR")).toString(); + if (!aTimeR.isEmpty()) { + section.setExpectedArrivalTime(QDateTime::fromString(dateStr + aTimeR, QLatin1String("yyyyMMddhhmmss"))); + } locIdx = arr.value(QLatin1String("locX")).toInt(); if ((unsigned int)locIdx < locs.size()) { section.setTo(locs[locIdx]); diff --git a/src/publictransport/backends/navitiaparser.cpp b/src/publictransport/backends/navitiaparser.cpp index a154fb5..17486b7 100644 --- a/src/publictransport/backends/navitiaparser.cpp +++ b/src/publictransport/backends/navitiaparser.cpp @@ -130,8 +130,9 @@ static JourneySection parseJourneySection(const QJsonObject &obj) section.setFrom(parseWrappedLocation(obj.value(QLatin1String("from")).toObject())); section.setTo(parseWrappedLocation(obj.value(QLatin1String("to")).toObject())); section.setRoute(route); - section.setDepartureTime(parseDateTime(obj.value(QLatin1String("departure_date_time")), section.from().timeZone())); - section.setArrivalTime(parseDateTime(obj.value(QLatin1String("arrival_date_time")), section.to().timeZone())); + // TODO realtime data + section.setScheduledArrivalTime(parseDateTime(obj.value(QLatin1String("departure_date_time")), section.from().timeZone())); + section.setScheduledDepartureTime(parseDateTime(obj.value(QLatin1String("arrival_date_time")), section.to().timeZone())); const auto typeStr = obj.value(QLatin1String("type")).toString(); if (typeStr == QLatin1String("public_transport")) { diff --git a/src/publictransport/datatypes/journey.cpp b/src/publictransport/datatypes/journey.cpp index cf72b74..2dd39ea 100644 --- a/src/publictransport/datatypes/journey.cpp +++ b/src/publictransport/datatypes/journey.cpp @@ -28,8 +28,10 @@ class JourneySectionPrivate : public QSharedData { public: JourneySection::Mode mode = JourneySection::Invalid; - QDateTime departureTime; - QDateTime arrivalTime; + QDateTime scheduledDepartureTime; + QDateTime expectedDepartureTime; + QDateTime scheduledArrivalTime; + QDateTime expectedArrivalTime; Location from; Location to; Route route; @@ -56,31 +58,79 @@ void JourneySection::setMode(JourneySection::Mode mode) d->mode = mode; } -QDateTime JourneySection::departureTime() const +QDateTime JourneySection::scheduledDepartureTime() const { - return d->departureTime; + return d->scheduledDepartureTime; } -void JourneySection::setDepartureTime(const QDateTime &dt) +void JourneySection::setScheduledDepartureTime(const QDateTime &dt) { d.detach(); - d->departureTime = dt; + d->scheduledDepartureTime = dt; } -QDateTime JourneySection::arrivalTime() const +QDateTime JourneySection::expectedDepartureTime() const { - return d->arrivalTime; + return d->expectedDepartureTime; } -void JourneySection::setArrivalTime(const QDateTime &dt) +void JourneySection::setExpectedDepartureTime(const QDateTime &dt) { d.detach(); - d->arrivalTime = dt; + d->expectedDepartureTime = dt; +} + +bool JourneySection::hasExpectedDepartureTime() const +{ + return d->expectedDepartureTime.isValid(); +} + +int JourneySection::departureDelay() const +{ + if (hasExpectedDepartureTime()) { + return d->scheduledDepartureTime.secsTo(d->expectedDepartureTime) / 60; + } + return 0; +} + +QDateTime JourneySection::scheduledArrivalTime() const +{ + return d->scheduledArrivalTime; +} + +void JourneySection::setScheduledArrivalTime(const QDateTime &dt) +{ + d.detach(); + d->scheduledArrivalTime = dt; +} + +QDateTime JourneySection::expectedArrivalTime() const +{ + return d->expectedArrivalTime; +} + +void JourneySection::setExpectedArrivalTime(const QDateTime &dt) +{ + d.detach(); + d->expectedArrivalTime = dt; +} + +bool JourneySection::hasExpectedArrivalTime() const +{ + return d->expectedArrivalTime.isValid(); +} + +int JourneySection::arrivalDelay() const +{ + if (hasExpectedArrivalTime()) { + return d->scheduledArrivalTime.secsTo(d->expectedArrivalTime) / 60; + } + return 0; } int JourneySection::duration() const { - return d->departureTime.secsTo(d->arrivalTime); + return d->scheduledDepartureTime.secsTo(d->scheduledArrivalTime); } Location JourneySection::from() const @@ -143,25 +193,25 @@ QVariantList Journey::sectionsVariant() const return l; } -QDateTime KPublicTransport::Journey::departureTime() const +QDateTime Journey::scheduledDepartureTime() const { if (!d->sections.empty()) { - return d->sections.front().departureTime(); + return d->sections.front().scheduledDepartureTime(); } return {}; } -QDateTime Journey::arrivalTime() const +QDateTime Journey::scheduledArrivalTime() const { if (!d->sections.empty()) { - return d->sections.back().arrivalTime(); + return d->sections.back().scheduledArrivalTime(); } return {}; } int Journey::duration() const { - return departureTime().secsTo(arrivalTime()); + return scheduledDepartureTime().secsTo(scheduledArrivalTime()); } #include "moc_journey.cpp" diff --git a/src/publictransport/datatypes/journey.h b/src/publictransport/datatypes/journey.h index c9c6f01..e70dcc3 100644 --- a/src/publictransport/datatypes/journey.h +++ b/src/publictransport/datatypes/journey.h @@ -36,12 +36,32 @@ class JourneySection KPUBLICTRANSPORT_GADGET(JourneySection) /** Mode of transport for this section. */ Q_PROPERTY(Mode mode READ mode WRITE setMode) - /** Departue time for this segment. */ - Q_PROPERTY(QDateTime departureTime READ departureTime WRITE setDepartureTime) - /** Arrival time for this segment. */ - Q_PROPERTY(QDateTime arrivalTime READ arrivalTime WRITE setArrivalTime) + + /** Planned departure time. */ + Q_PROPERTY(QDateTime scheduledDepartureTime READ scheduledDepartureTime WRITE setScheduledDepartureTime) + /** Actual departure time, if available. + * Set to invalid to indicate real-time data is not available. + */ + Q_PROPERTY(QDateTime expectedDepartureTime READ expectedDepartureTime WRITE setExpectedDepartureTime) + /** @c true if this has real-time data. */ + Q_PROPERTY(bool hasExpectedDepartureTime READ hasExpectedDepartureTime STORED false) + /** Difference to schedule in minutes. */ + Q_PROPERTY(int departureDelay READ departureDelay STORED false) + + /** Planned arrival time. */ + Q_PROPERTY(QDateTime scheduledArrivalTime READ scheduledArrivalTime WRITE setScheduledArrivalTime) + /** Actual arrival time, if available. + * Set to invalid to indicate real-time data is not available. + */ + Q_PROPERTY(QDateTime expectedArrivalTime READ expectedArrivalTime WRITE setExpectedArrivalTime) + /** @c true if this has real-time data. */ + Q_PROPERTY(bool hasExpectedArrivalTime READ hasExpectedArrivalTime STORED false) + /** Difference to schedule in minutes. */ + Q_PROPERTY(int arrivalDelay READ arrivalDelay STORED false) + /** Duration of the section in seconds. */ Q_PROPERTY(int duration READ duration STORED false) + /** Departure location of this segment. */ Q_PROPERTY(KPublicTransport::Location from READ from WRITE setFrom) /** Arrival location of this segment. */ @@ -49,7 +69,7 @@ class JourneySection /** Route to take on this segment. */ Q_PROPERTY(KPublicTransport::Route route READ route WRITE setRoute) - // TODO: planned vs. expected times? + // TODO: platforms public: /** Mode of transport. */ @@ -63,11 +83,23 @@ public: Q_ENUM(Mode) Mode mode() const; void setMode(Mode mode); - QDateTime departureTime() const; - void setDepartureTime(const QDateTime &dt); - QDateTime arrivalTime() const; - void setArrivalTime(const QDateTime &dt); + + QDateTime scheduledDepartureTime() const; + void setScheduledDepartureTime(const QDateTime &dt); + QDateTime expectedDepartureTime() const; + void setExpectedDepartureTime(const QDateTime &dt); + bool hasExpectedDepartureTime() const; + int departureDelay() const; + + QDateTime scheduledArrivalTime() const; + void setScheduledArrivalTime(const QDateTime &dt); + QDateTime expectedArrivalTime() const; + void setExpectedArrivalTime(const QDateTime &dt); + bool hasExpectedArrivalTime() const; + int arrivalDelay() const; + int duration() const; + Location from() const; void setFrom(const Location &from); Location to() const; @@ -84,10 +116,10 @@ class Journey KPUBLICTRANSPORT_GADGET(Journey) /** Journey sections for consumption by QML. */ Q_PROPERTY(QVariantList sections READ sectionsVariant) - /** Departure time of the journey. */ - Q_PROPERTY(QDateTime departureTime READ departureTime STORED false) - /** Arrival time of the journey. */ - Q_PROPERTY(QDateTime arrivalTime READ arrivalTime STORED false) + /** Departure time of the journey, according to schedule. */ + Q_PROPERTY(QDateTime scheduledDepartureTime READ scheduledDepartureTime STORED false) + /** Arrival time of the journey, according to schedule. */ + Q_PROPERTY(QDateTime scheduledArrivalTime READ scheduledArrivalTime STORED false) /** Duration of the entire journey in seconds. */ Q_PROPERTY(int duration READ duration STORED false) public: @@ -98,8 +130,8 @@ public: /** Sets the journey sections. */ void setSections(std::vector &§ions); - QDateTime departureTime() const; - QDateTime arrivalTime() const; + QDateTime scheduledDepartureTime() const; + QDateTime scheduledArrivalTime() const; int duration() const; private: QVariantList sectionsVariant() const; diff --git a/src/publictransport/journeyreply.cpp b/src/publictransport/journeyreply.cpp index edd99c8..5ceadac 100644 --- a/src/publictransport/journeyreply.cpp +++ b/src/publictransport/journeyreply.cpp @@ -50,7 +50,7 @@ void JourneyReplyPrivate::finalizeResult() postProcessJourneys(); std::sort(journeys.begin(), journeys.end(), [](const auto &lhs, const auto &rhs) { - return lhs.departureTime() < rhs.departureTime(); + return lhs.scheduledDepartureTime() < rhs.scheduledDepartureTime(); }); } @@ -65,17 +65,17 @@ void JourneyReplyPrivate::postProcessJourneys() auto from = section.from(); from.setTimeZone(section.to().timeZone()); section.setFrom(from); - auto dt = section.departureTime(); + auto dt = section.scheduledDepartureTime(); dt.setTimeZone(from.timeZone()); - section.setDepartureTime(dt); + section.setScheduledDepartureTime(dt); } if (section.from().timeZone().isValid() && !section.to().timeZone().isValid()) { auto to = section.to(); to.setTimeZone(section.from().timeZone()); section.setTo(to); - auto dt = section.arrivalTime(); + auto dt = section.scheduledArrivalTime(); dt.setTimeZone(to.timeZone()); - section.setArrivalTime(dt); + section.setScheduledArrivalTime(dt); } } } diff --git a/tests/journeyquery.cpp b/tests/journeyquery.cpp index c620ff4..51d8f93 100644 --- a/tests/journeyquery.cpp +++ b/tests/journeyquery.cpp @@ -73,9 +73,9 @@ public: for (const auto &journey : res) { qDebug() << journey.sections().size(); for (const auto §ion : journey.sections()) { - qDebug() << " From" << section.from().name() << section.departureTime(); + qDebug() << " From" << section.from().name() << section.scheduledDepartureTime(); qDebug() << " Mode" << section.mode() << section.route().line().name() << section.route().direction() << section.route().line().modeString(); - qDebug() << " To" << section.to().name() << section.arrivalTime(); + qDebug() << " To" << section.to().name() << section.scheduledArrivalTime(); } } } else { diff --git a/tests/journeyquery.qml b/tests/journeyquery.qml index d4d3752..7ccae6d 100644 --- a/tests/journeyquery.qml +++ b/tests/journeyquery.qml @@ -170,10 +170,16 @@ Kirigami.ApplicationWindow { text: "From: " + modelData.from.name } // TODO platform - QQC2.Label { - text: "Departure: " + modelData.departureTime.toTimeString() + RowLayout { + QQC2.Label { + text: "Departure: " + modelData.scheduledDepartureTime.toTimeString() + } + QQC2.Label { + text: (modelData.departureDelay >= 0 ? "+" : "") + modelData.departureDelay + color: modelData.departureDelay > 1 ? Kirigami.Theme.negativeTextColor : Kirigami.Theme.positiveTextColor + visible: modelData.hasExpectedDepartureTime + } } - // TODO departure delay QQC2.Label { text: { switch (modelData.mode) { @@ -193,10 +199,16 @@ Kirigami.ApplicationWindow { text: "To: " + modelData.to.name } // TODO platform - QQC2.Label { - text: "Arrival: " + modelData.arrivalTime.toTimeString() + RowLayout { + QQC2.Label { + text: "Arrival: " + modelData.scheduledArrivalTime.toTimeString() + } + QQC2.Label { + text: (modelData.arrivalDelay >= 0 ? "+" : "") + modelData.arrivalDelay + color: modelData.arrivalDelay > 1 ? Kirigami.Theme.negativeTextColor : Kirigami.Theme.positiveTextColor + visible: modelData.hasExpectedArrivalTime + } } - // TODO arrival delay } } } -- GitLab