diff --git a/.gitignore b/.gitignore index faca3dbb..cfada9b9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .directory +/.qmake.* /.clickable/ /.make.cache /Makefile @@ -12,3 +13,4 @@ /run_on_ubuntu_touch.sh /tmp/ /translations/*.qm +/rpm/harbour-fahrplan2.spec diff --git a/clickable.json b/clickable.json index 04b7ead4..92cc1388 100644 --- a/clickable.json +++ b/clickable.json @@ -8,7 +8,8 @@ "clean_tmp": "rm -rf ./tmp/*" }, "template": "custom", - "build": "bash -c 'cd ..; ./clickableCustomBuild.sh click_build'", + "build": "bash -c 'cd ..; ./clickableCustomBuild.sh click_build/tmp'", "dependencies": [ "libcurl4-gnutls-dev" ], - "default" : "kill clean clean_tmp build click-build install launch" + "default" : "kill clean clean_tmp build click-build install launch", + "ssh_" : "bq.fritz.box" } diff --git a/clickableCustomBuild.sh b/clickableCustomBuild.sh index f86faea2..201bd70c 100755 --- a/clickableCustomBuild.sh +++ b/clickableCustomBuild.sh @@ -13,15 +13,8 @@ if [[ -z "$clickBuildFolder" ]] exit 1 fi -# make sure we are in an armhf environment -if [[ ! -d "/usr/lib/arm-linux-gnueabihf" ]] - then - echo "this script has to be run in an armhf container (e.g. docker, chroot or libertine)" - exit 1 -fi - # determine qmake (e.g. cross compiler or direct) -if [[ -n $(which qt5-qmake-arm-linux-gnueabihf) ]] +if [[ -n $(which qt5-qmake-arm-linux-gnueabihf) && -d "/usr/lib/arm-linux-gnueabihf" ]] then QMAKE=qt5-qmake-arm-linux-gnueabihf else diff --git a/fahrplan2.pro b/fahrplan2.pro index 1bf931a2..adc07eed 100644 --- a/fahrplan2.pro +++ b/fahrplan2.pro @@ -2,7 +2,7 @@ APP_NAME = Fahrplan # Define Version -VERSION = 2.0.32-2 +VERSION = 2.0.33-2 # Switch for jolla to separate harbour and openrepo version openrepos { @@ -129,6 +129,8 @@ HEADERS += \ src/parser/parser_salzburg_efa.h \ src/parser/parser_resrobot.h \ src/parser/parser_finland_matka.h \ + src/parser/parser_london_tfl.h \ + src/parser/parser_xmlrmvde.h \ src/models/backends.h SOURCES += src/main.cpp \ @@ -163,6 +165,8 @@ SOURCES += src/main.cpp \ src/parser/parser_salzburg_efa.cpp \ src/parser/parser_resrobot.cpp \ src/parser/parser_finland_matka.cpp \ + src/parser/parser_london_tfl.cpp \ + src/parser/parser_xmlrmvde.cpp \ src/models/backends.cpp LIBS += $$PWD/3rdparty/gauss-kruger-cpp/gausskruger.cpp diff --git a/src/fahrplan.cpp b/src/fahrplan.cpp index d8d854e8..8cb974ec 100644 --- a/src/fahrplan.cpp +++ b/src/fahrplan.cpp @@ -45,12 +45,12 @@ Fahrplan::Fahrplan(QObject *parent) , m_mode(DepartureMode) , m_dateTime(QDateTime::currentDateTime()) { - settings = new QSettings(FAHRPLAN_SETTINGS_NAMESPACE, "fahrplan2"); + settings = new QSettings(FAHRPLAN_SETTINGS_NAMESPACE, "fahrplan2", this); setMode(static_cast(settings->value("mode", DepartureMode).toInt())); if (!m_parser_manager) { int currentBackend = settings->value("currentBackend", 0).toInt(); - m_parser_manager = new FahrplanBackendManager(currentBackend); + m_parser_manager = new FahrplanBackendManager(currentBackend, this); } connect(m_parser_manager, SIGNAL(parserChanged(const QString &, int)), this, SLOT(onParserChanged(const QString &, int))); @@ -80,6 +80,41 @@ Fahrplan::Fahrplan(QObject *parent) } } +Fahrplan::~Fahrplan() +{ + qDebug() << this; + + if (m_trainrestrictions) { + delete m_trainrestrictions; + } + + if (m_timetable) { + delete m_timetable; + } + + if (m_stationSearchResults) { + disconnect(m_stationSearchResults, SIGNAL(stationSelected(Fahrplan::StationType,Station)), + this, SLOT(setStation(Fahrplan::StationType,Station))); + delete m_stationSearchResults; + } + + if (m_favorites) { + disconnect(m_favorites, SIGNAL(stationSelected(Fahrplan::StationType,Station)), + this, SLOT(setStation(Fahrplan::StationType,Station))); + delete m_favorites; + } + + if (m_parser_manager) { + unbindParserSignals(); + disconnect(m_parser_manager, SIGNAL(parserChanged(const QString &, int)), this, SLOT(onParserChanged(const QString &, int))); + delete m_parser_manager; + } + + if (settings) { + delete settings; + } +} + void Fahrplan::bindParserSignals() { if (m_parser_manager->getParser()) { @@ -91,6 +126,17 @@ void Fahrplan::bindParserSignals() } } +void Fahrplan::unbindParserSignals() +{ + if (m_parser_manager->getParser()) { + disconnect(m_parser_manager->getParser(), SIGNAL(stationsResult(StationsList)), this, SLOT(onStationSearchResults(StationsList))); + disconnect(m_parser_manager->getParser(), SIGNAL(journeyResult(JourneyResultList*)), this, SIGNAL(parserJourneyResult(JourneyResultList*))); + disconnect(m_parser_manager->getParser(), SIGNAL(errorOccured(QString)), this, SIGNAL(parserErrorOccured(QString))); + disconnect(m_parser_manager->getParser(), SIGNAL(journeyDetailsResult(JourneyDetailResultList*)), this, SIGNAL(parserJourneyDetailsResult(JourneyDetailResultList*))); + disconnect(m_parser_manager->getParser(), SIGNAL(timeTableResult(TimetableEntriesList)), this, SLOT(onTimetableResult(TimetableEntriesList))); + } +} + void Fahrplan::storeSettingsValue(const QString &key, const QString &value) { settings->setValue(key, value); diff --git a/src/fahrplan.h b/src/fahrplan.h index 4b00037c..a0bf1b60 100644 --- a/src/fahrplan.h +++ b/src/fahrplan.h @@ -79,6 +79,7 @@ class Fahrplan : public QObject }; explicit Fahrplan(QObject *parent = 0); + virtual ~Fahrplan(); FahrplanParserThread *parser(); Favorites *favorites() const; QString parserName() const; @@ -143,6 +144,7 @@ class Fahrplan : public QObject void onStationSearchResults(const StationsList &result); void onTimetableResult(const TimetableEntriesList &timetableEntries); void bindParserSignals(); + void unbindParserSignals(); private: static FahrplanBackendManager *m_parser_manager; diff --git a/src/fahrplan_backend_manager.cpp b/src/fahrplan_backend_manager.cpp index 6831aab0..97170132 100644 --- a/src/fahrplan_backend_manager.cpp +++ b/src/fahrplan_backend_manager.cpp @@ -26,6 +26,14 @@ FahrplanBackendManager::FahrplanBackendManager(int defaultParser, QObject *paren currentParserIndex = defaultParser; } +FahrplanBackendManager::~FahrplanBackendManager() +{ + if (m_parser) { + // Parser object will be autodeleted after the thread quits. + m_parser->quit(); + } +} + QStringList FahrplanBackendManager::getParserList() { QStringList result; @@ -45,6 +53,8 @@ QStringList FahrplanBackendManager::getParserList() result.append(ParserSalzburgEFA::getName()); result.append(ParserResRobot::getName()); result.append(ParserFinlandMatka::getName()); + result.append(ParserLondonTfl::getName()); + result.append(ParserXmlRMVde::getName()); // Make sure the index is in bounds if (currentParserIndex > (result.count() - 1) || currentParserIndex < 0) { @@ -73,7 +83,7 @@ void FahrplanBackendManager::setParser(int index) m_parser->quit(); } - m_parser = new FahrplanParserThread(); + m_parser = new FahrplanParserThread(this); m_parser->init(index); // Init can fallback to another index if its invalid. diff --git a/src/fahrplan_backend_manager.h b/src/fahrplan_backend_manager.h index e78c0a36..5485bd5c 100644 --- a/src/fahrplan_backend_manager.h +++ b/src/fahrplan_backend_manager.h @@ -29,6 +29,7 @@ class FahrplanBackendManager : public QObject public: explicit FahrplanBackendManager(int defaultParser, QObject *parent = 0); + virtual ~FahrplanBackendManager(); QStringList getParserList(); void setParser(int index); FahrplanParserThread *getParser(); diff --git a/src/fahrplan_parser_thread.cpp b/src/fahrplan_parser_thread.cpp index 599ed2b5..26715a28 100644 --- a/src/fahrplan_parser_thread.cpp +++ b/src/fahrplan_parser_thread.cpp @@ -81,6 +81,11 @@ void FahrplanParserThread::cancelRequest() emit requestCancelRequest(); } +void FahrplanParserThread::clearJourney() +{ + emit requestClearJourney(); +} + QString FahrplanParserThread::name() { return m_name; } @@ -170,6 +175,12 @@ void FahrplanParserThread::run() case 15: m_parser = new ParserFinlandMatka(); break; + case 16: + m_parser = new ParserLondonTfl(); + break; + case 17: + m_parser = new ParserXmlRMVde(); + break; } m_name = m_parser->name(); @@ -191,6 +202,7 @@ void FahrplanParserThread::run() connect(this, SIGNAL(requestSearchJourney(Station,Station,Station,QDateTime,ParserAbstract::Mode,int)), m_parser, SLOT(searchJourney(Station,Station,Station,QDateTime,ParserAbstract::Mode,int)), Qt::QueuedConnection); connect(this, SIGNAL(requestSearchJourneyEarlier()), m_parser, SLOT(searchJourneyEarlier()), Qt::QueuedConnection); connect(this, SIGNAL(requestSearchJourneyLater()), m_parser, SLOT(searchJourneyLater()), Qt::QueuedConnection); + connect(this, SIGNAL(requestClearJourney()), m_parser, SLOT(clearJourney()), Qt::QueuedConnection); //Connect parser responses with threads corresponding results connect(m_parser, SIGNAL(errorOccured(QString)), this, SIGNAL(errorOccured(QString)), Qt::QueuedConnection); diff --git a/src/fahrplan_parser_thread.h b/src/fahrplan_parser_thread.h index 4ede6256..5608ffb5 100644 --- a/src/fahrplan_parser_thread.h +++ b/src/fahrplan_parser_thread.h @@ -40,6 +40,8 @@ #include "parser/parser_salzburg_efa.h" #include "parser/parser_resrobot.h" #include "parser/parser_finland_matka.h" +#include "parser/parser_london_tfl.h" +#include "parser/parser_xmlrmvde.h" class FahrplanParserThread : public QThread { @@ -57,6 +59,7 @@ class FahrplanParserThread : public QThread void requestSearchJourneyEarlier(); void requestGetJourneyDetails(const QString &id); void requestCancelRequest(); + void requestClearJourney(); //Real ones void stationsResult(const StationsList &result); @@ -77,6 +80,7 @@ public slots: void searchJourneyEarlier(); void getJourneyDetails(const QString &id); void cancelRequest(); + void clearJourney(); bool supportsGps(); bool supportsVia(); @@ -91,7 +95,7 @@ public slots: void run(); private: - bool m_ready; + volatile bool m_ready; int i_parser; QStringList m_trainrestrictions; diff --git a/src/gui/desktop-test/mainwindow.cpp b/src/gui/desktop-test/mainwindow.cpp index f460dca3..96061c34 100644 --- a/src/gui/desktop-test/mainwindow.cpp +++ b/src/gui/desktop-test/mainwindow.cpp @@ -146,7 +146,7 @@ void MainWindow::timeTableResult() for (int i=0; i < fahrplan->timetable()->count(); i++) { QModelIndex index = fahrplan->timetable()->index(i, 0, QModelIndex()); ui->searchJourneyResults->append(fahrplan->timetable()->data(index, Timetable::CurrentStation).toString()); - ui->searchJourneyResults->append(fahrplan->timetable()->data(index, Timetable::Time).toTime().toString(QLocale().timeFormat(QLocale::ShortFormat))); + ui->searchJourneyResults->append(fahrplan->timetable()->data(index, Timetable::Time).toTime().toString("HH:mm")); ui->searchJourneyResults->append(fahrplan->timetable()->data(index, Timetable::DestinationStation).toString()); ui->searchJourneyResults->append(fahrplan->timetable()->data(index, Timetable::TrainType).toString()); ui->searchJourneyResults->append(fahrplan->timetable()->data(index, Timetable::Platform).toString()); diff --git a/src/gui/ubuntu/AboutPage.qml b/src/gui/ubuntu/AboutPage.qml index 361d820d..60f9578a 100644 --- a/src/gui/ubuntu/AboutPage.qml +++ b/src/gui/ubuntu/AboutPage.qml @@ -146,7 +146,7 @@ Page { Item { width: tabView.width - height: tabView.height + height: tabView.height - header.height Flickable { clip: true @@ -154,7 +154,7 @@ Page { flickableDirection: Flickable.VerticalFlick contentWidth: width - contentHeight: creditsColumn.height + units.gu(2) + contentHeight: creditsColumn.height + units.gu(4) Column { id: creditsColumn diff --git a/src/gui/ubuntu/JourneyDetailsResultsPage.qml b/src/gui/ubuntu/JourneyDetailsResultsPage.qml index 88850da7..4bdd05eb 100644 --- a/src/gui/ubuntu/JourneyDetailsResultsPage.qml +++ b/src/gui/ubuntu/JourneyDetailsResultsPage.qml @@ -111,6 +111,9 @@ Page { id: delegateItem width: listView.width height: isStation ? isTrain ? item_train.height + item_station.height : item_station.height : isTrain ? item_train.height : 0 + property string previousRouteColor: (previousTrainColor != "") ? previousTrainColor : "#0d70c5"; + property string routeColor: (trainColor != "") ? trainColor : "#0d70c5"; + Item { anchors.verticalCenter: parent.verticalCenter @@ -132,14 +135,30 @@ Page { } Rectangle { + id: arrivalLine + visible: ! isStart anchors { left: parent.left leftMargin: units.gu(7.75) top: parent.top - topMargin: (isStart) ? parent.height / 2 : 0 + topMargin: 0 } - color: "#0d70c5" - height: (isStart || isStop) ? parent.height / 2 : parent.height + color: previousRouteColor + height: parent.height / 2 + width: units.gu(0.5) + } + + Rectangle { + id: departureLine + visible: ! isStop + anchors { + left: parent.left + leftMargin: units.gu(7.75) + top: parent.top + topMargin: parent.height / 2 + } + color: routeColor + height: parent.height / 2 width: units.gu(0.5) } @@ -153,11 +172,11 @@ Page { gradient: Gradient { GradientStop { position: 0.00; - color: "#0d70c5"; + color: previousRouteColor; } GradientStop { position: 0.38; - color: "#0d70c5"; + color: previousRouteColor; } GradientStop { position: 0.39; @@ -173,11 +192,11 @@ Page { } GradientStop { position: 0.62; - color: "#0d70c5"; + color: routeColor; } GradientStop { position: 1.0; - color: "#0d70c5"; + color: routeColor; } } radius: units.gu(1) @@ -260,7 +279,7 @@ Page { left: parent.left leftMargin: units.gu(7.75) } - color: "#0d70c5" + color: routeColor height: parent.height width: units.gu(0.5) } @@ -320,7 +339,7 @@ Page { console.log(result.count); if (result.count > 0) { - titleText = result.viaStation.length == 0 ? qsTr("%1%2").arg(result.departureStation).arg(result.arrivalStation) : qsTr("%1%3%2").arg(result.departureStation).arg(result.arrivalStation).arg(result.viaStation); + titleText = result.viaStation.length === 0 ? qsTr("%1%2").arg(result.departureStation).arg(result.arrivalStation) : qsTr("%1%3%2").arg(result.departureStation).arg(result.arrivalStation).arg(result.viaStation); var departureDate = Qt.formatDate(result.departureDateTime); var arrivalDate = Qt.formatDate(result.arrivalDateTime); @@ -328,14 +347,18 @@ Page { arrivalDate = ""; } - subTitleText = departureDate + " " + Qt.formatTime(result.departureDateTime, Qt.DefaultLocaleShortDate) + " - " + - arrivalDate + " " + Qt.formatTime(result.arrivalDateTime, Qt.DefaultLocaleShortDate); + subTitleText = departureDate + " " + Qt.formatTime(result.departureDateTime, 'HH:mm') + " - " + arrivalDate + " " + Qt.formatTime(result.arrivalDateTime, 'HH:mm'); subTitleText2 = qsTr("Dur.: %1").arg(result.duration); journeyDetailResultModel.clear(); for (var i = 0; i < result.count; i++) { var item = result.getItem(i); + + if (item === null) + { + continue; + } var nextItem = null; if (i < result.count -1) { @@ -365,10 +388,12 @@ Page { "trainName" : item.train, "trainDirection" : item.direction, "trainInfo" : item.info, + "previousTrainColor" : item.color, + "trainColor" : item.color, "stationName" : item.departureStation, "stationInfo" : item.departureInfo, "arrivalTime" : "", - "departureTime" : Qt.formatTime(item.departureDateTime, "hh:mm"), + "departureTime" : Qt.formatTime(item.departureDateTime, "HH:mm"), "isStation" : true, "isTrain" : true @@ -383,9 +408,11 @@ Page { "trainName" : "", "trainDirection": "", "trainInfo" : "", + "previousTrainColor" : item.color, + "trainColor" : item.color, "stationName" : item.arrivalStation, "stationInfo" : item.arrivalInfo, - "arrivalTime" : Qt.formatTime(item.arrivalDateTime, "hh:mm"), + "arrivalTime" : Qt.formatTime(item.arrivalDateTime, "HH:mm"), "departureTime" : "", "isStation" : true, "isTrain" : false @@ -401,7 +428,7 @@ Page { if (stationInfo.length > 0 && nextItem.departureInfo) { stationInfo = stationInfo + " / "; } - if (nextItem.departureStation != item.arrivalStation) { + if (nextItem.departureStation !== item.arrivalStation) { stationName += " / " + nextItem.departureStation; } @@ -413,10 +440,12 @@ Page { "trainName" : nextItem.train, "trainDirection" : nextItem.direction, "trainInfo" : nextItem.info, + "previousTrainColor" : item.color, + "trainColor" : nextItem.color, "stationName" : stationName, "stationInfo" : stationInfo, - "arrivalTime" : Qt.formatTime(item.arrivalDateTime, "hh:mm"), - "departureTime" : Qt.formatTime(nextItem.departureDateTime, "hh:mm"), + "arrivalTime" : Qt.formatTime(item.arrivalDateTime, "HH:mm"), + "departureTime" : Qt.formatTime(nextItem.departureDateTime, "HH:mm"), "isStation" : true, "isTrain" : true diff --git a/src/gui/ubuntu/MainPage.qml b/src/gui/ubuntu/MainPage.qml index 5ad719f3..c3d5c596 100644 --- a/src/gui/ubuntu/MainPage.qml +++ b/src/gui/ubuntu/MainPage.qml @@ -28,24 +28,25 @@ Page { id: mainPage property int searchmode : 0 + property bool timeTableEnabled: true property bool startup : true header: PageHeader { title: tabs.selectedTabIndex === 0 ? qsTr("Journey") : qsTr("Time table") flickable: flickable - + leadingActionBar.actions: [ Action { text: qsTr("Journey") onTriggered: tabs.selectedTabIndex = 0 }, - Action { text: qsTr("Time table") onTriggered: tabs.selectedTabIndex = 1 + enabled: timeTableEnabled } ] - + leadingActionBar.numberOfSlots: 0 trailingActionBar.actions: Action { @@ -67,8 +68,14 @@ Page { function updateButtonVisibility() { - if (!fahrplanBackend.parser.supportsTimeTable()) { - searchmode = 0; + //console.log("button..."); + + if (fahrplanBackend.parser.supportsTimeTable()) { + timeTableEnabled = true; + } + else { + timeTableEnabled = false; + tabs.selectedTabIndex = 0 } if (searchmode == 0) { @@ -144,7 +151,6 @@ Page { font.bold: true; textSize: Label.Large elide: Text.ElideRight - text: fahrplanBackend.parserShortName } } @@ -337,7 +343,7 @@ Page { id: timePickerButton title.text: qsTr("Time") - value: Qt.formatTime(fahrplanBackend.dateTime, Qt.DefaultLocaleShortDate) + value: Qt.formatTime(fahrplanBackend.dateTime, 'HH:mm') visible: timeModeSelector.selectedIndex !== 0 onClicked: { @@ -542,7 +548,7 @@ Page { currentParserName.text = fahrplanBackend.parserShortName; updateButtonVisibility(); - selectedBackendListView.currentIndex = fahrplanBackend.backends.getItemIndexForParserId(index); + //selectedBackendListView.currentIndex = fahrplanBackend.backends.getItemIndexForParserId(index); } } diff --git a/src/gui/ubuntu/TimeTableResultsPage.qml b/src/gui/ubuntu/TimeTableResultsPage.qml index c08c7f8a..557833a3 100644 --- a/src/gui/ubuntu/TimeTableResultsPage.qml +++ b/src/gui/ubuntu/TimeTableResultsPage.qml @@ -86,7 +86,7 @@ Page { Label { id: lbl_time - text: Qt.formatTime(model.time, Qt.DefaultLocaleShortDate) + text: Qt.formatTime(model.time, 'HH:mm') font.bold: true } diff --git a/src/gui/ubuntu/components/StationSelect.qml b/src/gui/ubuntu/components/StationSelect.qml index 617ded81..e913d67a 100644 --- a/src/gui/ubuntu/components/StationSelect.qml +++ b/src/gui/ubuntu/components/StationSelect.qml @@ -35,7 +35,10 @@ Page { property bool showFavorites: true - head.actions: [ + header: PageHeader { + + title: stationSelect.title + trailingActionBar.actions: [ Action { iconName: stationSelect.showFavorites ? "starred" : "non-starred" onTriggered: { @@ -52,11 +55,12 @@ Page { } } ] + } TextField { id: search - anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(2) } + anchors { left: parent.left; right: parent.right; top: header.bottom; margins: units.gu(2) } inputMethodHints: Qt.ImhNoPredictiveText onTextChanged: searchTimer.restart(); diff --git a/src/parser/parser_abstract.cpp b/src/parser/parser_abstract.cpp index 33d07d74..c99aedf0 100644 --- a/src/parser/parser_abstract.cpp +++ b/src/parser/parser_abstract.cpp @@ -47,7 +47,7 @@ #endif ParserAbstract::ParserAbstract(QObject *parent) : - QObject(parent) + QObject(parent), lastRequest(NULL) { NetworkManager = new QNetworkAccessManager(this); connect(NetworkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkReplyFinished(QNetworkReply*))); @@ -56,7 +56,7 @@ ParserAbstract::ParserAbstract(QObject *parent) : currentRequestState = FahrplanNS::noneRequest; - requestTimeout = new QTimer(); + requestTimeout = new QTimer(this); connect(requestTimeout, SIGNAL(timeout()), this, SLOT(networkReplyTimedOut())); @@ -67,10 +67,16 @@ ParserAbstract::ParserAbstract(QObject *parent) : ParserAbstract::~ParserAbstract() { + clearJourney(); delete requestTimeout; delete NetworkManager; } +void ParserAbstract::clearJourney() +{ + +} + void ParserAbstract::networkReplyFinished(QNetworkReply *networkReply) { FahrplanNS::curReqStates internalRequestState = currentRequestState; @@ -168,9 +174,9 @@ QVariantMap ParserAbstract::parseJson(const QByteArray &json) const } #ifdef BUILD_FOR_QT5 -QByteArray ParserAbstract::serializeToJson(const QVariantMap& doc) const +QByteArray ParserAbstract::serializeToJson(const QVariantMap& doc, bool indent) const { - return QJsonDocument(QJsonObject::fromVariantMap(doc)).toJson(QJsonDocument::Indented); + return QJsonDocument(QJsonObject::fromVariantMap(doc)).toJson(indent ? QJsonDocument::Indented : QJsonDocument::Compact); } #else QByteArray toJson(const QVariant& value) @@ -212,8 +218,9 @@ QByteArray toJson(const QVariant& value) } } -QByteArray ParserAbstract::serializeToJson(const QVariantMap& doc) const +QByteArray ParserAbstract::serializeToJson(const QVariantMap& doc, bool indent) const { + Q_UNUSED(indent) return toJson(doc); } #endif diff --git a/src/parser/parser_abstract.h b/src/parser/parser_abstract.h index c31823ec..4f7673b0 100644 --- a/src/parser/parser_abstract.h +++ b/src/parser/parser_abstract.h @@ -38,7 +38,7 @@ class ParserAbstract : public QObject enum Mode { Departure = 0, Arrival = 1 }; explicit ParserAbstract(QObject *parent = 0); - ~ParserAbstract(); + virtual ~ParserAbstract(); static QString getName() { return "Abstract"; } virtual QString name() { return getName(); } @@ -59,6 +59,7 @@ public slots: virtual bool supportsTimeTableDirection(); virtual QStringList getTrainRestrictions(); void cancelRequest(); + virtual void clearJourney(); signals: void stationsResult(const StationsList &result); @@ -93,7 +94,7 @@ protected slots: void sendHttpRequest(QUrl url, QByteArray data, const QList > &additionalHeaders = QList >()); void sendHttpRequest(QUrl url); QVariantMap parseJson(const QByteArray &data) const; - QByteArray serializeToJson(const QVariantMap &doc) const; + QByteArray serializeToJson(const QVariantMap &doc, bool indent = true) const; QByteArray gzipDecompress(QByteArray compressData); #ifdef BUILD_FOR_UBUNTU diff --git a/src/parser/parser_definitions.cpp b/src/parser/parser_definitions.cpp index 13363e72..b15e3c6a 100644 --- a/src/parser/parser_definitions.cpp +++ b/src/parser/parser_definitions.cpp @@ -71,6 +71,16 @@ TimetableEntry::TimetableEntry() //------------- JourneyResultList +JourneyResultList::JourneyResultList(QObject * parent) : QObject(parent) +{ + +} + +JourneyResultList::~JourneyResultList() +{ + qDeleteAll(m_items); +} + qreal JourneyResultList::itemcount() { return m_items.count(); @@ -127,6 +137,10 @@ void JourneyResultList::setTimeInfo(const QString &timeInfo) } //------------- JourneyResultItem +JourneyResultItem::JourneyResultItem(QObject *parent) : QObject(parent) +{ + +} QString JourneyResultItem::id() const { @@ -230,6 +244,16 @@ void JourneyResultItem::setInternalData2(const QString &internalData2) //------------- JourneyDetailResultList +JourneyDetailResultList::JourneyDetailResultList(QObject * parent) : QObject(parent) +{ + +} + +JourneyDetailResultList::~JourneyDetailResultList() +{ + qDeleteAll(m_items); +} + QString JourneyDetailResultList::id() const { return m_id; @@ -327,6 +351,11 @@ void JourneyDetailResultList::setDuration(const QString &duration) //------------- JourneyDetailResultItem +JourneyDetailResultItem::JourneyDetailResultItem(QObject *parent) : QObject(parent) +{ + +} + QString JourneyDetailResultItem::departureStation() const { return m_departureStation; @@ -417,6 +446,17 @@ void JourneyDetailResultItem::setDirection(const QString &direction) m_direction = direction; } + +QString JourneyDetailResultItem::color() const +{ + return m_color.isValid() ? m_color.name() : ""; +} + +void JourneyDetailResultItem::setColor(const QColor &color) +{ + m_color = color; +} + QString JourneyDetailResultItem::internalData1() const { return m_internalData1; diff --git a/src/parser/parser_definitions.h b/src/parser/parser_definitions.h index 7587c1ab..c3275fc7 100644 --- a/src/parser/parser_definitions.h +++ b/src/parser/parser_definitions.h @@ -21,6 +21,7 @@ #define PARSER_DEFINITIONS_H #include +#include #include #include #include @@ -95,6 +96,7 @@ class JourneyResultItem : public QObject Q_PROPERTY(QString internalData2 READ internalData2 WRITE setInternalData2) public: + explicit JourneyResultItem(QObject * parent = 0); QString id() const; void setId(const QString &); QDate date() const; @@ -140,6 +142,8 @@ class JourneyResultList : public QObject public slots: JourneyResultItem *getItem(int); public: + explicit JourneyResultList(QObject * parent = 0); + virtual ~JourneyResultList(); void appendItem(JourneyResultItem *item); qreal itemcount(); QString departureStation() const; @@ -171,11 +175,13 @@ class JourneyDetailResultItem : public QObject Q_PROPERTY(QString info READ info WRITE setInfo) Q_PROPERTY(QString train READ train WRITE setTrain) Q_PROPERTY(QString direction READ direction WRITE setDirection) + Q_PROPERTY(QString color READ color) //Some Internal Data fields, primarly to store additional data per backend, like the details url Q_PROPERTY(QString internalData1 READ internalData1 WRITE setInternalData1) Q_PROPERTY(QString internalData2 READ internalData2 WRITE setInternalData2) public: + explicit JourneyDetailResultItem(QObject * parent = 0); QString departureStation() const; void setDepartureStation(const QString &); QString departureInfo() const; @@ -194,6 +200,8 @@ class JourneyDetailResultItem : public QObject void setTrain(const QString &); QString direction() const; void setDirection(const QString &); + QString color() const; + void setColor(const QColor &); QString internalData1() const; void setInternalData1(const QString &); QString internalData2() const; @@ -208,6 +216,7 @@ class JourneyDetailResultItem : public QObject QString m_info; QString m_train; QString m_direction; + QColor m_color; QString m_internalData1; QString m_internalData2; }; @@ -228,6 +237,8 @@ class JourneyDetailResultList : public QObject public slots: JourneyDetailResultItem *getItem(int); public: + explicit JourneyDetailResultList(QObject * parent = 0); + virtual ~JourneyDetailResultList(); void appendItem(JourneyDetailResultItem *item); qreal itemcount(); QString id() const; diff --git a/src/parser/parser_efa.cpp b/src/parser/parser_efa.cpp index d934be35..9ece516b 100755 --- a/src/parser/parser_efa.cpp +++ b/src/parser/parser_efa.cpp @@ -99,15 +99,35 @@ #include #endif -QHash cachedJourneyDetailsEfa; +//QHash cachedJourneyDetailsEfa; ParserEFA::ParserEFA(QObject *parent) : - ParserAbstract(parent){ + ParserAbstract(parent), lastJourneyResultList(NULL) +{ m_searchJourneyParameters.isValid = false; m_timeTableForStationParameters.isValid = false; } +ParserEFA::~ParserEFA() +{ + clearJourney(); +} + +void ParserEFA::clearJourney() +{ + for (QHash::Iterator it = cachedJourneyDetailsEfa.begin(); it != cachedJourneyDetailsEfa.end();) { + JourneyDetailResultList *jdrl = it.value(); + it = cachedJourneyDetailsEfa.erase(it); + delete jdrl; + } + + if (lastJourneyResultList) { + delete lastJourneyResultList; + lastJourneyResultList = NULL; + } +} + bool ParserEFA::supportsGps() { return true; @@ -402,6 +422,8 @@ void ParserEFA::searchJourney(const Station &departureStation, const Station &vi return; currentRequestState = FahrplanNS::searchJourneyRequest; + clearJourney(); + m_searchJourneyParameters.isValid = false; m_searchJourneyParameters.departureStation = departureStation; m_searchJourneyParameters.arrivalStation = arrivalStation; @@ -504,13 +526,8 @@ void ParserEFA::searchJourney(const Station &departureStation, const Station &vi void ParserEFA::parseSearchJourney(QNetworkReply *networkReply) { qDebug() << "ParserEFA::parseSearchJourney(QNetworkReply *networkReply)"; - lastJourneyResultList = new JourneyResultList(); - for (QHash::Iterator it = cachedJourneyDetailsEfa.begin(); it != cachedJourneyDetailsEfa.end();) { - JourneyDetailResultList *jdrl = it.value(); - it = cachedJourneyDetailsEfa.erase(it); - delete jdrl; - } + lastJourneyResultList = new JourneyResultList(this); /// Use fallback values for empty results (i.e. no connections found) lastJourneyResultList->setDepartureStation(m_searchJourneyParameters.departureStation.name); @@ -528,7 +545,7 @@ void ParserEFA::parseSearchJourney(QNetworkReply *networkReply) QDomElement route = doc.firstChildElement("itdRequest").firstChildElement("itdTripRequest").firstChildElement("itdItinerary").firstChildElement("itdRouteList").firstChildElement("itdRoute"); for (; !route.isNull(); route = route.nextSiblingElement("itdRoute")) { QStringList motNameList; - JourneyDetailResultList *detailsList = new JourneyDetailResultList(); + JourneyDetailResultList *detailsList = new JourneyDetailResultList(this); QDomElement partialRoute = route.firstChildElement("itdPartialRouteList").firstChildElement("itdPartialRoute"); for (; !partialRoute.isNull(); partialRoute = partialRoute.nextSiblingElement("itdPartialRoute")) { QDomElement motElement = partialRoute.firstChildElement("itdMeansOfTransport"); @@ -542,7 +559,7 @@ void ParserEFA::parseSearchJourney(QNetworkReply *networkReply) info = tr("Guaranteed connection"); } motNameList.append(motName); - JourneyDetailResultItem *jdrItem = new JourneyDetailResultItem(); + JourneyDetailResultItem *jdrItem = new JourneyDetailResultItem(detailsList); jdrItem->setTrain(motName); jdrItem->setInfo(info); jdrItem->setDirection(motElement.attribute("destination")); @@ -578,9 +595,9 @@ void ParserEFA::parseSearchJourney(QNetworkReply *networkReply) detailsList->setDuration(duration); detailsList->setArrivalDateTime(arrivalDateTime); detailsList->setDepartureDateTime(departureDateTime); - cachedJourneyDetailsEfa[id] = detailsList; + cachedJourneyDetailsEfa.insert(id, detailsList); - JourneyResultItem *item = new JourneyResultItem(); + JourneyResultItem *item = new JourneyResultItem(lastJourneyResultList); item->setDate(departureDateTime.date()); item->setId(id); item->setTransfers(changes); @@ -591,10 +608,13 @@ void ParserEFA::parseSearchJourney(QNetworkReply *networkReply) item->setArrivalTime(arrivalDateTime.toString("hh:mm")); lastJourneyResultList->appendItem(item); - if (!m_earliestArrival.isValid() || arrivalDateTime < m_earliestArrival) + if (!m_earliestArrival.isValid() || arrivalDateTime < m_earliestArrival) { m_earliestArrival = arrivalDateTime.addSecs(-60); - if (!m_latestResultDeparture.isValid() || departureDateTime > m_latestResultDeparture) + } + + if (!m_latestResultDeparture.isValid() || departureDateTime > m_latestResultDeparture) { m_latestResultDeparture = departureDateTime.addSecs(60); + } } checkForError(&doc); @@ -619,20 +639,19 @@ void ParserEFA::searchJourneyLater() { qDebug() << "ParserEFA::searchJourneyLater()"; - if (m_latestResultDeparture.isValid()) - { + if (m_latestResultDeparture.isValid()) { qDebug() << "m_latestResultDeparture.isValid()"; searchJourney(m_searchJourneyParameters.departureStation, m_searchJourneyParameters.viaStation, m_searchJourneyParameters.arrivalStation, m_latestResultDeparture, Departure, 0); - } - else { + } else { qDebug() << "!m_latestResultDeparture.isValid(), "; - JourneyResultList *journeyResultList = new JourneyResultList(); - journeyResultList->setDepartureStation(m_searchJourneyParameters.departureStation.name); - journeyResultList->setViaStation(m_searchJourneyParameters.viaStation.name); - journeyResultList->setArrivalStation(m_searchJourneyParameters.arrivalStation.name); + clearJourney(); + lastJourneyResultList = new JourneyResultList(this); + lastJourneyResultList->setDepartureStation(m_searchJourneyParameters.departureStation.name); + lastJourneyResultList->setViaStation(m_searchJourneyParameters.viaStation.name); + lastJourneyResultList->setArrivalStation(m_searchJourneyParameters.arrivalStation.name); //: DATE, TIME - journeyResultList->setTimeInfo(tr("%1, %2", "DATE, TIME").arg(m_searchJourneyParameters.dateTime.date().toString(Qt::DefaultLocaleShortDate)).arg(m_searchJourneyParameters.dateTime.time().toString(Qt::DefaultLocaleShortDate))); - emit journeyResult(journeyResultList); + lastJourneyResultList->setTimeInfo(tr("%1, %2", "DATE, TIME").arg(m_searchJourneyParameters.dateTime.date().toString(Qt::DefaultLocaleShortDate)).arg(m_searchJourneyParameters.dateTime.time().toString(Qt::DefaultLocaleShortDate))); + emit journeyResult(lastJourneyResultList); } } @@ -642,13 +661,14 @@ void ParserEFA::searchJourneyEarlier() if (m_earliestArrival.isValid()) searchJourney(m_searchJourneyParameters.departureStation, m_searchJourneyParameters.viaStation, m_searchJourneyParameters.arrivalStation, m_earliestArrival, Arrival, 0); else { - JourneyResultList *journeyResultList = new JourneyResultList(); - journeyResultList->setDepartureStation(m_searchJourneyParameters.departureStation.name); - journeyResultList->setViaStation(m_searchJourneyParameters.viaStation.name); - journeyResultList->setArrivalStation(m_searchJourneyParameters.arrivalStation.name); + clearJourney(); + lastJourneyResultList = new JourneyResultList(this); + lastJourneyResultList->setDepartureStation(m_searchJourneyParameters.departureStation.name); + lastJourneyResultList->setViaStation(m_searchJourneyParameters.viaStation.name); + lastJourneyResultList->setArrivalStation(m_searchJourneyParameters.arrivalStation.name); //: DATE, TIME - journeyResultList->setTimeInfo(tr("%1, %2", "DATE, TIME").arg(m_searchJourneyParameters.dateTime.date().toString(Qt::DefaultLocaleShortDate)).arg(m_searchJourneyParameters.dateTime.time().toString(Qt::DefaultLocaleShortDate))); - emit journeyResult(journeyResultList); + lastJourneyResultList->setTimeInfo(tr("%1, %2", "DATE, TIME").arg(m_searchJourneyParameters.dateTime.date().toString(Qt::DefaultLocaleShortDate)).arg(m_searchJourneyParameters.dateTime.time().toString(Qt::DefaultLocaleShortDate))); + emit journeyResult(lastJourneyResultList); } } diff --git a/src/parser/parser_efa.h b/src/parser/parser_efa.h index 1ce9573b..6d95cc93 100755 --- a/src/parser/parser_efa.h +++ b/src/parser/parser_efa.h @@ -29,6 +29,7 @@ class ParserEFA : public ParserAbstract Q_OBJECT public: explicit ParserEFA(QObject *parent = 0); + virtual ~ParserEFA(); static QString getName() { return "EFA"; } virtual QString name() { return getName(); } virtual QString shortName() { return getName(); } @@ -47,6 +48,7 @@ public slots: bool supportsTimeTableDirection(); void checkForError(QDomDocument *serverReplyDomDoc); QStringList getTrainRestrictions(); + virtual void clearJourney(); protected: QString baseRestUrl; @@ -59,6 +61,7 @@ public slots: private: JourneyResultList *lastJourneyResultList; + QHash cachedJourneyDetailsEfa; struct { bool isValid; diff --git a/src/parser/parser_hafasbinary.cpp b/src/parser/parser_hafasbinary.cpp index b66dcc47..8fcbfbc7 100644 --- a/src/parser/parser_hafasbinary.cpp +++ b/src/parser/parser_hafasbinary.cpp @@ -43,7 +43,8 @@ void ParserHafasBinary::searchJourney(const Station &departureStation, const Sta currentRequestState = FahrplanNS::searchJourneyRequest; hafasContext.seqNr = ""; - lastJourneyResultList = NULL; + + clearJourney(); QString trainrestr = getTrainRestrictionsCodes(trainrestrictions); @@ -85,8 +86,8 @@ void ParserHafasBinary::searchJourney(const Station &departureStation, const Sta void ParserHafasBinary::parseSearchJourney(QNetworkReply *networkReply) { - lastJourneyResultList = new JourneyResultList(); - journeyDetailInlineData.clear(); + lastJourneyResultList = new JourneyResultList(this); + stringCache.clear(); QByteArray tmpBuffer = networkReply->readAll(); @@ -324,11 +325,11 @@ void ParserHafasBinary::parseSearchJourney(QNetworkReply *networkReply) qDebug()<<"conId"<seek(0x4a + partsOffset + iPart * 20); @@ -571,7 +572,7 @@ void ParserHafasBinary::parseSearchJourney(QNetworkReply *networkReply) lineNames.removeDuplicates(); - JourneyResultItem *item = new JourneyResultItem(); + JourneyResultItem *item = new JourneyResultItem(lastJourneyResultList); item->setDate(journeyDate); item->setId(connectionId); item->setTransfers(QString::number(numChanges)); @@ -580,16 +581,12 @@ void ParserHafasBinary::parseSearchJourney(QNetworkReply *networkReply) if (realtimeStatus != 2) { item->setMiscInfo(""); } else { - item->setMiscInfo(QString("%1") - .arg(tr("Journey contains canceled trains!"))); + item->setMiscInfo(QString("%1").arg(tr("Journey contains canceled trains!"))); } item->setTrainType(lineNames.join(", ").trimmed()); - const QString timeFormat = QLocale().timeFormat(QLocale::ShortFormat); - item->setDepartureTime(inlineResults->getItem(0)->departureDateTime() - .time().toString(timeFormat)); - item->setArrivalTime(inlineResults - ->getItem(inlineResults->itemcount() - 1)->arrivalDateTime() - .time().toString(timeFormat)); + const QString timeFormat = "HH:mm"; + item->setDepartureTime(inlineResults->getItem(0)->departureDateTime().time().toString(timeFormat)); + item->setArrivalTime(inlineResults->getItem(inlineResults->itemcount() - 1)->arrivalDateTime().time().toString(timeFormat)); journeyResultsByArrivalMap.insert(inlineResults->getItem(inlineResults->itemcount() - 1)->arrivalDateTime(), item); } } @@ -633,6 +630,8 @@ void ParserHafasBinary::searchJourneyLater() currentRequestState = FahrplanNS::searchJourneyLaterRequest; + clearJourney(); + QUrl uri = baseBinaryUrl; #if defined(BUILD_FOR_QT5) QUrlQuery query; @@ -668,6 +667,8 @@ void ParserHafasBinary::searchJourneyEarlier() currentRequestState = FahrplanNS::searchJourneyEarlierRequest; + clearJourney(); + QUrl uri = baseBinaryUrl; #if defined(BUILD_FOR_QT5) QUrlQuery query; diff --git a/src/parser/parser_hafasxml.cpp b/src/parser/parser_hafasxml.cpp index 72f6ccd6..f4f1f21f 100644 --- a/src/parser/parser_hafasxml.cpp +++ b/src/parser/parser_hafasxml.cpp @@ -32,7 +32,7 @@ // http://stefanwehrmeyer.com/projects/vbbxsd/ ParserHafasXml::ParserHafasXml(QObject *parent) : - ParserAbstract(parent) + ParserAbstract(parent), lastJourneyResultList(NULL) { //baseUrl = "http://fahrplan.oebb.at/bin/query.exe"; //OEB (fully operational/no RT) //no xmlhandle, detaildate already present! //baseUrl = "http://hafas.bene-system.com/bin/query.exe"; //hafas dev?? system? / no gps @@ -50,6 +50,24 @@ ParserHafasXml::ParserHafasXml(QObject *parent) : STTableMode = 0; } +ParserHafasXml::~ParserHafasXml() +{ + clearJourney(); +} + +void ParserHafasXml::clearJourney() +{ + if (lastJourneyResultList) { + delete lastJourneyResultList; + lastJourneyResultList = NULL; + } + + if (!journeyDetailInlineData.isEmpty()) { + qDeleteAll(journeyDetailInlineData); + journeyDetailInlineData.clear(); + } +} + bool ParserHafasXml::supportsGps() { return true; @@ -526,7 +544,8 @@ void ParserHafasXml::searchJourney(const Station &departureStation, const Statio currentRequestState = FahrplanNS::searchJourneyRequest; hafasContext.seqNr = ""; - lastJourneyResultList = NULL; + + clearJourney(); QString trainrestr = getTrainRestrictionsCodes(trainrestrictions); @@ -655,8 +674,7 @@ QString ParserHafasXml::parseExternalIds(const QVariant &id) const void ParserHafasXml::parseSearchJourney(QNetworkReply *networkReply) { - lastJourneyResultList = new JourneyResultList(); - journeyDetailInlineData.clear(); + lastJourneyResultList = new JourneyResultList(this); QDomDocument doc; if (!parseXml(doc, networkReply->readAll())) @@ -664,7 +682,7 @@ void ParserHafasXml::parseSearchJourney(QNetworkReply *networkReply) const QDomNodeList connections = doc.elementsByTagName("Connection"); for (int i = 0; i < connections.count(); ++i) { - JourneyResultItem *item = new JourneyResultItem(); + JourneyResultItem *item = new JourneyResultItem(lastJourneyResultList); item->setId(connections.at(i).toElement().attribute("id").trimmed()); QDomElement overview = connections.at(i).firstChildElement("Overview"); @@ -757,6 +775,8 @@ void ParserHafasXml::searchJourneyLater() currentRequestState = FahrplanNS::searchJourneyLaterRequest; + clearJourney(); + QByteArray postData = ""; postData.append(""); postData.append(""); @@ -782,6 +802,8 @@ void ParserHafasXml::searchJourneyEarlier() currentRequestState = FahrplanNS::searchJourneyEarlierRequest; + clearJourney(); + QByteArray postData = ""; postData.append(""); postData.append(""); @@ -850,11 +872,11 @@ void ParserHafasXml::getJourneyDetails(const QString &id) JourneyDetailResultList* ParserHafasXml::internalParseJourneyDetails(const QDomElement &connection) { - JourneyDetailResultList *results = new JourneyDetailResultList(); + JourneyDetailResultList *results = new JourneyDetailResultList(this); const QDomNodeList sections = connection.elementsByTagName("ConSection"); for (int i = 0; i < sections.count(); ++i) { - JourneyDetailResultItem *item = new JourneyDetailResultItem(); + JourneyDetailResultItem *item = new JourneyDetailResultItem(results); const QDomNode section = sections.at(i); QDomElement stop; diff --git a/src/parser/parser_hafasxml.h b/src/parser/parser_hafasxml.h index aee85971..513d206a 100644 --- a/src/parser/parser_hafasxml.h +++ b/src/parser/parser_hafasxml.h @@ -57,6 +57,7 @@ class ParserHafasXml : public ParserAbstract Q_OBJECT public: explicit ParserHafasXml(QObject *parent = 0); + virtual ~ParserHafasXml(); static QString getName() { return "HafasXML"; } virtual QString name() { return getName(); } virtual QString shortName() { return getName(); } @@ -74,6 +75,7 @@ public slots: bool supportsTimeTable(); bool supportsTimeTableDirection(); QStringList getTrainRestrictions(); + virtual void clearJourney(); protected: QString baseXmlUrl; diff --git a/src/parser/parser_london_tfl.cpp b/src/parser/parser_london_tfl.cpp new file mode 100644 index 00000000..6eeb1cdd --- /dev/null +++ b/src/parser/parser_london_tfl.cpp @@ -0,0 +1,893 @@ +/**************************************************************************** +** +** This file is a part of Fahrplan. +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU 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 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 "parser_london_tfl.h" + +#include +#include +#include +#ifdef BUILD_FOR_QT5 +# include +#else +#include +# define setQuery(q) setQueryItems(q.queryItems()) +#endif +#include + +namespace TransportModes +{ + const QString Bus = "bus"; + const QString CableCar = "cable-car"; + const QString Coach = "coach"; + const QString Dlr = "dlr"; + const QString Rail = "national-rail"; + const QString Overground = "overground"; + const QString ReplacementBus = "replacement-bus"; + const QString RiverBus = "river-bus"; + const QString RiverTour = "river-tour"; + const QString TflRail = "tflrail"; + const QString Tram = "tram"; + const QString Tube = "tube"; + const QString Walking = "walking"; +} + +namespace +{ + const QUrl BaseUrl("https://api.tfl.gov.uk"); + + bool sortByTimeLessThan(const TimetableEntry &first, const TimetableEntry &second) + { + if (first.time == second.time) + return first.trainType < second.trainType; + else + return first.time < second.time; + } + + QString shortLineName(const QString & lineName) + { + QMap shortNamesMap; + + shortNamesMap["Hammersmith & City"] = "H & C"; + shortNamesMap["London Overground"] = "Overground"; + + // not found in map + if (shortNamesMap.find( lineName ) == shortNamesMap.end()) + return lineName; + + return shortNamesMap[lineName]; + } + + // e.g. replace "Underground Station" with symbol + QString shortStationName(const QString & stationName) + { + QString outputStationName = stationName; + + return outputStationName.replace("Underground Station", "🚇"); + } + + // tube color map + QMap initializeStaticTubeColorMap() { + + QMap tubeColorMap; + + tubeColorMap["Bakerloo"] = QColor("#B36305"); + tubeColorMap["Central"] = QColor("#E32017"); + tubeColorMap["Circle"] = QColor("#FFD300"); + tubeColorMap["District"] = QColor("#00782A"); + tubeColorMap["Hammersmith & City"] = QColor("#F3A9BB"); + tubeColorMap["Jubilee"] = QColor("#A0A5A9"); + tubeColorMap["Metropolitan"] = QColor("#9B0056"); + tubeColorMap["Northern"] = QColor("#000000"); + tubeColorMap["Piccadilly"] = QColor("#003688"); + tubeColorMap["Victoria"] = QColor("#0098D4"); + tubeColorMap["Waterloo & City"] = QColor("#95CDBA"); + + return tubeColorMap; + } + + const QMap tubeColorMap = initializeStaticTubeColorMap(); + + // mode color map + QMap initializeStaticModeColorMap() { + + QMap modeColorMap; + + modeColorMap[TransportModes::Bus] = QColor("#ff0000"); + modeColorMap[TransportModes::Dlr] = QColor( "#00A4A7"); + modeColorMap[TransportModes::Overground] = QColor("#EE7C0E"); + modeColorMap[TransportModes::Tram] = QColor("#84B817"); + modeColorMap[TransportModes::CableCar] = QColor("#de768b"); + modeColorMap[TransportModes::RiverBus] = QColor("#1c3e95"); + modeColorMap[TransportModes::RiverTour] = QColor("#009ddc"); + modeColorMap[TransportModes::Walking] = QColor("#CBCBCB"); + + return modeColorMap; + } + + const QMap modeColorMap = initializeStaticModeColorMap(); + + QColor getLineColor(const QString & mode, const QString & train = "") + { + if ((mode == TransportModes::Tube) && (train != "")) + { + if (tubeColorMap.find(train) == tubeColorMap.end()) + return QColor(); + + return tubeColorMap[train]; + } + + if (modeColorMap.find(mode) == modeColorMap.end()) + return QColor(); + + return modeColorMap[mode]; + } +} + +ParserLondonTfl::ParserLondonTfl(QObject *parent):ParserAbstract(parent),lastJourneyResultList(NULL) +{ + lastCoordinates.isValid = false; + + NetworkManagerTimeTableSubQuery = new QNetworkAccessManager(this); +} + +ParserLondonTfl::~ParserLondonTfl() +{ + clearJourney(); +} + +void ParserLondonTfl::clearJourney() +{ + if (lastJourneyResultList) { + delete lastJourneyResultList; + lastJourneyResultList = NULL; + } + + for (QMap::Iterator it = cachedResults.begin(); it != cachedResults.end();) { + JourneyDetailResultList *jdrl = it.value(); + it = cachedResults.erase(it); + delete jdrl; + } +} + +void ParserLondonTfl::getTimeTableForStation(const Station ¤tStation, + const Station &, + const QDateTime &, + ParserAbstract::Mode, + int trainrestrictions) +{ + QUrl relativeUri(QString("/stoppoint/%1").arg(parseJson(currentStation.id.toByteArray()).value("naptanId").toString())); + +#if defined(BUILD_FOR_QT5) + QUrlQuery query; +#else + QUrl query; +#endif + + relativeUri.setQuery(query); + + timetableRestrictions = trainrestrictions; + sendHttpRequest(BaseUrl.resolved(relativeUri)); + currentRequestState=FahrplanNS::getTimeTableForStationRequest; +} + +void ParserLondonTfl::findStationsByName(const QString &stationName) +{ + qDebug() << "FINDBYNAME"; + QUrl relativeUrl (QString("/Stoppoint/Search/%1").arg(stationName)); + +#if defined(BUILD_FOR_QT5) + QUrlQuery query; +#else + QUrl query; +#endif + relativeUrl.setQuery(query); + + sendHttpRequest(BaseUrl.resolved(relativeUrl)); + currentRequestState=FahrplanNS::stationsByNameRequest; +} + +void ParserLondonTfl::findStationsByCoordinates(qreal longitude, qreal latitude) +{ + QUrl relativeuri(QString("/Stoppoint")); + +#if defined(BUILD_FOR_QT5) + QUrlQuery query; +#else + QUrl query; +#endif + + // for testing (location near Oxford Circus) + //longitude = -0.14244; + //latitude = 51.51519; + + query.addQueryItem("lon", QString::number(longitude)); + query.addQueryItem("lat", QString::number(latitude)); + query.addQueryItem("stoptypes", "NaptanMetroStation,NaptanRailStation,NaptanBusCoachStation,NaptanFerryPort,NaptanPublicBusCoachTram"); + query.addQueryItem("radius","350"); + + relativeuri.setQuery(query); + + lastCoordinates.isValid = true; + lastCoordinates.latitude = latitude; + lastCoordinates.longitude = longitude; + + sendHttpRequest(BaseUrl.resolved(relativeuri)); + currentRequestState = FahrplanNS::stationsByCoordinatesRequest; +} + +void ParserLondonTfl::searchJourney(const Station &departureStation, + const Station &viaStation, + const Station &arrivalStation, + const QDateTime &dateTime, + const ParserAbstract::Mode mode, + int trainrestrictions) +{ + lastsearch.from=departureStation; + lastsearch.to=arrivalStation; + lastsearch.restrictions=trainrestrictions; + lastsearch.via=viaStation; + + QUrl relativeUri(QString("/Journey/Journeyresults/%1/to/%2").arg(parseJson(departureStation.id.toByteArray()).value("icsId").toString(), + parseJson(arrivalStation.id.toByteArray()).value("icsId").toString())); + +#if defined(BUILD_FOR_QT5) + QUrlQuery query; +#else + QUrl query; +#endif + + if(viaStation.valid) + query.addQueryItem("via", parseJson(viaStation.id.toByteArray()).value("icsId").toString()); + + query.addQueryItem("date", dateTime.toString("yyyyMMdd")); + query.addQueryItem("time", dateTime.toString("HHmm")); + query.addQueryItem("timeIs", (mode == Departure) ? "Departing" : "Arriving"); + + QStringList modesList = getModesFromTrainRestrictions(trainrestrictions); + + query.addQueryItem("mode", modesList.join(",")); + + relativeUri.setQuery(query); + sendHttpRequest(BaseUrl.resolved(relativeUri)); + + currentRequestState=FahrplanNS::searchJourneyRequest; +} + +void ParserLondonTfl::searchJourneyLater() +{ + QDateTime time = lastsearch.firstOption.addSecs(30*60); + searchJourney(lastsearch.from, lastsearch.via, lastsearch.to, time, Departure , lastsearch.restrictions); +} + +void ParserLondonTfl::searchJourneyEarlier() +{ + QDateTime time = lastsearch.firstOption.addSecs(-30*60); + searchJourney(lastsearch.from, lastsearch.via, lastsearch.to,time, Departure, lastsearch.restrictions); +} + +void ParserLondonTfl::getJourneyDetails(const QString &id) +{ + if(cachedResults.contains(id)) + emit journeyDetailsResult(cachedResults.value(id)); +} + +QStringList ParserLondonTfl::getTrainRestrictions() +{ + QStringList restrictions; + restrictions << tr("All"); + restrictions << tr("Nat. Rail, Tube, Overground, DLR"); + restrictions << tr("Tube, Overground, DLR"); + restrictions << tr("Bus, Tram, Tube, Overground, DLR"); + restrictions << tr("Bus, Tram"); + restrictions << tr("Bus"); + restrictions << tr("Nat. Rail"); + restrictions << tr("Tube"); + restrictions << tr("Overground"); + restrictions << tr("DLR"); + + return restrictions; +} + +void ParserLondonTfl::parseTimeTable(QNetworkReply *networkReply) +{ + // the response contains the line groups with the stop points. + qDebug() << "PARSING STATIONS"; + QByteArray allData = networkReply->readAll(); + qDebug() << "REPLY:>>>>>>>>>>>>\n" << allData; + + QVariantMap doc = parseJson(allData); + if (doc.isEmpty()) { + emit errorOccured(tr("Cannot parse reply from the server")); + return; + } + + QVariantList lineGroups = doc.value("lineGroup").toList(); + QStringList stopCodes; + + TimetableEntriesList result; + int counter; + + QVariantList::const_iterator i; + for (i = lineGroups.begin(); i != lineGroups.constEnd(); ++i) { + + counter++; + QVariantMap lineGroup = i->toMap(); + + QString stopCode; + stopCode = lineGroup.value("naptanIdReference").toString(); + + if (stopCode.isEmpty()) + { + stopCode = lineGroup.value("stationAtcoCode").toString(); + } + + if (! stopCodes.contains(stopCode)) + { + stopCodes.append(stopCode); + } + } + + QStringList::const_iterator j; + for (j = stopCodes.constBegin(); j != stopCodes.constEnd(); ++j) + { + addTimeTableEntriesOfStopPoint(*j, result); + } + + // Departures / arrivals are grouped by transportation type, + // while we want them sorted by departure / arrival time. + qSort(result.begin(), result.end(), sortByTimeLessThan); + + emit timetableResult(result); +} + +void ParserLondonTfl::parseStationsByName(QNetworkReply *networkReply) +{ + qDebug() << "PARSING STATIONS"; + QByteArray allData = networkReply->readAll(); + qDebug() << "REPLY:>>>>>>>>>>>>\n" << allData; + + QVariantMap doc = parseJson(allData); + if (doc.isEmpty()) { + emit errorOccured(tr("Cannot parse reply from the server")); + return; + } + + QVariantList stations = doc.value("matches").toList(); + + StationsList result; + + QVariantList::const_iterator i; + for (i = stations.constBegin(); i != stations.constEnd(); ++i) { + QVariantMap station = i->toMap(); + + // we need to keep the icsId and the + QVariantMap idMap; + idMap["icsId"] = station.value("icsId").toString(); + idMap["naptanId"] = station.value("id").toString(); + + Station s; + s.id = serializeToJson(idMap, false); + s.name = station.value("name").toString(); + s.latitude = station.value("lat").toDouble(); + s.longitude = station.value("lon").toDouble(); + if (station.contains("zone")) + { + s.miscInfo = "Zone " + station.value("zone").toString(); + } + result.append(s); + } + + emit stationsResult(result); +} + +void ParserLondonTfl::parseStationsByCoordinates(QNetworkReply *networkReply) +{ + qDebug() << "PARSING STATIONS BY COORDINATES"; + QByteArray allData = networkReply->readAll(); + qDebug() << "REPLY:>>>>>>>>>>>>\n" << allData; + + QVariantMap doc = parseJson(allData); + if (doc.isEmpty()) { + emit errorOccured(tr("Cannot parse reply from the server")); + return; + } + + QStringList icsIds; + QVariantList stations = doc.value("stopPoints").toList(); + + StationsList result; + + QVariantList::const_iterator i; + for (i = stations.constBegin(); i != stations.constEnd(); ++i) { + QVariantMap station = i->toMap(); + + // we skip entries without icsId + if (station.value("icsCode").toString().isEmpty()) + { + continue; + } + + QString currentIcsId = station.value("icsCode").toString(); + + // we skip already found icsIds + if (icsIds.contains(currentIcsId)) + { + continue; + } + + icsIds.append(currentIcsId); + + // we need to keep the icsId and the + QVariantMap idMap; + idMap["icsId"] = currentIcsId; + idMap["naptanId"] = station.value("id").toString(); + + Station s; + s.id = serializeToJson(idMap, false); + s.name = station.value("commonName").toString(); + s.latitude = station.value("lat").toDouble(); + s.longitude = station.value("lon").toDouble(); + + QVariantList additionalProperties = station.value("additionalProperties").toList(); + + QVariantList::const_iterator j; + for (j = additionalProperties.constBegin(); j != additionalProperties.constEnd(); ++j) { + + // find the zone information + QVariantMap currentProperty = j->toMap(); + + if (currentProperty.value("key").toString() == "Zone") + { + s.miscInfo = "Zone " + currentProperty.value("value").toString(); + + // currently we do not need more info + + break; + } + } + + result.append(s); + } + + emit stationsResult(result); +} + +void ParserLondonTfl::parseSearchJourney(QNetworkReply *networkReply) +{ + // baker street to great portland street + //https://api.tfl.gov.uk/Journey/Journeyresults/1000011/to/1000091 + + qDebug() << "PARSING JOURNEYS"; + QByteArray allData = networkReply->readAll(); + qDebug() << "REPLY:>>>>>>>>>>>>\n" << allData; + + QVariantMap doc = parseJson(allData); + if (doc.isEmpty()) { + emit errorOccured(tr("Cannot parse reply from the server")); + return; + } + + QVariantList journeys = doc.value("journeys").toList(); + + clearJourney(); + + lastJourneyResultList = new JourneyResultList(this); + + QDateTime arrival; + QDateTime departure; + + QVariantList::const_iterator i; + int counter = 0; + + for (i = journeys.constBegin(); i != journeys.constEnd(); ++i) { + counter++; + QVariantMap journey = i->toMap(); + QString id = QString::number(counter); + parseJourneyOption(journey, id); + JourneyResultItem* item = new JourneyResultItem(lastJourneyResultList); + arrival = QDateTime::fromString(journey.value("arrivalDateTime").toString(), "yyyy-MM-ddTHH:mm:ss"); + departure = QDateTime::fromString(journey.value("startDateTime").toString(), "yyyy-MM-ddTHH:mm:ss"); + + if (i == journeys.constBegin()) + { + lastsearch.firstOption=departure; + } + + item->setArrivalTime(arrival.toString("HH:mm")); + item->setDepartureTime(departure.toString("HH:mm")); + + QVariantList legs = journey.value("legs").toList(); + QStringList trains; + + QVariantList::const_iterator j; + for (j = legs.constBegin(); j != legs.constEnd(); ++j) + { + const QVariantMap mode = j->toMap().value("mode").toMap(); + if (mode.value("type").toString() != TransportModes::Walking) { + const QString typeName = mode.value("name").toString(); + if (!typeName.isEmpty()) + trains.append(typeName); + } + } + + trains.removeDuplicates(); + + item->setTrainType(trains.join(", ").trimmed()); + + item->setTransfers(QString::number(legs.count() - 1)); + + int minutes = departure.secsTo(arrival)/60; + item->setDuration(QString("%1:%2").arg(minutes/60).arg(minutes%60,2,10,QChar('0'))); + + item->setId(id); + + lastJourneyResultList->appendItem(item); + + //Set result metadata based on first result + if (lastJourneyResultList->itemcount() == 1) { + lastJourneyResultList->setTimeInfo(arrival.date().toString()); + lastJourneyResultList->setDepartureStation(cachedResults[item->id()]->departureStation()); + lastJourneyResultList->setArrivalStation(cachedResults[item->id()]->arrivalStation()); + } + } + lastsearch.lastOption=departure; + emit journeyResult(lastJourneyResultList); +} + +void ParserLondonTfl::parseSearchLaterJourney(QNetworkReply *) +{ + +} + +void ParserLondonTfl::parseSearchEarlierJourney(QNetworkReply *) +{ + +} + +void ParserLondonTfl::parseJourneyDetails(QNetworkReply *) +{ + //should never happen +} + +void ParserLondonTfl::parseJourneyOption(const QVariantMap &object, const QString &id) +{ + JourneyDetailResultList* resultList = new JourneyDetailResultList(this); + + QVariantList legs = object.value("legs").toList(); + + QDateTime arrival = QDateTime::fromString(object.value("arrivalDateTime").toString(), "yyyy-MM-ddTHH:mm:ss"); + QDateTime departure = QDateTime::fromString(object.value("startDateTime").toString(), "yyyy-MM-ddTHH:mm:ss"); + resultList->setArrivalDateTime(arrival); + resultList->setDepartureDateTime(departure); + int minutes=departure.secsTo(arrival)/60; + int hours=minutes/60; + minutes=minutes%60; + resultList->setDuration(QString("%1:%2").arg(hours).arg(minutes, 2, 10, QChar('0'))); + + resultList->setId(id); + + for(int i = 0; i < legs.count(); i++) + { + QVariantMap leg = legs.at(i).toMap(); + JourneyDetailResultItem* resultItem = new JourneyDetailResultItem(resultList); + + QVariantMap firstStop = leg["departurePoint"].toMap(); + QVariantMap lastStop = leg["arrivalPoint"].toMap(); + + resultItem->setDepartureDateTime(QDateTime::fromString(leg.value("departureTime").toString(), "yyyy-MM-ddTHH:mm:ss")); + resultItem->setArrivalDateTime(QDateTime::fromString(leg.value("arrivalTime").toString(), "yyyy-MM-ddTHH:mm:ss")); + resultItem->setDepartureStation( shortStationName(firstStop.value("commonName").toString())); + resultItem->setArrivalStation( shortStationName( lastStop.value("commonName").toString())); + + QString mode = leg.value("mode").toMap().value("name").toString(); + + if (mode != TransportModes::Walking) { + + // stop letter / platform (departure) + if (! firstStop.value("stopLetter").toString().isEmpty()) { + resultItem->setDepartureInfo(QString("🚏 %1").arg(firstStop.value("stopLetter").toString())); + } + + else if (! firstStop.value("platformName").toString().isEmpty()) { + resultItem->setDepartureInfo(QString("Pl. %1").arg(firstStop.value("platformName").toString())); + } + + // stop letter / platform (arrival) + if (! lastStop.value("stopLetter").toString().isEmpty()) { + resultItem->setArrivalInfo(QString("🚏 %1").arg(lastStop.value("stopLetter").toString())); + } + + else if (! lastStop.value("platformName").toString().isEmpty()) { + resultItem->setArrivalInfo(QString("Pl. %1").arg(lastStop.value("platformName").toString())); + } + } + + // get the transport options (e.g. bus or tube lines) + QVariantList stations = leg.value("routeOptions").toList(); + QStringList routeOptionsTrains; + QStringList routeOptionsDirections; + QString detailedInfo; + + QVariantList::const_iterator it_routeOpt; + for (it_routeOpt = stations.constBegin(); it_routeOpt != stations.constEnd(); ++it_routeOpt) { + + QStringList routeOptionsDirectionsCurrentTrain; + QVariantMap routeOption = it_routeOpt->toMap(); + + QString currentRouteOption = routeOption.value("name").toString(); + + #ifdef BUILD_FOR_QT5 + currentRouteOption = currentRouteOption.toHtmlEscaped(); + #else + currentRouteOption = Qt::escape(currentRouteOption); + #endif + + routeOptionsTrains.push_back(currentRouteOption); + detailedInfo += currentRouteOption + " to "; + + QVariantList directions = routeOption["directions"].toList(); + + QVariantList::const_iterator it_directions; + + for (it_directions = directions.constBegin(); it_directions != directions.constEnd(); ++it_directions) + { + QString currentDestination = it_directions->toString().replace("Underground Station", ""); + + #ifdef BUILD_FOR_QT5 + currentDestination = currentDestination.toHtmlEscaped(); + #else + currentDestination = Qt::escape(currentDestination); + #endif + + if (! routeOptionsDirections.contains(currentDestination)) + { + routeOptionsDirections.push_back(currentDestination); + } + + routeOptionsDirectionsCurrentTrain.push_back(currentDestination); + } + + detailedInfo += routeOptionsDirectionsCurrentTrain.join(" or ") + ".
"; + } + + resultItem->setDirection(routeOptionsDirections.join(" or ")); + + // walking + if (mode == TransportModes::Walking) + { + const QString duration = leg.value("duration").toString(); + resultItem->setTrain(tr("Walk for %2 min").arg(duration)); + resultItem->setColor(getLineColor(mode)); + } + + // only one train --> put it in title + else if (routeOptionsTrains.count() == 1) + { + resultItem->setTrain(routeOptionsTrains[0]); + resultItem->setColor( getLineColor(mode, routeOptionsTrains[0]) ); + } + else + { + resultItem->setTrain(mode); + resultItem->setColor(getLineColor(mode)); + resultItem->setInfo(detailedInfo); + } + + //resultItem->setDirection(leg.value("destination").toString()); + + QStringList attributes; + const QVariantList attrs = leg.value("attributes").toList(); + foreach (const QVariant &attr, attrs) { + attributes << attr.toMap().value("title").toString(); + } + //resultItem->setInfo(attributes.join(tr(", "))); + + resultList->appendItem(resultItem); + } + + resultList->setDepartureStation(resultList->getItem(0)->departureStation()); + resultList->setArrivalStation(resultList->getItem(resultList->itemcount() - 1)->arrivalStation()); + + cachedResults.insert(id, resultList); +} + +QStringList ParserLondonTfl::getModesFromTrainRestrictions(int trainRestrictions) +{ + + QStringList availableModes; + + switch(trainRestrictions){ + case all: + availableModes.append(TransportModes::Bus); + availableModes.append(TransportModes::CableCar); + availableModes.append(TransportModes::Coach); + availableModes.append(TransportModes::Dlr); + availableModes.append(TransportModes::Rail); + availableModes.append(TransportModes::Overground); + availableModes.append(TransportModes::ReplacementBus); + availableModes.append(TransportModes::RiverBus); + availableModes.append(TransportModes::RiverTour); + availableModes.append(TransportModes::TflRail); + availableModes.append(TransportModes::Tram); + availableModes.append(TransportModes::Tube); + break; + case bus_tram_tube_overground_dlr: + availableModes.append(TransportModes::Bus); + availableModes.append(TransportModes::Tram); + availableModes.append(TransportModes::Tube); + availableModes.append(TransportModes::Overground); + availableModes.append(TransportModes::Dlr); + break; + case rail_tube_overground_dlr: + availableModes.append(TransportModes::Rail); + availableModes.append(TransportModes::Tube); + availableModes.append(TransportModes::Overground); + availableModes.append(TransportModes::Dlr); + break; + case tube_overground_dlr: + availableModes.append(TransportModes::Tube); + availableModes.append(TransportModes::Overground); + availableModes.append(TransportModes::Dlr); + break; + case bus_tram: + availableModes.append(TransportModes::Bus); + availableModes.append(TransportModes::Tram); + break; + case bus: + availableModes.append(TransportModes::Bus); + break; + case rail: + availableModes.append(TransportModes::Rail); + break; + case tube: + availableModes.append(TransportModes::Tube); + break; + case overground: + availableModes.append(TransportModes::Overground); + break; + case dlr: + availableModes.append(TransportModes::Dlr); + break; + } + + return availableModes; +} + +// check the mode +bool ParserLondonTfl::doesModeMatchTrainRestrictions(const QString & mode, int trainRestrictions) +{ + return getModesFromTrainRestrictions(trainRestrictions).contains(mode); +} + +// this is for reducing the network replies, ids not suitable for trainRestrictions are skipped +QStringList ParserLondonTfl::filterStopIdsByTrainRestrictions(const QStringList & stopIds, int trainRestrictions) +{ + QStringList output; + + QStringList::const_iterator j; + for (j = stopIds.constBegin(); j != stopIds.constEnd(); ++j) + { + QString currentId = *j; + + bool includeInFilteredOutput = true; + + bool isTubeId = (currentId.left(8) == "940GZZLU"); + bool isDlrId = (currentId.left(8) == "940GZZDL"); + bool isRailOrOverGroundId = (! isTubeId && ! isDlrId && (currentId.left(4) == "940G")); + bool isBusOrTramId = (!isTubeId && ! isDlrId && ! isRailOrOverGroundId); + + switch(trainRestrictions){ + case all: + case bus_tram_tube_overground_dlr: + break; + case rail_tube_overground_dlr: + case tube_overground_dlr: + includeInFilteredOutput = (! isBusOrTramId); + break; + case bus_tram: + case bus: + includeInFilteredOutput = isBusOrTramId; + break; + case rail: + includeInFilteredOutput = isRailOrOverGroundId; + break; + case tube: + includeInFilteredOutput = isTubeId; + break; + case overground: + includeInFilteredOutput = isRailOrOverGroundId; + break; + case dlr: + includeInFilteredOutput = isDlrId; + break; + } + + if (includeInFilteredOutput) + { + output.push_back(currentId); + } + } + + return output; +} + +void ParserLondonTfl::addTimeTableEntriesOfStopPoint(const QString & stopPointId, TimetableEntriesList & entriesList) +{ + QNetworkRequest request; + QUrl relativeUrl (QString("/Stoppoint/%1/Arrivals").arg(stopPointId)); + + request.setUrl(BaseUrl.resolved(relativeUrl)); + + QNetworkReply *networkReply = NetworkManagerTimeTableSubQuery->get(request); + + QEventLoop loop; + connect(networkReply, SIGNAL(finished()), &loop, SLOT(quit())); + loop.exec(); + + QByteArray allData = networkReply->readAll(); + // qDebug() << "REPLY:>>>>>>>>>>>>\n" << allData; + + QVariantMap doc = parseJson("{ \"output\":" + allData + "}"); + + if (doc.isEmpty()) { + emit errorOccured(tr("Cannot parse reply from the server")); + return; + } + + QVariantList arrivals = doc.value("output").toList(); + + QVariantList::const_iterator i; + + for (i = arrivals.constBegin(); i != arrivals.constEnd(); ++i) { + + QVariantMap arrival = i->toMap(); + QString modeName = arrival.value("modeName").toString(); + + // if the mode is not correct, we skip the value + if (! doesModeMatchTrainRestrictions(modeName, timetableRestrictions) ) + { + continue; + } + + TimetableEntry entry; + + entry.currentStation = shortStationName (arrival.value("stationName").toString()); + entry.destinationStation = shortStationName (arrival.value("destinationName").toString()); + entry.time = QDateTime::fromString(arrival.value("expectedArrival").toString(), "yyyy-MM-ddTHH:mm:ssZ").time(); + + QString platformName = arrival.value("platformName").toString(); + + if ((platformName != "null") && (platformName != "Platform Unknown") ) + { + if (platformName.startsWith("Platform ")) + platformName = platformName.mid(9); + + // for buses use the bus stop symbol + if (modeName == TransportModes::Bus) + { + platformName = "🚏" + platformName; + } + + entry.platform = platformName; + } + + entry.trainType = shortLineName(arrival.value("lineName").toString()); + entriesList.append(entry); + } +} diff --git a/src/parser/parser_london_tfl.h b/src/parser/parser_london_tfl.h new file mode 100644 index 00000000..9850b285 --- /dev/null +++ b/src/parser/parser_london_tfl.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** This file is a part of Fahrplan. +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU 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 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 PARSER_LONDONTFL_H +#define PARSER_LONDONTFL_H + +#include "parser_abstract.h" + +#include + +class QNetworkReply; +/** + * @brief The ParserNinetwo class + * Parser for the 9292ov.nl dutch public transport route planner backend. + * it uses the unofficial json backend + */ +class ParserLondonTfl : public ParserAbstract +{ + Q_OBJECT + struct { + QDateTime firstOption; + QDateTime lastOption; + Station from; + Station via; + Station to; + int restrictions; + Mode mode; + + } lastsearch; + + struct { + bool isValid; + qreal latitude; + qreal longitude; + } lastCoordinates; + + typedef enum restrictions{ + all = 0, + rail_tube_overground_dlr = 1, + tube_overground_dlr = 2, + bus_tram_tube_overground_dlr = 3, + bus_tram = 4, + bus = 5, + rail = 6, + tube = 7, + overground = 8, + dlr = 9 + } restrictions; + + int timetableRestrictions; + +public: + ParserLondonTfl(QObject* parent = 0); + virtual ~ParserLondonTfl(); + + // ParserAbstract interface +public: + static QString getName() { return QString("%1 (tfl.gov.uk)").arg(tr("London")); } + virtual QString name() { return getName(); } + virtual QString shortName() { return "Transport for London"; } + +public slots: + void getTimeTableForStation(const Station ¤tStation, const Station &directionStation, const QDateTime &dateTtime, ParserAbstract::Mode mode, int trainrestrictions); + void findStationsByName(const QString &stationName); + void findStationsByCoordinates(qreal longitude, qreal latitude); + void searchJourney(const Station &departureStation,const Station &viaStation,const Station &arrivalStation,const QDateTime &dateTime,const ParserAbstract::Mode mode, int trainrestrictions); + void searchJourneyLater(); + void searchJourneyEarlier(); + void getJourneyDetails(const QString &id); + bool supportsGps() { return true; } + bool supportsVia() { return true; } + bool supportsTimeTable() { return true; } + bool supportsTimeTableDirection() { return false; } + QStringList getTrainRestrictions(); + virtual void clearJourney(); + +protected: + void parseTimeTable(QNetworkReply *networkReply); + void parseStationsByName(QNetworkReply *networkReply); + void parseStationsByCoordinates(QNetworkReply *networkReply); + void parseSearchJourney(QNetworkReply *networkReply); + void parseSearchLaterJourney(QNetworkReply *networkReply); + void parseSearchEarlierJourney(QNetworkReply *networkReply); + void parseJourneyDetails(QNetworkReply *networkReply); + QMap cachedResults; + + JourneyResultList *lastJourneyResultList; + +private: + void parseJourneyOption(const QVariantMap &object, const QString & id); + + QStringList getModesFromTrainRestrictions(int trainRestrictions); + bool doesModeMatchTrainRestrictions(const QString & mode, int trainRestrictions); + QStringList filterStopIdsByTrainRestrictions(const QStringList & stopIds, int trainRestrictions); + void addTimeTableEntriesOfStopPoint(const QString & stopPointId, TimetableEntriesList & entriesList); + + QNetworkAccessManager *NetworkManagerTimeTableSubQuery; +}; + +#endif // PARSER_LONDONTFL_H diff --git a/src/parser/parser_ninetwo.cpp b/src/parser/parser_ninetwo.cpp index 1dab8be3..e7475657 100644 --- a/src/parser/parser_ninetwo.cpp +++ b/src/parser/parser_ninetwo.cpp @@ -49,11 +49,30 @@ inline int distance(qreal lat1, qreal lon1, qreal lat2, qreal lon2) return qRound(6371.0 * c * 1000); } -ParserNinetwo::ParserNinetwo(QObject *parent):ParserAbstract(parent) +ParserNinetwo::ParserNinetwo(QObject *parent):ParserAbstract(parent), lastJourneyResultList(NULL) { lastCoordinates.isValid = false; } +ParserNinetwo::~ParserNinetwo() +{ + clearJourney(); +} + +void ParserNinetwo::clearJourney() +{ + if (lastJourneyResultList) { + delete lastJourneyResultList; + lastJourneyResultList = NULL; + } + + for (QMap::Iterator it = cachedResults.begin(); it != cachedResults.end();) { + JourneyDetailResultList *jdrl = it.value(); + it = cachedResults.erase(it); + delete jdrl; + } +} + void ParserNinetwo::getTimeTableForStation(const Station ¤tStation, const Station &, const QDateTime &, @@ -166,6 +185,8 @@ void ParserNinetwo::searchJourney(const Station &departureStation, currentRequestState=FahrplanNS::searchJourneyRequest; + clearJourney(); + } void ParserNinetwo::searchJourneyLater() @@ -352,9 +373,23 @@ void ParserNinetwo::parseSearchJourney(QNetworkReply *networkReply) return; } + if (doc.contains("error")) { + QString error = doc.value("error").toString(); + QString message; + if (error == "NoJourneys") { + message = tr("No connections have been found that correspond to your request."); + } else { + message = tr("Unknown error ocurred with the backend (error %1).").arg(error); + } + emit errorOccured(message); + return; + } + QVariantList journeys = doc.value("journeys").toList(); - JourneyResultList* result=new JourneyResultList; + clearJourney(); + + lastJourneyResultList = new JourneyResultList(this); QDateTime arrival; QDateTime departure; @@ -363,13 +398,15 @@ void ParserNinetwo::parseSearchJourney(QNetworkReply *networkReply) for (i = journeys.constBegin(); i != journeys.constEnd(); ++i) { QVariantMap journey = i->toMap(); parseJourneyOption(journey); - JourneyResultItem* item = new JourneyResultItem; + JourneyResultItem* item = new JourneyResultItem(lastJourneyResultList); arrival = QDateTime::fromString(journey.value("arrival").toString(), "yyyy-MM-ddTHH:mm"); departure = QDateTime::fromString(journey.value("departure").toString(), "yyyy-MM-ddTHH:mm"); - if (i == journeys.constBegin()) - lastsearch.firstOption=departure; + if (i == journeys.constBegin()) { + lastsearch.firstOption = departure; + } + item->setDate(departure.date()); item->setArrivalTime(arrival.toString("HH:mm")); item->setDepartureTime(departure.toString("HH:mm")); @@ -394,17 +431,17 @@ void ParserNinetwo::parseSearchJourney(QNetworkReply *networkReply) int minutes = departure.secsTo(arrival)/60; item->setDuration(QString("%1:%2").arg(minutes/60).arg(minutes%60,2,10,QChar('0'))); item->setId(journey.value("id").toString()); - result->appendItem(item); + lastJourneyResultList ->appendItem(item); //Set result metadata based on first result - if (result->itemcount() == 1) { - result->setTimeInfo(arrival.date().toString()); - result->setDepartureStation(cachedResults[item->id()]->departureStation()); - result->setArrivalStation(cachedResults[item->id()]->arrivalStation()); + if (lastJourneyResultList ->itemcount() == 1) { + lastJourneyResultList ->setTimeInfo(arrival.date().toString()); + lastJourneyResultList ->setDepartureStation(cachedResults[item->id()]->departureStation()); + lastJourneyResultList ->setArrivalStation(cachedResults[item->id()]->arrivalStation()); } } lastsearch.lastOption=departure; - emit journeyResult(result); + emit journeyResult(lastJourneyResultList ); } void ParserNinetwo::parseSearchLaterJourney(QNetworkReply *) @@ -424,7 +461,7 @@ void ParserNinetwo::parseJourneyDetails(QNetworkReply *) void ParserNinetwo::parseJourneyOption(const QVariantMap &object) { - JourneyDetailResultList* result = new JourneyDetailResultList; + JourneyDetailResultList* result = new JourneyDetailResultList(this); QString id = object.value("id").toString(); QVariantList legs = object.value("legs").toList(); @@ -442,7 +479,7 @@ void ParserNinetwo::parseJourneyOption(const QVariantMap &object) for(int i = 0; i < legs.count(); i++) { QVariantMap leg = legs.at(i).toMap(); - JourneyDetailResultItem* resultItem = new JourneyDetailResultItem; + JourneyDetailResultItem* resultItem = new JourneyDetailResultItem(result); QVariantList stops = leg.value("stops").toList(); diff --git a/src/parser/parser_ninetwo.h b/src/parser/parser_ninetwo.h index ed379065..7aa0b9cc 100644 --- a/src/parser/parser_ninetwo.h +++ b/src/parser/parser_ninetwo.h @@ -60,6 +60,7 @@ class ParserNinetwo : public ParserAbstract public: ParserNinetwo(QObject* parent = 0); + virtual ~ParserNinetwo(); // ParserAbstract interface public: @@ -80,6 +81,7 @@ public slots: bool supportsTimeTable() { return true; } bool supportsTimeTableDirection() { return false; } QStringList getTrainRestrictions(); + virtual void clearJourney(); protected: void parseTimeTable(QNetworkReply *networkReply); @@ -91,6 +93,8 @@ public slots: void parseJourneyDetails(QNetworkReply *networkReply); QMap cachedResults; + JourneyResultList *lastJourneyResultList; + private: void parseJourneyOption(const QVariantMap &object); }; diff --git a/src/parser/parser_resrobot.cpp b/src/parser/parser_resrobot.cpp index af323ec5..5f255beb 100644 --- a/src/parser/parser_resrobot.cpp +++ b/src/parser/parser_resrobot.cpp @@ -24,7 +24,7 @@ ParserResRobot::ParserResRobot(QObject *parent) : ParserAbstract(parent), - timetableAPIKey(QLatin1String("75d0c2b5-c179-489c-90c2-eb6d2bc8970c")), + lastJourneyResultList(NULL), journeyAPIKey(QLatin1String("c8436ea6-3c7e-489f-93b1-5b636fc55f2e")), baseURL(QLatin1String("https://api.resrobot.se/v2")) { @@ -138,6 +138,25 @@ ParserResRobot::ParserResRobot(QObject *parent) : generalTransportModes.insert("U", tr("Rapid transit")); } +ParserResRobot::~ParserResRobot() +{ + clearJourney(); +} + +void ParserResRobot::clearJourney() +{ + for (QHash::Iterator it = cachedResults.begin(); it != cachedResults.end();) { + JourneyDetailResultList *jdrl = it.value(); + it = cachedResults.erase(it); + delete jdrl; + } + + if (lastJourneyResultList) { + delete lastJourneyResultList; + lastJourneyResultList = NULL; + } +} + bool ParserResRobot::supportsGps() { return true; @@ -170,10 +189,14 @@ QStringList ParserResRobot::getTrainRestrictions() void ParserResRobot::findStationsByName(const QString &stationName) { lastStationSearch = stationName; - if (stationName.length() < 2) + if (stationName.length() < 2) { return; - if (currentRequestState != FahrplanNS::noneRequest) + } + + if (currentRequestState != FahrplanNS::noneRequest) { return; + } + currentRequestState = FahrplanNS::stationsByNameRequest; QUrl url(baseURL + QLatin1String("/location.name")); @@ -202,8 +225,10 @@ void ParserResRobot::findStationsByName(const QString &stationName) void ParserResRobot::findStationsByCoordinates(qreal longitude, qreal latitude) { - if (currentRequestState != FahrplanNS::noneRequest) + if (currentRequestState != FahrplanNS::noneRequest) { return; + } + currentRequestState = FahrplanNS::stationsByCoordinatesRequest; QUrl url(baseURL + QLatin1String("/location.nearbystops")); @@ -238,17 +263,21 @@ void ParserResRobot::getTimeTableForStation(const Station ¤tStation, ParserAbstract::Mode mode, int trainrestrictions) { - if (currentRequestState != FahrplanNS::noneRequest) + if (currentRequestState != FahrplanNS::noneRequest) { return; + } + currentRequestState = FahrplanNS::getTimeTableForStationRequest; timetableSearchMode = mode; QUrl url; - if (mode == Arrival) + if (mode == Arrival) { url.setUrl(baseURL + QLatin1String("/arrivalBoard")); - else + } else { url.setUrl(baseURL + QLatin1String("/departureBoard")); + } + #if defined(BUILD_FOR_QT5) QUrlQuery query; #else @@ -286,9 +315,14 @@ void ParserResRobot::searchJourney(const Station &departureStation, const Statio const Station &arrivalStation, const QDateTime &dateTime, ParserAbstract::Mode mode, int trainRestrictions) { - if (currentRequestState != FahrplanNS::noneRequest) + if (currentRequestState != FahrplanNS::noneRequest) { return; + } + currentRequestState = FahrplanNS::searchJourneyRequest; + + clearJourney(); + lastJourneySearch.dateTime = dateTime; lastJourneySearch.from = departureStation; lastJourneySearch.via = viaStation; @@ -315,6 +349,7 @@ void ParserResRobot::searchJourney(const Station &departureStation, const Statio void ParserResRobot::searchJourneyLater() { currentRequestState = FahrplanNS::searchJourneyLaterRequest; + clearJourney(); #if defined(BUILD_FOR_QT5) QUrlQuery query; #else @@ -327,6 +362,7 @@ void ParserResRobot::searchJourneyLater() void ParserResRobot::searchJourneyEarlier() { currentRequestState = FahrplanNS::searchJourneyEarlierRequest; + clearJourney(); #if defined(BUILD_FOR_QT5) QUrlQuery query; #else @@ -334,6 +370,7 @@ void ParserResRobot::searchJourneyEarlier() #endif query.addQueryItem("context", searchEarlierReference); doSearchJourney(query); + } #if defined(BUILD_FOR_QT5) @@ -500,9 +537,7 @@ void ParserResRobot::parseSearchJourney(QNetworkReply *networkReply) searchLaterReference = doc.value("scrF").toString(); QVariantList journeyListData = doc.value("Trip").toList(); - cachedResults.clear(); - - JourneyResultList *journeyList = new JourneyResultList(); + lastJourneyResultList = new JourneyResultList(this); int journeyCounter = 0; foreach (QVariant journeyData, journeyListData) { @@ -516,19 +551,22 @@ void ParserResRobot::parseSearchJourney(QNetworkReply *networkReply) minutes = minutes % 60; QString duration = QString("%1:%2").arg(hours).arg(minutes, 2, 10, QChar('0')); - // Compile list of transport modes used - QStringList transportModes; + JourneyDetailResultList* journeyDetails = new JourneyDetailResultList(this); + + QStringList transportModes; // Compile list of transport modes used foreach (JourneyDetailResultItem* segment, segments) { - if (segment->internalData1() != "WALK") + if (segment->internalData1() != "WALK") { transportModes.append(segment->train()); + } + journeyDetails->appendItem(segment); } + // When the distance is short, an option with only "walk" can be present if (transportModes.count() == 0 && segments.count() == 1) - transportModes.append(segments.first()->train()); + { + transportModes.append(segments.first()->train()); + } - JourneyDetailResultList* journeyDetails = new JourneyDetailResultList; - foreach (JourneyDetailResultItem* segment, segments) - journeyDetails->appendItem(segment); journeyDetails->setId(journeyID); journeyDetails->setDepartureStation(segments.first()->departureStation()); journeyDetails->setDepartureDateTime(segments.first()->departureDateTime()); @@ -540,10 +578,12 @@ void ParserResRobot::parseSearchJourney(QNetworkReply *networkReply) // Indicate in the departure/arrival times if they are another day (e.g. "14:37+1") int depDayDiff = lastJourneySearch.dateTime.date().daysTo(journeyDetails->departureDateTime().date()); QString depTime = journeyDetails->departureDateTime().toString("HH:mm"); - if (depDayDiff > 0) + if (depDayDiff > 0) { depTime += "+" + QString::number(depDayDiff); - else if (depDayDiff < 0) + } else if (depDayDiff < 0) { depTime += QString::number(depDayDiff); + } + int arrDayDiff = lastJourneySearch.dateTime.date().daysTo(journeyDetails->arrivalDateTime().date()); QString arrTime = journeyDetails->arrivalDateTime().toString("HH:mm"); if (arrDayDiff > 0) @@ -551,7 +591,7 @@ void ParserResRobot::parseSearchJourney(QNetworkReply *networkReply) else if (arrDayDiff < 0) arrTime += QString::number(arrDayDiff); - JourneyResultItem* journey = new JourneyResultItem; + JourneyResultItem* journey = new JourneyResultItem(lastJourneyResultList); journey->setId(journeyID); journey->setDate(segments.first()->departureDateTime().date()); journey->setDepartureTime(depTime); @@ -559,31 +599,37 @@ void ParserResRobot::parseSearchJourney(QNetworkReply *networkReply) journey->setTrainType(transportModes.join(", ")); journey->setDuration(duration); journey->setTransfers(QString::number(transportModes.count()-1)); - journeyList->appendItem(journey); + lastJourneyResultList->appendItem(journey); if (journeyCounter == 0) { - if (lastJourneySearch.mode == Departure) + if (lastJourneySearch.mode == Departure) { lastJourneySearch.firstOption = journeyDetails->departureDateTime(); - else + } else { lastJourneySearch.firstOption = journeyDetails->arrivalDateTime(); + } } - if (lastJourneySearch.mode == Departure) + + if (lastJourneySearch.mode == Departure) { lastJourneySearch.lastOption = journeyDetails->departureDateTime(); - else + } else { lastJourneySearch.lastOption = journeyDetails->arrivalDateTime(); + } ++journeyCounter; } - journeyList->setDepartureStation(lastJourneySearch.from.name); - journeyList->setArrivalStation(lastJourneySearch.to.name); + lastJourneyResultList->setDepartureStation(lastJourneySearch.from.name); + lastJourneyResultList->setArrivalStation(lastJourneySearch.to.name); + QString modeString; - if (lastJourneySearch.mode == Arrival) + if (lastJourneySearch.mode == Arrival) { modeString = tr("Arrivals"); - else + } else { modeString = tr("Departures"); - journeyList->setTimeInfo(modeString + " " + lastJourneySearch.dateTime.toString(tr("ddd MMM d, HH:mm"))); + } + + lastJourneyResultList->setTimeInfo(modeString + " " + lastJourneySearch.dateTime.toString(tr("ddd MMM d, HH:mm"))); - emit journeyResult(journeyList); + emit journeyResult(lastJourneyResultList); } // Parse info about one journey option. Store detailed info about segments for later use. @@ -592,10 +638,9 @@ QList ParserResRobot::parseJourneySegments(const QVari QList results; QVariantList segments = journeyData.value("LegList").toMap().value("Leg").toList(); - foreach (QVariant segmentData, segments) - { + foreach (QVariant segmentData, segments) { const QVariantMap& segment = segmentData.toMap(); - JourneyDetailResultItem* resultItem = new JourneyDetailResultItem; + JourneyDetailResultItem* resultItem = new JourneyDetailResultItem(this); // Departure QVariantMap departure = segment.value("Origin").toMap(); @@ -647,25 +692,31 @@ QList ParserResRobot::parseJourneySegments(const QVari if (!operatorName.isEmpty()) { if (operatorURL.isEmpty()) operatorInfo = operatorName; - else + } else { operatorInfo = "" + operatorName + ""; - } + } resultItem->setTrain(transportType); } else { while (!results.isEmpty()) delete results.takeFirst(); delete resultItem; - break; } - if (!distance.isEmpty()) + if (distance.isEmpty()) { + QStringList infoList; + + if (!operatorInfo.isEmpty()) { + infoList << operatorInfo; + } + + if (!info.isEmpty()) { + infoList << info.join(", "); + } + + resultItem->setInfo(infoList.join("
")); + } else { resultItem->setInfo(distance + " m"); - else if (!operatorInfo.isEmpty() && !info.isEmpty()) - resultItem->setInfo(operatorInfo + "
" + info.join(", ")); - else if (!operatorInfo.isEmpty()) - resultItem->setInfo(operatorInfo); - else if (!info.isEmpty()) - resultItem->setInfo(info.join(", ")); + } resultItem->setDirection(segment.value("direction").toString()); results.append(resultItem); @@ -675,8 +726,9 @@ QList ParserResRobot::parseJourneySegments(const QVari void ParserResRobot::getJourneyDetails(const QString &id) { - if (cachedResults.contains(id)) + if (cachedResults.contains(id)) { emit journeyDetailsResult(cachedResults.value(id)); + } } void ParserResRobot::parseSearchLaterJourney(QNetworkReply *networkReply) @@ -699,9 +751,13 @@ void ParserResRobot::parseJourneyDetails(QNetworkReply *networkReply) QString ParserResRobot::hafasAttribute(const QString& code) { if (hafasAttributes.contains(code)) + { return hafasAttributes[code]; + } else + { return ""; + } } QString ParserResRobot::transportMode(const QString& code, const QString& fallback) diff --git a/src/parser/parser_resrobot.h b/src/parser/parser_resrobot.h index 67054c93..4527eb57 100644 --- a/src/parser/parser_resrobot.h +++ b/src/parser/parser_resrobot.h @@ -48,6 +48,7 @@ class ParserResRobot : public ParserAbstract Q_OBJECT public: explicit ParserResRobot(QObject *parent = 0); + virtual ~ParserResRobot(); static QString getName() { return QString("%1 (resrobot.se)").arg(tr("Sweden")); } virtual QString name() { return getName(); } @@ -72,6 +73,7 @@ public slots: virtual void searchJourneyLater(); virtual void searchJourneyEarlier(); virtual void getJourneyDetails(const QString &id); + virtual void clearJourney(); protected: virtual void parseTimeTable(QNetworkReply *networkReply); @@ -82,6 +84,8 @@ public slots: virtual void parseSearchEarlierJourney(QNetworkReply *networkReply); virtual void parseJourneyDetails(QNetworkReply *networkReply); + JourneyResultList *lastJourneyResultList; + private: enum TransportModePreset { ALL_TRANSPORT_MODES, @@ -120,7 +124,7 @@ public slots: const QString baseURL; const QString timetableAPIVersion; const QString journeyAPIVersion; - QMap cachedResults; + QHash cachedResults; QHash hafasAttributes; QHash specificTransportModes; QHash generalTransportModes; diff --git a/src/parser/parser_xmlrmvde.cpp b/src/parser/parser_xmlrmvde.cpp new file mode 100644 index 00000000..76cadd54 --- /dev/null +++ b/src/parser/parser_xmlrmvde.cpp @@ -0,0 +1,31 @@ +#include "parser_xmlrmvde.h" + +#include + +ParserXmlRMVde::ParserXmlRMVde(QObject *parent) : + ParserHafasXml(parent) +{ + baseXmlUrl = QLatin1String("http://www.rmv.de/auskunft/bin/jp/query.exe"); + baseUrl = QLatin1String("http://www.rmv.de/auskunft/bin/jp/query.exe"); +} + +void ParserXmlRMVde::parseStationsByName(QNetworkReply *networkReply) +{ + const QString data = QString::fromUtf8(networkReply->readAll()); + const StationsList result = internalParseStationsByName(data); + emit stationsResult(result); +} + +QString ParserXmlRMVde::getTrainRestrictionsCodes(int trainrestrictions) +{ + Q_UNUSED(trainrestrictions) + // TODO should something else get returned? + return "1111111111111111"; +} + +QStringList ParserXmlRMVde::getTrainRestrictions() +{ + QStringList result; + result.append(tr("All")); + return result; +} diff --git a/src/parser/parser_xmlrmvde.h b/src/parser/parser_xmlrmvde.h new file mode 100644 index 00000000..2641068a --- /dev/null +++ b/src/parser/parser_xmlrmvde.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** This file is a part of Fahrplan. +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU 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 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 PARSER_XMLRMVDE_H +#define PARSER_XMLRMVDE_H + +#include "parser_hafasxml.h" + +class ParserXmlRMVde : public ParserHafasXml +{ + Q_OBJECT + +public: + explicit ParserXmlRMVde(QObject *parent = 0); + static QString getName() { return QString(QLatin1String("%1 (rmv.de)")).arg(tr("Germany, Hesse")); } + virtual QString name() { return getName(); } + virtual QString shortName() { return QLatin1String("rmv.de"); } + bool supportsTimeTable() { return false; } + +protected: + void parseStationsByName(QNetworkReply *networkReply); + QStringList getTrainRestrictions(); + QString getTrainRestrictionsCodes(int trainrestrictions); +}; + +#endif // PARSER_XMLRMVDE_H diff --git a/src/parser/parser_xmlvasttrafikse.cpp b/src/parser/parser_xmlvasttrafikse.cpp index a98f7fca..06ca6e1f 100644 --- a/src/parser/parser_xmlvasttrafikse.cpp +++ b/src/parser/parser_xmlvasttrafikse.cpp @@ -45,7 +45,7 @@ const QString ParserXmlVasttrafikSe::baseRestUrl = QLatin1String("https://api.va const char *ParserXmlVasttrafikSe::consumerCredentials = "ZHhONXJGdUpOZ1NfQjltc29zZlRuYTBwelpjYTpPMDVsNEhTNGdieU5KdGY4a29MRmdCZ1g0WUFh"; ParserXmlVasttrafikSe::ParserXmlVasttrafikSe(QObject *parent) - : ParserAbstract(parent) + : ParserAbstract(parent), lastJourneyResultList(NULL) { m_nam = new QNetworkAccessManager(this); m_searchJourneyParameters.isValid = false; @@ -61,8 +61,24 @@ ParserXmlVasttrafikSe::ParserXmlVasttrafikSe(QObject *parent) m_deviceId = QString(QLatin1String("device_%1")).arg(h, 24, 10, QLatin1Char('0')); } -ParserXmlVasttrafikSe::~ParserXmlVasttrafikSe() { +ParserXmlVasttrafikSe::~ParserXmlVasttrafikSe() +{ delete m_nam; + clearJourney(); +} + +void ParserXmlVasttrafikSe::clearJourney() +{ + if (lastJourneyResultList) { + delete lastJourneyResultList; + lastJourneyResultList = NULL; + } + + for (QHash::Iterator it = cachedJourneyDetails.begin(); it != cachedJourneyDetails.end();) { + JourneyDetailResultList *jdrl = it.value(); + it = cachedJourneyDetails.erase(it); + delete jdrl; + } } void ParserXmlVasttrafikSe::getTimeTableForStation(const Station ¤tStation, const Station &directionStation, const QDateTime &dateTime, Mode mode, int trainrestrictions) @@ -190,6 +206,8 @@ void ParserXmlVasttrafikSe::searchJourney(const Station &departureStation, const return; currentRequestState = FahrplanNS::searchJourneyRequest; + clearJourney(); + m_searchJourneyParameters.departureStation = departureStation; m_searchJourneyParameters.arrivalStation = arrivalStation; m_searchJourneyParameters.viaStation = viaStation; @@ -358,20 +376,14 @@ void ParserXmlVasttrafikSe::parseTimeTable(QNetworkReply *networkReply) void ParserXmlVasttrafikSe::parseSearchJourney(QNetworkReply *networkReply) { - JourneyResultList *journeyResultList = new JourneyResultList(); - - for (QHash::Iterator it = cachedJourneyDetails.begin(); it != cachedJourneyDetails.end();) { - JourneyDetailResultList *jdrl = it.value(); - it = cachedJourneyDetails.erase(it); - delete jdrl; - } + lastJourneyResultList = new JourneyResultList(this); /// Use fallback values for empty results (i.e. no connections found) - journeyResultList->setDepartureStation(m_searchJourneyParameters.departureStation.name); - journeyResultList->setViaStation(m_searchJourneyParameters.viaStation.name); - journeyResultList->setArrivalStation(m_searchJourneyParameters.arrivalStation.name); + lastJourneyResultList->setDepartureStation(m_searchJourneyParameters.departureStation.name); + lastJourneyResultList->setViaStation(m_searchJourneyParameters.viaStation.name); + lastJourneyResultList->setArrivalStation(m_searchJourneyParameters.arrivalStation.name); //: DATE, TIME - journeyResultList->setTimeInfo(tr("%1, %2", "DATE, TIME").arg(m_searchJourneyParameters.dateTime.date().toString(Qt::DefaultLocaleShortDate)).arg(m_searchJourneyParameters.dateTime.time().toString(Qt::DefaultLocaleShortDate))); + lastJourneyResultList->setTimeInfo(tr("%1, %2", "DATE, TIME").arg(m_searchJourneyParameters.dateTime.date().toString(Qt::DefaultLocaleShortDate)).arg(m_searchJourneyParameters.dateTime.time().toString(Qt::DefaultLocaleShortDate))); m_earliestArrival = m_latestResultDeparture = QDateTime(); @@ -382,8 +394,8 @@ void ParserXmlVasttrafikSe::parseSearchJourney(QNetworkReply *networkReply) if (doc.setContent(xmlRawtext, false)) { QDomNodeList tripNodeList = doc.elementsByTagName("Trip"); for (int i = 0; i < tripNodeList.length(); ++i) { - JourneyResultItem *jritem = new JourneyResultItem(); - JourneyDetailResultList *detailsList = new JourneyDetailResultList(); + JourneyResultItem *jritem = new JourneyResultItem(lastJourneyResultList); + JourneyDetailResultList *detailsList = new JourneyDetailResultList(this); /// Set default values for journey's start and end time QDateTime journeyStart = QDateTime::currentDateTime(); @@ -404,15 +416,15 @@ void ParserXmlVasttrafikSe::parseSearchJourney(QNetworkReply *networkReply) journeyStart.setTime(time); if (i == 0) { const QDate date = QDate::fromString(getAttribute(originNode, "date"), QLatin1String("yyyy-MM-dd")); - journeyResultList->setDepartureStation(getAttribute(originNode, "name")); + lastJourneyResultList->setDepartureStation(getAttribute(originNode, "name")); //: DATE, TIME - journeyResultList->setTimeInfo(tr("%1, %2", "DATE, TIME").arg(date.toString(Qt::DefaultLocaleShortDate)).arg(time.toString(Qt::DefaultLocaleShortDate))); + lastJourneyResultList->setTimeInfo(tr("%1, %2", "DATE, TIME").arg(date.toString(Qt::DefaultLocaleShortDate)).arg(time.toString("HH:mm"))); } } if (j == legNodeList.length() - 1) { journeyEnd.setTime(QTime::fromString(getAttribute(destinationNode, "time"), "hh:mm")); if (i == 0) - journeyResultList->setArrivalStation(getAttribute(destinationNode, "name")); + lastJourneyResultList->setArrivalStation(getAttribute(destinationNode, "name")); } if (getAttribute(legNode, "type") != QLatin1String("WALK") || getAttribute(originNode, "name") != getAttribute(destinationNode, "name")) { @@ -420,7 +432,7 @@ void ParserXmlVasttrafikSe::parseSearchJourney(QNetworkReply *networkReply) trainTypes.append(i18nConnectionType(getAttribute(legNode, "name"))); } - JourneyDetailResultItem *jdrItem = new JourneyDetailResultItem(); + JourneyDetailResultItem *jdrItem = new JourneyDetailResultItem(detailsList); jdrItem->setDepartureStation(getAttribute(originNode, "name")); const QString depTrack = getAttribute(originNode, "track"); jdrItem->setDepartureInfo(depTrack.isEmpty() ? QChar(0x2014) : tr("Track %1").arg(depTrack)); @@ -480,7 +492,7 @@ void ParserXmlVasttrafikSe::parseSearchJourney(QNetworkReply *networkReply) journeyEnd = journeyEnd.addDays(1); jritem->setDate(journeyStart.date()); - const QString timeFormat = QLocale().timeFormat(QLocale::ShortFormat); + const QString timeFormat = "HH:mm"; jritem->setDepartureTime(journeyStart.time().toString(timeFormat)); jritem->setArrivalTime(journeyEnd.time().toString(timeFormat)); int diffTime = journeyStart.secsTo(journeyEnd); @@ -494,14 +506,14 @@ void ParserXmlVasttrafikSe::parseSearchJourney(QNetworkReply *networkReply) else if (tripRtStatus == TRIP_RTDATA_ONTIME) jritem->setMiscInfo(tr("on time")); - journeyResultList->appendItem(jritem); + lastJourneyResultList->appendItem(jritem); const QString id = QString::number(i); jritem->setId(id); detailsList->setId(id); - detailsList->setDepartureStation(journeyResultList->departureStation()); - detailsList->setViaStation(journeyResultList->viaStation()); - detailsList->setArrivalStation(journeyResultList->arrivalStation()); + detailsList->setDepartureStation(lastJourneyResultList->departureStation()); + detailsList->setViaStation(lastJourneyResultList->viaStation()); + detailsList->setArrivalStation(lastJourneyResultList->arrivalStation()); detailsList->setDuration(jritem->duration()); detailsList->setArrivalDateTime(journeyEnd); detailsList->setDepartureDateTime(journeyStart); @@ -514,7 +526,7 @@ void ParserXmlVasttrafikSe::parseSearchJourney(QNetworkReply *networkReply) } } - emit journeyResult(journeyResultList); + emit journeyResult(lastJourneyResultList); } void ParserXmlVasttrafikSe::searchJourneyLater() @@ -522,13 +534,14 @@ void ParserXmlVasttrafikSe::searchJourneyLater() if (m_latestResultDeparture.isValid()) searchJourney(m_searchJourneyParameters.departureStation, m_searchJourneyParameters.arrivalStation, m_searchJourneyParameters.viaStation, m_latestResultDeparture, Departure, 0); else { - JourneyResultList *journeyResultList = new JourneyResultList(); - journeyResultList->setDepartureStation(m_searchJourneyParameters.departureStation.name); - journeyResultList->setViaStation(m_searchJourneyParameters.viaStation.name); - journeyResultList->setArrivalStation(m_searchJourneyParameters.arrivalStation.name); + clearJourney(); + lastJourneyResultList = new JourneyResultList(this); + lastJourneyResultList->setDepartureStation(m_searchJourneyParameters.departureStation.name); + lastJourneyResultList->setViaStation(m_searchJourneyParameters.viaStation.name); + lastJourneyResultList->setArrivalStation(m_searchJourneyParameters.arrivalStation.name); //: DATE, TIME - journeyResultList->setTimeInfo(tr("%1, %2", "DATE, TIME").arg(m_searchJourneyParameters.dateTime.date().toString(Qt::DefaultLocaleShortDate)).arg(m_searchJourneyParameters.dateTime.time().toString(Qt::DefaultLocaleShortDate))); - emit journeyResult(journeyResultList); + lastJourneyResultList->setTimeInfo(tr("%1, %2", "DATE, TIME").arg(m_searchJourneyParameters.dateTime.date().toString(Qt::DefaultLocaleShortDate)).arg(m_searchJourneyParameters.dateTime.time().toString(Qt::DefaultLocaleShortDate))); + emit journeyResult(lastJourneyResultList); } } @@ -537,13 +550,14 @@ void ParserXmlVasttrafikSe::searchJourneyEarlier() if (m_earliestArrival.isValid()) searchJourney(m_searchJourneyParameters.departureStation, m_searchJourneyParameters.arrivalStation, m_searchJourneyParameters.viaStation, m_earliestArrival, Arrival, 0); else { - JourneyResultList *journeyResultList = new JourneyResultList(); - journeyResultList->setDepartureStation(m_searchJourneyParameters.departureStation.name); - journeyResultList->setViaStation(m_searchJourneyParameters.viaStation.name); - journeyResultList->setArrivalStation(m_searchJourneyParameters.arrivalStation.name); + clearJourney(); + lastJourneyResultList = new JourneyResultList(this); + lastJourneyResultList->setDepartureStation(m_searchJourneyParameters.departureStation.name); + lastJourneyResultList->setViaStation(m_searchJourneyParameters.viaStation.name); + lastJourneyResultList->setArrivalStation(m_searchJourneyParameters.arrivalStation.name); //: DATE, TIME - journeyResultList->setTimeInfo(tr("%1, %2", "DATE, TIME").arg(m_searchJourneyParameters.dateTime.date().toString(Qt::DefaultLocaleShortDate)).arg(m_searchJourneyParameters.dateTime.time().toString(Qt::DefaultLocaleShortDate))); - emit journeyResult(journeyResultList); + lastJourneyResultList->setTimeInfo(tr("%1, %2", "DATE, TIME").arg(m_searchJourneyParameters.dateTime.date().toString(Qt::DefaultLocaleShortDate)).arg(m_searchJourneyParameters.dateTime.time().toString(Qt::DefaultLocaleShortDate))); + emit journeyResult(lastJourneyResultList); } } diff --git a/src/parser/parser_xmlvasttrafikse.h b/src/parser/parser_xmlvasttrafikse.h index cc82ce2a..013fdc37 100644 --- a/src/parser/parser_xmlvasttrafikse.h +++ b/src/parser/parser_xmlvasttrafikse.h @@ -28,7 +28,7 @@ class ParserXmlVasttrafikSe : public ParserAbstract public: explicit ParserXmlVasttrafikSe(QObject *parent = 0); - ~ParserXmlVasttrafikSe(); + virtual ~ParserXmlVasttrafikSe(); static QString getName() { return QString("%1 (vasttrafik.se)").arg(tr("Sweden")); } virtual QString name() { return getName(); } @@ -48,6 +48,7 @@ public slots: virtual bool supportsTimeTableDirection(); // virtual QStringList getTrainRestrictions(); // void cancelRequest(); + virtual void clearJourney(); protected: virtual void parseStationsByName(QNetworkReply *networkReply); @@ -55,6 +56,8 @@ public slots: virtual void parseTimeTable(QNetworkReply *networkReply); virtual void parseSearchJourney(QNetworkReply *networkReply); + JourneyResultList *lastJourneyResultList; + void sendHttpRequestWithBearer(const QUrl &uri); private: