diff --git a/autotests/navitiaparsertest.cpp b/autotests/navitiaparsertest.cpp index 00ecfbe..6d4b17a 100644 --- a/autotests/navitiaparsertest.cpp +++ b/autotests/navitiaparsertest.cpp @@ -1,121 +1,126 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include class NavitiaParserTest : public QObject { Q_OBJECT private: QByteArray readFile(const char *fn) { QFile f(QString::fromUtf8(fn)); f.open(QFile::ReadOnly); return f.readAll(); } private slots: void initTestCase() { qputenv("TZ", "UTC"); } void testParseJourneys() { const auto res = KPublicTransport::NavitiaParser::parseJourneys(readFile(SOURCE_DIR "/data/navitia/journey-response.json")); QCOMPARE(res.size(), 4); { const auto journey = res[0]; QCOMPARE(journey.sections().size(), 6); + QCOMPARE(journey.departureTime(), QDateTime({2018, 12, 2}, {22, 4, 51})); + QCOMPARE(journey.arrivalTime(), QDateTime({2018, 12, 2}, {23, 0, 15})); + QCOMPARE(journey.duration(), 3324); auto sec = journey.sections()[0]; QCOMPARE(sec.mode(), KPublicTransport::JourneySection::Walking); sec = journey.sections()[1]; QCOMPARE(sec.mode(), KPublicTransport::JourneySection::PublicTransport); - QCOMPARE(sec.departureTime(), QDateTime({2018, 12, 2}, {22, 06})); + QCOMPARE(sec.departureTime(), QDateTime({2018, 12, 2}, {22, 6})); QCOMPARE(sec.arrivalTime(), QDateTime({2018, 12, 2}, {22, 41})); + QCOMPARE(sec.duration(), 2100); QCOMPARE(sec.from().name(), QStringLiteral("Aéroport CDG 2 TGV (Le Mesnil-Amelot)")); QCOMPARE(sec.from().latitude(), 49.0047f); QCOMPARE(sec.to().name(), QStringLiteral("Châtelet les Halles (Paris)")); QCOMPARE(sec.to().longitude(), 2.34701f); QCOMPARE(sec.route().line().name(), QStringLiteral("B")); QCOMPARE(sec.route().line().mode(), KPublicTransport::Line::RapidTransit); QCOMPARE(sec.route().line().modeString(), QStringLiteral("RER")); QCOMPARE(sec.route().line().color(), QColor(123, 163, 220)); QCOMPARE(sec.route().line().textColor(), QColor(255, 255, 255)); sec = journey.sections()[2]; QCOMPARE(sec.mode(), KPublicTransport::JourneySection::Transfer); sec = journey.sections()[3]; QCOMPARE(sec.mode(), KPublicTransport::JourneySection::Waiting); sec = journey.sections()[4]; QCOMPARE(sec.departureTime(), QDateTime({2018, 12, 2}, {22, 49})); QCOMPARE(sec.arrivalTime(), QDateTime({2018, 12, 2}, {22, 51})); + QCOMPARE(sec.duration(), 120); QCOMPARE(sec.route().line().name(), QStringLiteral("A")); QCOMPARE(sec.route().line().color(), QColor(QStringLiteral("#D1302F"))); QCOMPARE(sec.route().line().textColor(), QColor(255, 255, 255)); QCOMPARE(sec.from().name(), QStringLiteral("Châtelet les Halles (Paris)")); QCOMPARE(sec.to().name(), QStringLiteral("Gare de Lyon RER A (Paris)")); } { const auto journey = res[1]; QCOMPARE(journey.sections().size(), 6); auto sec = journey.sections()[1]; QCOMPARE(sec.route().line().name(), QStringLiteral("B")); QCOMPARE(sec.route().line().mode(), KPublicTransport::Line::RapidTransit); sec = journey.sections()[4]; QCOMPARE(sec.route().line().name(), QStringLiteral("65")); } { const auto journey = res[2]; QCOMPARE(journey.sections().size(), 6); auto sec = journey.sections()[1]; QCOMPARE(sec.route().line().name(), QStringLiteral("B")); sec = journey.sections()[4]; QCOMPARE(sec.route().line().name(), QStringLiteral("91")); QCOMPARE(sec.route().line().modeString(), QStringLiteral("Bus")); QCOMPARE(sec.route().line().mode(), KPublicTransport::Line::Bus); } { const auto journey = res[3]; QCOMPARE(journey.sections().size(), 3); auto sec = journey.sections()[1]; QCOMPARE(sec.route().line().name(), QStringLiteral("DIRECT 4")); QCOMPARE(sec.route().line().mode(), KPublicTransport::Line::Bus); QCOMPARE(sec.route().line().modeString(), QStringLiteral("Bus")); } } }; QTEST_GUILESS_MAIN(NavitiaParserTest) #include "navitiaparsertest.moc" diff --git a/src/publictransport/datatypes/journey.cpp b/src/publictransport/datatypes/journey.cpp index 1b7489e..9950748 100644 --- a/src/publictransport/datatypes/journey.cpp +++ b/src/publictransport/datatypes/journey.cpp @@ -1,136 +1,162 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "journey.h" #include "datatypes_p.h" #include using namespace KPublicTransport; namespace KPublicTransport { class JourneySectionPrivate : public QSharedData { public: JourneySection::Mode mode = JourneySection::Invalid; QDateTime departureTime; QDateTime arrivalTime; Location from; Location to; Route route; }; class JourneyPrivate : public QSharedData { public: std::vector sections; }; } KPUBLICTRANSPORT_MAKE_GADGET(JourneySection) JourneySection::Mode JourneySection::mode() const { return d->mode; } void JourneySection::setMode(JourneySection::Mode mode) { d.detach(); d->mode = mode; } QDateTime JourneySection::departureTime() const { return d->departureTime; } void JourneySection::setDepartureTime(const QDateTime &dt) { d.detach(); d->departureTime = dt; } QDateTime JourneySection::arrivalTime() const { return d->arrivalTime; } void JourneySection::setArrivalTime(const QDateTime &dt) { d.detach(); d->arrivalTime = dt; } +int JourneySection::duration() const +{ + return d->departureTime.secsTo(d->arrivalTime); +} + Location JourneySection::from() const { return d->from; } void JourneySection::setFrom(const Location &from) { d.detach(); d->from = from; } Location JourneySection::to() const { return d->to; } void JourneySection::setTo(const Location &to) { d.detach(); d->to = to; } Route JourneySection::route() const { return d->route; } void JourneySection::setRoute(const Route &route) { d.detach(); d->route = route; } KPUBLICTRANSPORT_MAKE_GADGET(Journey) const std::vector& Journey::sections() const { return d->sections; } void Journey::setSections(std::vector &§ions) { d.detach(); d->sections = std::move(sections); } QVariantList Journey::sectionsVariant() const { QVariantList l; l.reserve(d->sections.size()); std::transform(d->sections.begin(), d->sections.end(), std::back_inserter(l), [](const auto &sec) { return QVariant::fromValue(sec); }); return l; } +QDateTime KPublicTransport::Journey::departureTime() const +{ + if (!d->sections.empty()) { + return d->sections.front().departureTime(); + } + return {}; +} + +QDateTime Journey::arrivalTime() const +{ + if (!d->sections.empty()) { + return d->sections.back().arrivalTime(); + } + return {}; +} + +int Journey::duration() const +{ + return departureTime().secsTo(arrivalTime()); +} + #include "moc_journey.cpp" diff --git a/src/publictransport/datatypes/journey.h b/src/publictransport/datatypes/journey.h index 546f594..8e6c491 100644 --- a/src/publictransport/datatypes/journey.h +++ b/src/publictransport/datatypes/journey.h @@ -1,98 +1,111 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KPUBLICTRANSPORT_JOURNEY_H #define KPUBLICTRANSPORT_JOURNEY_H #include "datatypes.h" #include "line.h" #include "location.h" #include #include namespace KPublicTransport { class JourneySectionPrivate; /** A segment of a journey plan. */ 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) + /** 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. */ Q_PROPERTY(KPublicTransport::Location to READ to WRITE setTo) /** Route to take on this segment. */ Q_PROPERTY(KPublicTransport::Route route READ route WRITE setRoute) // TODO: planned vs. expected times? public: /** Mode of transport. */ enum Mode { Invalid, PublicTransport, Transfer, Walking, Waiting }; 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); + int duration() const; Location from() const; void setFrom(const Location &from); Location to() const; void setTo(const Location &to); Route route() const; void setRoute(const Route &route); }; class JourneyPrivate; /** A journey plan. */ 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) + /** Duration of the entire journey in seconds. */ + Q_PROPERTY(int duration READ duration STORED false) public: /** The journey sections. */ const std::vector& sections() const; /** Sets the journey sections. */ void setSections(std::vector &§ions); + QDateTime departureTime() const; + QDateTime arrivalTime() const; + int duration() const; private: QVariantList sectionsVariant() const; }; } Q_DECLARE_METATYPE(KPublicTransport::JourneySection) Q_DECLARE_METATYPE(KPublicTransport::Journey) #endif // KPUBLICTRANSPORT_JOURNEY_H diff --git a/tests/journeyquery.qml b/tests/journeyquery.qml index 437d8ff..89f3fa1 100644 --- a/tests/journeyquery.qml +++ b/tests/journeyquery.qml @@ -1,86 +1,95 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ import QtQuick 2.5 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.1 as QQC2 import org.kde.kirigami 2.0 as Kirigami import org.kde.kpublictransport 1.0 Kirigami.ApplicationWindow { title: "Journey Query" reachableModeEnabled: false width: 480 height: 720 pageStack.initialPage: journyQueryPage + function displayDuration(dur) + { + if (dur < 60) + return "<1min"; + if (dur < 3600) + return Math.floor(dur/60) + "min"; + return Math.floor(dur/3600) + ":" + Math.floor((dur % 3600)/60) + } + Component { id: journyQueryPage Kirigami.Page { ColumnLayout { anchors.fill: parent QQC2.ComboBox { id: journeySelector model: _journeys } ListView { Layout.fillHeight: true model: _journeys[journeySelector.currentIndex].sections delegate: Item { implicitHeight: delegateLayout.implicitHeight implicitWidth: delegateLayout.implicitWidth ColumnLayout { id: delegateLayout Text { text: "From: " + modelData.from.name visible: index == 0 } Text { text: { switch (modelData.mode) { case JourneySection.PublicTransport: - return modelData.route.line.modeString + " " + modelData.route.line.name; + return modelData.route.line.modeString + " " + modelData.route.line.name + " " + displayDuration(modelData.duration); case JourneySection.Walking: - return "Walk"; + return "Walk " + displayDuration(modelData.duration) case JourneySection.Transfer: - return "Transfer"; + return "Transfer " + displayDuration(modelData.duration) case JourneySection.Waiting: - return "Wait"; + return "Wait " + displayDuration(modelData.duration) return "???"; }} } Text { text: "To: " + modelData.to.name } } Rectangle { anchors.left: parent.left anchors.leftMargin: -8 height: parent.height width: 4 color: modelData.route.line.color } } } } } } }