opentripplannerrestbackend.cpp 6.93 KB
Newer Older
1
/*
2
    SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3

4
    SPDX-License-Identifier: LGPL-2.0-or-later
5
6
7
8
9
10
11
12
13
14
15
*/

#include "opentripplannerrestbackend.h"
#include "opentripplannerparser.h"

#include <KPublicTransport/Journey>
#include <KPublicTransport/JourneyReply>
#include <KPublicTransport/JourneyRequest>
#include <KPublicTransport/Location>
#include <KPublicTransport/LocationReply>
#include <KPublicTransport/LocationRequest>
Volker Krause's avatar
Volker Krause committed
16
#include <KPublicTransport/Stopover>
17
#include <KPublicTransport/StopoverReply>
18
#include <KPublicTransport/StopoverRequest>
19
20

#include <QDebug>
21
#include <QJsonArray>
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <QJsonDocument>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QUrl>
#include <QUrlQuery>

using namespace KPublicTransport;

OpenTripPlannerRestBackend::OpenTripPlannerRestBackend() = default;
OpenTripPlannerRestBackend::~OpenTripPlannerRestBackend() = default;

AbstractBackend::Capabilities OpenTripPlannerRestBackend::capabilities() const
{
    return m_endpoint.startsWith(QLatin1String("https://")) ? Secure : NoCapability;
}

bool OpenTripPlannerRestBackend::needsLocationQuery(const Location &loc, AbstractBackend::QueryType type) const
{
    Q_UNUSED(type);
41
42
43
44
45
46
47
    switch (type) {
        case AbstractBackend::QueryType::Journey:
            return !loc.hasCoordinate() && loc.identifier(backendId()).isEmpty();
        case AbstractBackend::QueryType::Departure:
            return loc.identifier(backendId()).isEmpty();
    }
    return false;
48
49
50
51
}

bool OpenTripPlannerRestBackend::queryLocation(const LocationRequest &req, LocationReply *reply, QNetworkAccessManager *nam) const
{
52
53
54
55
    if ((req.types() & Location::Stop) == 0) {
        return false;
    }

56
57
58
59
    if (req.hasCoordinate()) {
        QUrlQuery query;
        query.addQueryItem(QStringLiteral("lat"), QString::number(req.latitude()));
        query.addQueryItem(QStringLiteral("lon"), QString::number(req.longitude()));
60
        query.addQueryItem(QStringLiteral("radius"), QString::number(std::max(1, req.maximumDistance())));
61
62
63
64
65
66
67
68
69
70
71
72

        QUrl url(m_endpoint + QLatin1String("index/stops"));
        url.setQuery(query);

        QNetworkRequest netReq(url);
        logRequest(req, netReq);
        auto netReply = nam->get(netReq);
        QObject::connect(netReply, &QNetworkReply::finished, reply, [this, netReply, reply] {
            const auto data = netReply->readAll();
            logReply(reply, netReply, data);

            if (netReply->error() != QNetworkReply::NoError) {
73
                addError(reply, Reply::NetworkError, netReply->errorString());
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
                return;
            }
            OpenTripPlannerParser p(backendId());
            addResult(reply, p.parseLocationsArray(QJsonDocument::fromJson(data).array()));
        });

        return true;
    }
    if (!req.name().isEmpty()) {
        QUrlQuery query;
        query.addQueryItem(QStringLiteral("query"), req.name());
        query.addQueryItem(QStringLiteral("stops"), QStringLiteral("true"));
        query.addQueryItem(QStringLiteral("corners"), QStringLiteral("false"));

        QUrl url(m_endpoint + QLatin1String("geocode"));
        url.setQuery(query);

        QNetworkRequest netReq(url);
        logRequest(req, netReq);
        auto netReply = nam->get(netReq);
        QObject::connect(netReply, &QNetworkReply::finished, reply, [this, netReply, reply] {
            const auto data = netReply->readAll();
            logReply(reply, netReply, data);

            if (netReply->error() != QNetworkReply::NoError) {
99
                addError(reply, Reply::NetworkError, netReply->errorString());
100
101
102
103
104
105
106
                return;
            }
            OpenTripPlannerParser p(backendId());
            addResult(reply, p.parseGeocodeResult(QJsonDocument::fromJson(data).array()));
        });

        return true;
107
108
    }

109
    return false;
110
111
}

112
bool OpenTripPlannerRestBackend::queryStopover(const StopoverRequest &req, StopoverReply *reply, QNetworkAccessManager *nam) const
113
{
114
115
116
    QUrlQuery query;
    query.addQueryItem(QStringLiteral("date"), QString::number(req.dateTime().toSecsSinceEpoch()));
    query.addQueryItem(QStringLiteral("numberOfDepartures"), QStringLiteral("12"));
117
    query.addQueryItem(QStringLiteral("omitNonPickups"), req.mode() == StopoverRequest::QueryDeparture ? QStringLiteral("true") : QStringLiteral("false"));
118
119
120
121
122
123
124

    QUrl url(m_endpoint + QLatin1String("index/stops/") + req.stop().identifier(backendId()) + QLatin1String("/stoptimes"));
    url.setQuery(query);

    QNetworkRequest netReq(url);
    logRequest(req, netReq);
    auto netReply = nam->get(netReq);
125
    QObject::connect(netReply, &QNetworkReply::finished, reply, [this, netReply, req, reply] {
126
127
128
129
        const auto data = netReply->readAll();
        logReply(reply, netReply, data);

        if (netReply->error() != QNetworkReply::NoError) {
130
            addError(reply, Reply::NetworkError, netReply->errorString());
131
132
133
            return;
        }
        OpenTripPlannerParser p(backendId());
134
135
136
137
138
        auto res = p.parseDeparturesArray(QJsonDocument::fromJson(data).array());
        for (auto &dep : res) {
            dep.setStopPoint(req.stop());
        }
        addResult(reply, this, std::move(res));
139
140
141
    });

    return true;
142
143
144
145
}

bool OpenTripPlannerRestBackend::queryJourney(const JourneyRequest &req, JourneyReply *reply, QNetworkAccessManager *nam) const
{
146
147
148
149
    if ((req.modes() & JourneySection::PublicTransport) == 0) {
        return false;
    }

150
151
152
    QUrlQuery query;
    query.addQueryItem(QStringLiteral("fromPlace"), locationToQuery(req.from()));
    query.addQueryItem(QStringLiteral("toPlace"), locationToQuery(req.to()));
153
154
155
156
157
158
159
    auto dt = req.dateTime();
    if (timeZone().isValid()) {
        dt = dt.toTimeZone(timeZone());
        dt.setTimeSpec(Qt::LocalTime); // pretend we have local time, so toString() isn't adding a UTC offset
    }
    query.addQueryItem(QStringLiteral("date"), dt.date().toString(Qt::ISODate));
    query.addQueryItem(QStringLiteral("time"), dt.time().toString(Qt::ISODate));
160
161
162
163
164
165
166
167
168
169
170
171
172
    query.addQueryItem(QStringLiteral("arriveBy"), req.dateTimeMode() == JourneyRequest::Arrival ? QStringLiteral("true") : QStringLiteral("false"));

    QUrl url(m_endpoint + QLatin1String("plan"));
    url.setQuery(query);

    QNetworkRequest netReq(url);
    logRequest(req, netReq);
    auto netReply = nam->get(netReq);
    QObject::connect(netReply, &QNetworkReply::finished, reply, [this, netReply, reply] {
        const auto data = netReply->readAll();
        logReply(reply, netReply, data);

        if (netReply->error() != QNetworkReply::NoError) {
173
            addError(reply, Reply::NetworkError, netReply->errorString());
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
            return;
        }
        OpenTripPlannerParser p(backendId());
        addResult(reply, this, p.parseJourneys(QJsonDocument::fromJson(data).object()));
    });

    return true;
}

QString OpenTripPlannerRestBackend::locationToQuery(const Location &loc) const
{
    if (loc.hasCoordinate()) {
        return QString::number(loc.latitude()) + QLatin1Char(',') + QString::number(loc.longitude());
    }
    return loc.identifier(backendId());
}