File indexing completed on 2024-05-12 04:42:40
0001 /* 0002 SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "openjourneyplannerrequestbuilder.h" 0008 0009 #include <KPublicTransport/JourneyRequest> 0010 #include <KPublicTransport/LocationRequest> 0011 #include <KPublicTransport/StopoverRequest> 0012 0013 #include <QByteArray> 0014 #include <QDateTime> 0015 #include <QDebug> 0016 #include <QTimeZone> 0017 #include <QXmlStreamWriter> 0018 0019 using namespace KPublicTransport; 0020 0021 static QString siriNS() { return QStringLiteral("http://www.siri.org.uk/siri"); } 0022 static QString ojpNS() { return QStringLiteral("http://www.vdv.de/ojp"); } 0023 static QString triasNS() { return QStringLiteral("http://www.vdv.de/trias"); } 0024 0025 OpenJourneyPlannerRequestBuilder::OpenJourneyPlannerRequestBuilder() = default; 0026 OpenJourneyPlannerRequestBuilder::~OpenJourneyPlannerRequestBuilder() = default; 0027 0028 void OpenJourneyPlannerRequestBuilder::setRequestorRef(const QString &ref) 0029 { 0030 m_requestorRef = ref; 0031 } 0032 0033 void OpenJourneyPlannerRequestBuilder::setUseTrias(bool isTrias) 0034 { 0035 m_useTrias = isTrias; 0036 } 0037 0038 void OpenJourneyPlannerRequestBuilder::setTestMode(bool testMode) 0039 { 0040 m_testMode = testMode; 0041 } 0042 0043 QByteArray OpenJourneyPlannerRequestBuilder::buildLocationInformationRequest(const LocationRequest &req) const 0044 { 0045 QByteArray output; 0046 QXmlStreamWriter w(&output); 0047 setupWriter(w); 0048 writeStartServiceRequest(w); 0049 w.writeStartElement(ns(), m_useTrias ? QStringLiteral("LocationInformationRequest") : QStringLiteral("OJPLocationInformationRequest")); 0050 writeRequestTimestamp(w); 0051 0052 w.writeStartElement(ns(), QStringLiteral("InitialInput")); 0053 if (req.hasCoordinate()) { 0054 w.writeStartElement(ns(), QStringLiteral("GeoRestriction")); 0055 w.writeStartElement(ns(), QStringLiteral("Circle")); 0056 w.writeStartElement(ns(), QStringLiteral("Center")); 0057 w.writeTextElement(siriNS(), QStringLiteral("Longitude"), QString::number(req.location().longitude())); 0058 w.writeTextElement(siriNS(), QStringLiteral("Latitude"), QString::number(req.location().latitude())); 0059 w.writeEndElement(); // </Center> 0060 w.writeTextElement(ns(), QStringLiteral("Radius"), QString::number(req.maximumDistance())); 0061 w.writeEndElement(); // </Circle> 0062 w.writeEndElement(); // </GeoRestriction> 0063 } else { 0064 w.writeTextElement(ns(), QStringLiteral("LocationName"), req.location().name()); 0065 } 0066 w.writeEndElement(); // </InitialInput> 0067 0068 w.writeStartElement(ns(), QStringLiteral("Restrictions")); 0069 w.writeTextElement(ns(), QStringLiteral("Type"), QStringLiteral("stop")); 0070 w.writeTextElement(ns(), QStringLiteral("NumberOfResults"), QString::number(req.maximumResults())); 0071 // TODO ojp:Language 0072 w.writeEndElement(); // </Restrictions> 0073 0074 w.writeEndElement(); // </LocationInformationRequest> 0075 writeEndServiceRequest(w); 0076 return output; 0077 } 0078 0079 QByteArray OpenJourneyPlannerRequestBuilder::buildStopEventRequest(const StopoverRequest &req) const 0080 { 0081 QByteArray output; 0082 QXmlStreamWriter w(&output); 0083 setupWriter(w); 0084 writeStartServiceRequest(w); 0085 w.writeStartElement(ns(), m_useTrias ? QStringLiteral("StopEventRequest") : QStringLiteral("OJPStopEventRequest")); 0086 writeRequestTimestamp(w); 0087 0088 w.writeStartElement(ns(), QStringLiteral("Location")); 0089 writePlaceRef(w, req.stop()); 0090 w.writeTextElement(ns(), QStringLiteral("DepArrTime"), req.dateTime().toUTC().toString(Qt::ISODate)); 0091 w.writeEndElement(); // </Location> 0092 0093 w.writeStartElement(ns(), QStringLiteral("Params")); 0094 w.writeTextElement(ns(), QStringLiteral("StopEventType"), req.mode() == StopoverRequest::QueryArrival ? QStringLiteral("arrival") : QStringLiteral("departure")); // "both" is also supported 0095 w.writeTextElement(ns(), QStringLiteral("IncludePreviousCalls"), QStringLiteral("false")); 0096 w.writeTextElement(ns(), QStringLiteral("IncludeOnwardCalls"), QStringLiteral("false")); 0097 w.writeTextElement(ns(), QStringLiteral("IncludeOperatingDays"), QStringLiteral("false")); 0098 w.writeTextElement(ns(), QStringLiteral("IncludeRealtimeData"), QStringLiteral("true")); 0099 w.writeTextElement(ns(), QStringLiteral("NumberOfResults"), QString::number(req.maximumResults())); 0100 w.writeEndElement(); // </Params> 0101 0102 w.writeEndElement(); // </StopEventRequest> 0103 writeEndServiceRequest(w); 0104 return output; 0105 } 0106 0107 struct { 0108 IndividualTransport::Mode mode; 0109 const char *ojpMode; 0110 } static constexpr const individual_transport_modes[] = { 0111 { IndividualTransport::Walk, "walk" }, 0112 { IndividualTransport::Bike, "cycle" }, 0113 // TODO taxi, self-drive-car others-drive-car motorcycle truck 0114 }; 0115 0116 QByteArray OpenJourneyPlannerRequestBuilder::buildTripRequest(const JourneyRequest &req) const 0117 { 0118 QByteArray output; 0119 QXmlStreamWriter w(&output); 0120 setupWriter(w); 0121 writeStartServiceRequest(w); 0122 w.writeStartElement(ns(), m_useTrias ? QStringLiteral("TripRequest") : QStringLiteral("OJPTripRequest")); 0123 writeRequestTimestamp(w); 0124 0125 w.writeStartElement(ns(), QStringLiteral("Origin")); 0126 writePlaceRef(w, req.from()); 0127 if (req.dateTimeMode() == JourneyRequest::Departure) { 0128 w.writeTextElement(ns(), QStringLiteral("DepArrTime"), req.dateTime().toUTC().toString(Qt::ISODate)); 0129 } 0130 w.writeEndElement(); // </Origin> 0131 0132 w.writeStartElement(ns(), QStringLiteral("Destination")); 0133 writePlaceRef(w, req.to()); 0134 if (req.dateTimeMode() == JourneyRequest::Arrival) { 0135 w.writeTextElement(ns(), QStringLiteral("DepArrTime"), req.dateTime().toUTC().toString(Qt::ISODate)); 0136 } 0137 w.writeEndElement(); // </Destination> 0138 0139 if (!m_useTrias) { 0140 w.writeStartElement(ns(), QStringLiteral("IndividualTransportOptions")); 0141 for (const auto &accessMode : req.accessModes()) { 0142 const auto it = std::find_if(std::begin(individual_transport_modes), std::end(individual_transport_modes), [accessMode](const auto &m) { 0143 return m.mode == accessMode.mode(); 0144 }); 0145 if (it != std::end(individual_transport_modes)) { 0146 w.writeTextElement(ns(), QStringLiteral("Mode"), QLatin1String((*it).ojpMode)); 0147 break; 0148 } 0149 } 0150 w.writeEndElement(); // </IndividualTransportOptions> 0151 } 0152 0153 w.writeStartElement(ns(), QStringLiteral("Params")); 0154 w.writeTextElement(ns(), QStringLiteral("IncludeTrackSections"), req.includePaths() ? QStringLiteral("true") : QStringLiteral("false")); 0155 w.writeTextElement(ns(), QStringLiteral("IncludeLegProjection"), req.includePaths() ? QStringLiteral("true") : QStringLiteral("false")); 0156 w.writeTextElement(ns(), QStringLiteral("IncludeTurnDescription"), req.includePaths() ? QStringLiteral("true") : QStringLiteral("false")); 0157 w.writeTextElement(ns(), QStringLiteral("IncludeAccessibility"), QStringLiteral("true")); // ??? 0158 w.writeTextElement(ns(), QStringLiteral("IncludeIntermediateStops"), req.includeIntermediateStops() ? QStringLiteral("true") : QStringLiteral("false")); 0159 w.writeTextElement(ns(), QStringLiteral("IncludeFares"), QStringLiteral("false")); // TODO 0160 w.writeTextElement(ns(), QStringLiteral("NumberOfResults"), QString::number(req.maximumResults())); 0161 // TODO NumberOfResultsBefore|After for next/prev requests 0162 // TODO BikeTransport 0163 w.writeEndElement(); // </Params> 0164 0165 w.writeEndElement(); // </TripRequest> 0166 writeEndServiceRequest(w); 0167 return output; 0168 } 0169 0170 void OpenJourneyPlannerRequestBuilder::setupWriter(QXmlStreamWriter &w) const 0171 { 0172 if (Q_UNLIKELY(m_testMode)) { 0173 w.setAutoFormatting(true); 0174 w.setAutoFormattingIndent(1); 0175 } 0176 } 0177 0178 void OpenJourneyPlannerRequestBuilder::writeStartServiceRequest(QXmlStreamWriter &w) const 0179 { 0180 w.writeStartDocument(); 0181 w.writeNamespace(siriNS(), QStringLiteral("siri")); 0182 if (m_useTrias) { 0183 w.writeNamespace(triasNS(), QStringLiteral("trias")); 0184 w.writeStartElement(triasNS(), QStringLiteral("Trias")); 0185 w.writeAttribute(QStringLiteral("version"), QStringLiteral("1.2")); 0186 w.writeStartElement(triasNS(), QStringLiteral("ServiceRequest")); 0187 } else { 0188 w.writeNamespace(ojpNS(), QStringLiteral("ojp")); 0189 w.writeStartElement(siriNS(), QStringLiteral("OJP")); 0190 w.writeAttribute(QStringLiteral("version"), QStringLiteral("1.0")); 0191 w.writeStartElement(siriNS(), QStringLiteral("OJPRequest")); 0192 w.writeStartElement(siriNS(), QStringLiteral("ServiceRequest")); 0193 } 0194 if (!m_requestorRef.isEmpty()) { 0195 w.writeTextElement(siriNS(), QStringLiteral("RequestorRef"), m_requestorRef); 0196 } 0197 writeRequestTimestamp(w); 0198 0199 if (m_useTrias) { 0200 w.writeStartElement(ns(), QStringLiteral("RequestPayload")); 0201 } 0202 } 0203 0204 void OpenJourneyPlannerRequestBuilder::writeEndServiceRequest(QXmlStreamWriter &w) const 0205 { 0206 if (m_useTrias) { 0207 w.writeEndElement(); // </trias:RequestPayload> 0208 } 0209 w.writeEndElement(); // </siri:ServiceRequest> 0210 if (!m_useTrias) { 0211 w.writeEndElement(); // </siri:OJPRequest> 0212 } 0213 w.writeEndElement(); // </siri:OJP> or </siri:TRIAS> 0214 w.writeEndDocument(); 0215 } 0216 0217 void OpenJourneyPlannerRequestBuilder::writePlaceRef(QXmlStreamWriter &w, const Location &loc) const 0218 { 0219 w.writeStartElement(ns(), m_useTrias ? QStringLiteral("LocationRef") : QStringLiteral("PlaceRef")); 0220 const auto id = loc.identifier(QStringLiteral("uic")); // ### TODO configure id type 0221 if (!id.isEmpty()) { 0222 w.writeTextElement(ns(), m_useTrias ? QStringLiteral("StopPointRef") : QStringLiteral("StopPlaceRef"), id); 0223 } else if (loc.hasCoordinate()) { 0224 w.writeStartElement(ns(), QStringLiteral("GeoPosition")); 0225 w.writeTextElement(siriNS(), QStringLiteral("Longitude"), QString::number(loc.longitude())); 0226 w.writeTextElement(siriNS(), QStringLiteral("Latitude"), QString::number(loc.latitude())); 0227 w.writeEndElement(); // </GeoPosition> 0228 0229 w.writeStartElement(ns(), QStringLiteral("LocationName")); 0230 w.writeTextElement(ns(), QStringLiteral("Text"), loc.name().isEmpty() ? QStringLiteral(" ") : loc.name()); 0231 w.writeEndElement(); // </LocationName> 0232 } 0233 w.writeEndElement(); // </PlaceRef> 0234 } 0235 0236 void OpenJourneyPlannerRequestBuilder::writeRequestTimestamp(QXmlStreamWriter &w) const 0237 { 0238 if (Q_UNLIKELY(m_testMode)) { 0239 w.writeTextElement(siriNS(), QStringLiteral("RequestTimestamp"), QDateTime({2023, 3, 24}, {12, 34, 56}, QTimeZone::UTC).toString(Qt::ISODate)); 0240 } else { 0241 w.writeTextElement(siriNS(), QStringLiteral("RequestTimestamp"), QDateTime::currentDateTimeUtc().toString(Qt::ISODate)); 0242 } 0243 } 0244 0245 QString OpenJourneyPlannerRequestBuilder::ns() const 0246 { 0247 return m_useTrias ? triasNS() : ojpNS(); 0248 }