Commit f1a62d34 authored by Volker Krause's avatar Volker Krause

Initial support for the (older?) "query.exe" Hafas variant

This gives us live data for SBB and NS, for example. Still needs location
query support for becoming practically useful.
parent c9fb3c24
......@@ -12,6 +12,8 @@ set(kpublictransport_srcs
backends/cache.cpp
backends/hafasmgatebackend.cpp
backends/hafasmgateparser.cpp
backends/hafasquerybackend.cpp
backends/hafasqueryparser.cpp
backends/navitiabackend.cpp
backends/navitiaparser.cpp
......
/*
Copyright (C) 2018 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "hafasquerybackend.h"
#include "logging.h"
#include <KPublicTransport/Departure>
#include <KPublicTransport/DepartureReply>
#include <KPublicTransport/DepartureRequest>
#include <KPublicTransport/Location>
#include <QDateTime>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QUrl>
#include <QUrlQuery>
using namespace KPublicTransport;
HafasQueryBackend::HafasQueryBackend() = default;
bool HafasQueryBackend::isSecure() const
{
return m_endpoint.startsWith(QLatin1String("https://"));
}
bool HafasQueryBackend::queryDeparture(DepartureReply *reply, QNetworkAccessManager *nam) const
{
const auto request = reply->request();
const auto stationId = request.stop().identifier(QLatin1String("ibnr")); // only seems to exist with IBNR so far? if that turns out to be wrong, do the same as in the mgate backend
if (stationId.isEmpty()) {
// TODO support location queries like in the mgate variant
return false;
}
QUrl url(m_endpoint);
url.setPath(url.path() + QLatin1String("/stboard.exe/en")); // dn/nn?
QUrlQuery query;
query.addQueryItem(QLatin1String("boardType"), request.mode() == DepartureRequest::QueryDeparture ? QLatin1String("dep") : QLatin1String("arr"));
query.addQueryItem(QLatin1String("disableEquivs"), QLatin1String("0"));
query.addQueryItem(QLatin1String("maxJourneys"), QLatin1String("12"));
query.addQueryItem(QLatin1String("input"), stationId);
query.addQueryItem(QLatin1String("date"), request.dateTime().date().toString(QLatin1String("dd.MM.yy")));
query.addQueryItem(QLatin1String("time"), request.dateTime().time().toString(QLatin1String("hh:mm")));
query.addQueryItem(QLatin1String("L"), QLatin1String("vs_java3"));
query.addQueryItem(QLatin1String("start"), QLatin1String("yes"));
url.setQuery(query);
qDebug() << url;
auto netReply = nam->get(QNetworkRequest(url));
QObject::connect(netReply, &QNetworkReply::finished, [this, netReply, reply]() {
qDebug() << netReply->request().url();
if (netReply->error() != QNetworkReply::NoError) {
addError(reply, Reply::NetworkError, netReply->errorString());
qCDebug(Log) << reply->error() << reply->errorString();
return;
}
auto res = m_parser.parseStationBoardResponse(netReply->readAll(), reply->request().mode() == DepartureRequest::QueryArrival);
// TODO error handling
addResult(reply, std::move(res));
netReply->deleteLater();
});
return true;
}
/*
Copyright (C) 2018 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef KPUBLICTRANSPORT_HAFASQUERYBACKEND_H
#define KPUBLICTRANSPORT_HAFASQUERYBACKEND_H
#include "abstractbackend.h"
#include "hafasqueryparser.h"
namespace KPublicTransport {
/** Backends for the Hafas "query.exe" based providers. */
class HafasQueryBackend : public AbstractBackend
{
Q_GADGET
Q_PROPERTY(QString endpoint MEMBER m_endpoint)
public:
HafasQueryBackend();
bool isSecure() const override;
bool queryDeparture(DepartureReply *reply, QNetworkAccessManager *nam) const override;
private:
QString m_endpoint;
mutable HafasQueryParser m_parser;
};
}
#endif // KPUBLICTRANSPORT_HAFASQUERYBACKEND_H
/*
Copyright (C) 2018 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "hafasqueryparser.h"
#include <KPublicTransport/Departure>
#include <QDateTime>
#include <QDebug>
#include <QXmlStreamReader>
using namespace KPublicTransport;
std::vector<Departure> HafasQueryParser::parseStationBoardResponse(const QByteArray &data, bool isArrival)
{
qDebug().noquote() << data;
std::vector<Departure> res;
QXmlStreamReader reader;
if (data.startsWith("<Journey")) { // SBB and RT don't reply with valid XML...
reader.addData("<dummyRoot>");
reader.addData(data);
reader.addData("</dummyRoot>");
} else {
reader.addData(data);
}
Location stopPoint;
while (!reader.atEnd()) {
const auto token = reader.readNext();
switch (token) {
case QXmlStreamReader::StartElement:
if (reader.name() == QLatin1String("St")) {
stopPoint.setName(reader.attributes().value(QLatin1String("name")).toString());
stopPoint.setIdentifier(QLatin1String("ibnr"), reader.attributes().value(QLatin1String("evaId")).toString());
} else if (reader.name() == QLatin1String("Journey")) {
auto dt = QDateTime::fromString(reader.attributes().value(QLatin1String("fpDate")) + reader.attributes().value(QLatin1String("fpTime")), QLatin1String("dd.MM.yyhh:mm"));
if (dt.date().year() < 2000) {
dt = dt.addYears(100);
}
const auto delayStr = reader.attributes().value(QLatin1String("e_delay"));
const auto delaySecs = delayStr.toInt() * 60;
Departure dep;
if (isArrival) {
dep.setScheduledArrivalTime(dt);
if (!delayStr.isEmpty()) {
dep.setExpectedArrivalTime(dt.addSecs(delaySecs));
}
} else {
dep.setScheduledDepartureTime(dt);
if (!delayStr.isEmpty()) {
dep.setExpectedDepartureTime(dt.addSecs(delaySecs));
}
}
dep.setScheduledPlatform(reader.attributes().value(QLatin1String("platform")).toString());
dep.setExpectedPlatform(reader.attributes().value(QLatin1String("newpl")).toString());
Route route;
route.setDirection(reader.attributes().value(QLatin1String("targetLoc")).toString());
Line line;
line.setName(reader.attributes().value(QLatin1String("hafasname")).toString());
if (line.name().isEmpty()) {
const auto prod = reader.attributes().value(QLatin1String("prod"));
const auto idx = prod.indexOf(QLatin1Char('#'));
line.setName(prod.left(idx).toString().simplified());
}
// TODO line mode (class attribute, or second part of prod attribute)
route.setLine(line);
dep.setRoute(route);
dep.setStopPoint(stopPoint);
res.push_back(dep);
}
break;
default:
break;
}
}
return res;
}
/*
Copyright (C) 2018 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef KPUBLICTRANSPORT_HAFASQUERYPARSER_H
#define KPUBLICTRANSPORT_HAFASQUERYPARSER_H
#include <vector>
class QByteArray;
namespace KPublicTransport {
class Departure;
/** Parse for Hafas "query.exe" variant XML responses. */
class HafasQueryParser
{
public:
std::vector<Departure> parseStationBoardResponse(const QByteArray &data, bool isArrival);
};
}
#endif // KPUBLICTRANSPORT_HAFASQUERYPARSER_H
......@@ -7,6 +7,8 @@
{ "type": "Feature", "properties": { "id": 1, "pto": "vbb" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 11.049980606188592, 53.255023340553734 ], [ 14.450175166469796, 53.658123819796209 ], [ 14.907763336584974, 51.429550154798044 ], [ 13.148976578464548, 51.250906431986493 ], [ 11.049980606188592, 53.255023340553734 ] ] ] ] } },
{ "type": "Feature", "properties": { "id": 2, "pto": "oebb" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 9.14562225603507, 47.724658258492276 ], [ 17.052572342480296, 49.537661624446251 ], [ 17.325824045961401, 46.267315839926397 ], [ 9.622728404970328, 46.492856928513973 ], [ 9.14562225603507, 47.724658258492276 ] ] ] ] } },
{ "type": "Feature", "properties": { "id": 3, "pto": "sncf" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -5.61972872135432, 48.662605574012751 ], [ 2.460714510158212, 51.369098637063651 ], [ 8.732491704343463, 49.000917206894115 ], [ 10.098750221748965, 41.063605820062165 ], [ -2.314684307821011, 42.807211927989179 ], [ -5.61972872135432, 48.662605574012751 ] ] ] ] } },
{ "type": "Feature", "properties": { "id": 4, "pto": "sncb" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 1.797103230275546, 51.108858919462591 ], [ 5.551061156670661, 49.1245310727546 ], [ 6.598526020014879, 50.464765618399994 ], [ 5.154195587329063, 51.837530128745527 ], [ 1.797103230275546, 51.108858919462591 ] ] ] ] } }
{ "type": "Feature", "properties": { "id": 4, "pto": "sncb" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 1.797103230275546, 51.108858919462591 ], [ 5.551061156670661, 49.1245310727546 ], [ 6.598526020014879, 50.464765618399994 ], [ 5.154195587329063, 51.837530128745527 ], [ 1.797103230275546, 51.108858919462591 ] ] ] ] } },
{ "type": "Feature", "properties": { "id": 6, "pto": "ns" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 3.169867740621071, 51.273135241198254 ], [ 6.130094528332992, 50.612776957785606 ], [ 7.730568791579436, 53.46240186551708 ], [ 5.290821439069613, 53.891797399558804 ], [ 3.169867740621071, 51.273135241198254 ] ] ] ] } },
{ "type": "Feature", "properties": { "id": 7, "pto": "sbb" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 5.865517482105258, 45.912758561552828 ], [ 10.432724526003646, 45.70456678747199 ], [ 10.532483084417382, 47.851544457680639 ], [ 6.38165958868067, 47.77347254240032 ], [ 5.865517482105258, 45.912758561552828 ] ] ] ] } }
]
}
......@@ -28,6 +28,7 @@
#include "backends/cache.h"
#include "backends/hafasmgatebackend.h"
#include "backends/hafasquerybackend.h"
#include "backends/navitiabackend.h"
#include <QDirIterator>
......@@ -99,9 +100,13 @@ std::unique_ptr<AbstractBackend> ManagerPrivate::loadNetwork(const QJsonObject &
const auto type = obj.value(QLatin1String("type")).toString();
if (type == QLatin1String("navitia")) {
return loadNetwork<NavitiaBackend>(obj);
} else if (type == QLatin1String("hafas_mgate")) {
}
if (type == QLatin1String("hafas_mgate")) {
return loadNetwork<HafasMgateBackend>(obj);
}
if (type == QLatin1String("hafas_query")) {
return loadNetwork<HafasQueryBackend>(obj);
}
qCWarning(Log) << "Unknown backend type:" << type;
return {};
......
......@@ -4,7 +4,10 @@
<file>db.json</file>
<file>dsb.json</file>
<file>navitia.json</file>
<file>ns.json</file>
<file>oebb.json</file>
<file>railteam.json</file>
<file>sbb.json</file>
<file>sncb.json</file>
<file>sncf.json</file>
<file>vbb.json</file>
......
{
"type": "hafas_query",
"options": {
"endpoint": "https://hafas.bene-system.com/bin/"
},
"filter": {
"geo": [ [ 3.169, 51.273 ], [ 6.13, 50.612 ], [ 7.73, 53.462 ], [ 5.29, 53.891 ] ]
}
}
{
"type": "hafas_query",
"options": {
"endpoint": "http://railteam.hafas.eu/bin/"
}
}
{
"type": "hafas_query",
"options": {
"endpoint": "http://fahrplan.sbb.ch/bin/"
},
"filter": {
"geo": [ [ 5.865, 45.912 ], [ 10.432, 45.704 ], [ 10.532, 47.851 ], [ 6.381, 47.773 ] ]
}
}
......@@ -51,6 +51,7 @@ Kirigami.ApplicationWindow {
ListElement { name: "Akademy 2014 venue"; lat: 16.57564; lon: 49.22462 }
ListElement { name: "Copenhagen Central"; lat: 12.56489; lon: 55.67238; locId:"8600626"; idType: "uic" }
ListElement { name: "Frankfurt (Main) Hauptbahnhof"; lat: 8.6625; lon: 50.106944; locId:"8000105"; idType: "ibnr" }
ListElement { name: "Schiphol Airport"; lat: 4.7611; lon: 52.309; locId: "8400561"; idType: "ibnr" }
}
Component {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment