Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgeorouteparserosrmv4.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:network-protocol
4
7#include "qgeomaneuver.h"
8
9#include <QtLocation/private/qgeorouteparser_p_p.h>
10#include <QtCore/private/qobject_p.h>
11#include <QtCore/QJsonDocument>
12#include <QtCore/QJsonObject>
13#include <QtCore/QJsonArray>
14#include <QtCore/QUrlQuery>
15
16QT_BEGIN_NAMESPACE
17
18static QList<QGeoCoordinate> parsePolyline(const QByteArray &data)
19{
20 QList<QGeoCoordinate> path;
21
22 bool parsingLatitude = true;
23
24 int shift = 0;
25 int value = 0;
26
27 QGeoCoordinate coord(0, 0);
28
29 for (int i = 0; i < data.length(); ++i) {
30 unsigned char c = data.at(i) - 63;
31
32 value |= (c & 0x1f) << shift;
33 shift += 5;
34
35 // another chunk
36 if (c & 0x20)
37 continue;
38
39 int diff = (value & 1) ? ~(value >> 1) : (value >> 1);
40
41 if (parsingLatitude) {
42 coord.setLatitude(coord.latitude() + (double)diff/1e6);
43 } else {
44 coord.setLongitude(coord.longitude() + (double)diff/1e6);
45 path.append(coord);
46 }
47
48 parsingLatitude = !parsingLatitude;
49
50 value = 0;
51 shift = 0;
52 }
53
54 return path;
55}
56
57static QGeoManeuver::InstructionDirection osrmInstructionDirection(const QString &instructionCode, QGeoRouteParser::TrafficSide trafficSide)
58{
59 if (instructionCode == QLatin1String("0"))
60 return QGeoManeuver::NoDirection;
61 else if (instructionCode == QLatin1String("1"))
62 return QGeoManeuver::DirectionForward;
63 else if (instructionCode == QLatin1String("2"))
64 return QGeoManeuver::DirectionBearRight;
65 else if (instructionCode == QLatin1String("3"))
66 return QGeoManeuver::DirectionRight;
67 else if (instructionCode == QLatin1String("4"))
68 return QGeoManeuver::DirectionHardRight;
69 else if (instructionCode == QLatin1String("5")) {
70 switch (trafficSide) {
71 case QGeoRouteParser::RightHandTraffic:
72 return QGeoManeuver::DirectionUTurnLeft;
73 case QGeoRouteParser::LeftHandTraffic:
74 return QGeoManeuver::DirectionUTurnRight;
75 }
76 return QGeoManeuver::DirectionUTurnLeft;
77 } else if (instructionCode == QLatin1String("6"))
78 return QGeoManeuver::DirectionHardLeft;
79 else if (instructionCode == QLatin1String("7"))
80 return QGeoManeuver::DirectionLeft;
81 else if (instructionCode == QLatin1String("8"))
82 return QGeoManeuver::DirectionBearLeft;
83 else if (instructionCode == QLatin1String("9"))
84 return QGeoManeuver::NoDirection;
85 else if (instructionCode == QLatin1String("10"))
86 return QGeoManeuver::DirectionForward;
87 else if (instructionCode == QLatin1String("11"))
88 return QGeoManeuver::NoDirection;
89 else if (instructionCode == QLatin1String("12"))
90 return QGeoManeuver::NoDirection;
91 else if (instructionCode == QLatin1String("13"))
92 return QGeoManeuver::NoDirection;
93 else if (instructionCode == QLatin1String("14"))
94 return QGeoManeuver::NoDirection;
95 else if (instructionCode == QLatin1String("15"))
96 return QGeoManeuver::NoDirection;
97 else
98 return QGeoManeuver::NoDirection;
99}
100
101static QString osrmInstructionText(const QString &instructionCode, const QString &wayname)
102{
103 if (instructionCode == QLatin1String("0")) {
104 return QString();
105 } else if (instructionCode == QLatin1String("1")) {
106 if (wayname.isEmpty())
107 return QGeoRouteParserOsrmV4::tr("Go straight.");
108 else
109 return QGeoRouteParserOsrmV4::tr("Go straight onto %1.").arg(wayname);
110 } else if (instructionCode == QLatin1String("2")) {
111 if (wayname.isEmpty())
112 return QGeoRouteParserOsrmV4::tr("Turn slightly right.");
113 else
114 return QGeoRouteParserOsrmV4::tr("Turn slightly right onto %1.").arg(wayname);
115 } else if (instructionCode == QLatin1String("3")) {
116 if (wayname.isEmpty())
117 return QGeoRouteParserOsrmV4::tr("Turn right.");
118 else
119 return QGeoRouteParserOsrmV4::tr("Turn right onto %1.").arg(wayname);
120 } else if (instructionCode == QLatin1String("4")) {
121 if (wayname.isEmpty())
122 return QGeoRouteParserOsrmV4::tr("Make a sharp right.");
123 else
124 return QGeoRouteParserOsrmV4::tr("Make a sharp right onto %1.").arg(wayname);
125 }
126 else if (instructionCode == QLatin1String("5")) {
127 return QGeoRouteParserOsrmV4::tr("When it is safe to do so, perform a U-turn.");
128 } else if (instructionCode == QLatin1String("6")) {
129 if (wayname.isEmpty())
130 return QGeoRouteParserOsrmV4::tr("Make a sharp left.");
131 else
132 return QGeoRouteParserOsrmV4::tr("Make a sharp left onto %1.").arg(wayname);
133 } else if (instructionCode == QLatin1String("7")) {
134 if (wayname.isEmpty())
135 return QGeoRouteParserOsrmV4::tr("Turn left.");
136 else
137 return QGeoRouteParserOsrmV4::tr("Turn left onto %1.").arg(wayname);
138 } else if (instructionCode == QLatin1String("8")) {
139 if (wayname.isEmpty())
140 return QGeoRouteParserOsrmV4::tr("Turn slightly left.");
141 else
142 return QGeoRouteParserOsrmV4::tr("Turn slightly left onto %1.").arg(wayname);
143 } else if (instructionCode == QLatin1String("9")) {
144 return QGeoRouteParserOsrmV4::tr("Reached waypoint.");
145 } else if (instructionCode == QLatin1String("10")) {
146 if (wayname.isEmpty())
147 return QGeoRouteParserOsrmV4::tr("Head on.");
148 else
149 return QGeoRouteParserOsrmV4::tr("Head onto %1.").arg(wayname);
150 } else if (instructionCode == QLatin1String("11")) {
151 return QGeoRouteParserOsrmV4::tr("Enter the roundabout.");
152 } else if (instructionCode == QLatin1String("11-1")) {
153 if (wayname.isEmpty())
154 return QGeoRouteParserOsrmV4::tr("At the roundabout take the first exit.");
155 else
156 return QGeoRouteParserOsrmV4::tr("At the roundabout take the first exit onto %1.").arg(wayname);
157 } else if (instructionCode == QLatin1String("11-2")) {
158 if (wayname.isEmpty())
159 return QGeoRouteParserOsrmV4::tr("At the roundabout take the second exit.");
160 else
161 return QGeoRouteParserOsrmV4::tr("At the roundabout take the second exit onto %1.").arg(wayname);
162 } else if (instructionCode == QLatin1String("11-3")) {
163 if (wayname.isEmpty())
164 return QGeoRouteParserOsrmV4::tr("At the roundabout take the third exit.");
165 else
166 return QGeoRouteParserOsrmV4::tr("At the roundabout take the third exit onto %1.").arg(wayname);
167 } else if (instructionCode == QLatin1String("11-4")) {
168 if (wayname.isEmpty())
169 return QGeoRouteParserOsrmV4::tr("At the roundabout take the fourth exit.");
170 else
171 return QGeoRouteParserOsrmV4::tr("At the roundabout take the fourth exit onto %1.").arg(wayname);
172 } else if (instructionCode == QLatin1String("11-5")) {
173 if (wayname.isEmpty())
174 return QGeoRouteParserOsrmV4::tr("At the roundabout take the fifth exit.");
175 else
176 return QGeoRouteParserOsrmV4::tr("At the roundabout take the fifth exit onto %1.").arg(wayname);
177 } else if (instructionCode == QLatin1String("11-6")) {
178 if (wayname.isEmpty())
179 return QGeoRouteParserOsrmV4::tr("At the roundabout take the sixth exit.");
180 else
181 return QGeoRouteParserOsrmV4::tr("At the roundabout take the sixth exit onto %1.").arg(wayname);
182 } else if (instructionCode == QLatin1String("11-7")) {
183 if (wayname.isEmpty())
184 return QGeoRouteParserOsrmV4::tr("At the roundabout take the seventh exit.");
185 else
186 return QGeoRouteParserOsrmV4::tr("At the roundabout take the seventh exit onto %1.").arg(wayname);
187 } else if (instructionCode == QLatin1String("11-8")) {
188 if (wayname.isEmpty())
189 return QGeoRouteParserOsrmV4::tr("At the roundabout take the eighth exit.");
190 else
191 return QGeoRouteParserOsrmV4::tr("At the roundabout take the eighth exit onto %1.").arg(wayname);
192 } else if (instructionCode == QLatin1String("11-9")) {
193 if (wayname.isEmpty())
194 return QGeoRouteParserOsrmV4::tr("At the roundabout take the ninth exit.");
195 else
196 return QGeoRouteParserOsrmV4::tr("At the roundabout take the ninth exit onto %1.").arg(wayname);
197 } else if (instructionCode == QLatin1String("12")) {
198 if (wayname.isEmpty())
199 return QGeoRouteParserOsrmV4::tr("Leave the roundabout.");
200 else
201 return QGeoRouteParserOsrmV4::tr("Leave the roundabout onto %1.").arg(wayname);
202 } else if (instructionCode == QLatin1String("13")) {
203 return QGeoRouteParserOsrmV4::tr("Stay on the roundabout.");
204 } else if (instructionCode == QLatin1String("14")) {
205 if (wayname.isEmpty())
206 return QGeoRouteParserOsrmV4::tr("Start at the end of the street.");
207 else
208 return QGeoRouteParserOsrmV4::tr("Start at the end of %1.").arg(wayname);
209 } else if (instructionCode == QLatin1String("15")) {
210 return QGeoRouteParserOsrmV4::tr("You have reached your destination.");
211 } else {
212 return QGeoRouteParserOsrmV4::tr("Don't know what to say for '%1'").arg(instructionCode);
213 }
214}
215
216static QGeoRoute constructRoute(const QByteArray &geometry, const QJsonArray &instructions,
217 const QJsonObject &summary, QGeoRouteParser::TrafficSide trafficSide)
218{
219 QGeoRoute route;
220
221 const QList<QGeoCoordinate> path = parsePolyline(geometry);
222
223 QGeoRouteSegment firstSegment;
224 int firstPosition = -1;
225
226 for (qsizetype i = instructions.count() - 1; i >= 0; --i) {
227 const QJsonArray instruction = instructions.at(i).toArray();
228
229 if (instruction.count() < 8) {
230 qWarning("Instruction does not contain enough fields.");
231 continue;
232 }
233
234 const QString instructionCode = instruction.at(0).toString();
235 const QString wayname = instruction.at(1).toString();
236 double segmentLength = instruction.at(2).toDouble();
237 int position = instruction.at(3).toDouble();
238 int time = instruction.at(4).toDouble();
239 //const QString segmentLengthString = instruction.at(5).toString();
240 //const QString direction = instruction.at(6).toString();
241 //double azimuth = instruction.at(7).toDouble();
242
243 QGeoRouteSegment segment;
244 segment.setDistance(segmentLength);
245
246 QGeoManeuver maneuver;
247 maneuver.setDirection(osrmInstructionDirection(instructionCode, trafficSide));
248 maneuver.setDistanceToNextInstruction(segmentLength);
249 maneuver.setInstructionText(osrmInstructionText(instructionCode, wayname));
250 maneuver.setPosition(path.at(position));
251 maneuver.setTimeToNextInstruction(time);
252
253 segment.setManeuver(maneuver);
254
255 if (firstPosition == -1)
256 segment.setPath(path.mid(position));
257 else
258 segment.setPath(path.mid(position, firstPosition - position));
259
260 segment.setTravelTime(time);
261
262 segment.setNextRouteSegment(firstSegment);
263
264 firstSegment = segment;
265 firstPosition = position;
266 }
267
268 route.setDistance(summary.value(QStringLiteral("total_distance")).toDouble());
269 route.setTravelTime(summary.value(QStringLiteral("total_time")).toDouble());
270 route.setFirstRouteSegment(firstSegment);
271 route.setPath(path);
272
273 return route;
274}
275
277{
278 Q_DECLARE_PUBLIC(QGeoRouteParserOsrmV4)
279public:
282
283 QGeoRouteReply::Error parseReply(QList<QGeoRoute> &routes, QString &errorString, const QByteArray &reply) const override;
284 QUrl requestUrl(const QGeoRouteRequest &request, const QString &prefix) const override;
285};
286
287QGeoRouteParserOsrmV4Private::QGeoRouteParserOsrmV4Private() : QGeoRouteParserPrivate()
288{
289}
290
294
295QGeoRouteReply::Error QGeoRouteParserOsrmV4Private::parseReply(QList<QGeoRoute> &routes, QString &errorString, const QByteArray &reply) const
296{
297 // OSRM v4 specs: https://github.com/Project-OSRM/osrm-backend/wiki/Server-API---v4,-old
298 QJsonDocument document = QJsonDocument::fromJson(reply);
299
300 if (document.isObject()) {
301 QJsonObject object = document.object();
302
303 //double version = object.value(QStringLiteral("version")).toDouble();
304 int status = object.value(QStringLiteral("status")).toDouble();
305 QString statusMessage = object.value(QStringLiteral("status_message")).toString();
306
307 // status code 0 or 200 are case of success
308 // status code is 207 if no route was found
309 // an error occurred when trying to find a route
310 if (0 != status && 200 != status) {
311 errorString = statusMessage;
312 return QGeoRouteReply::UnknownError;
313 }
314
315 QJsonObject routeSummary = object.value(QStringLiteral("route_summary")).toObject();
316
317 QByteArray routeGeometry =
318 object.value(QStringLiteral("route_geometry")).toString().toLatin1();
319
320 QJsonArray routeInstructions = object.value(QStringLiteral("route_instructions")).toArray();
321
322 QGeoRoute route = constructRoute(routeGeometry, routeInstructions, routeSummary, trafficSide);
323
324 routes.append(route);
325
326 const QJsonArray alternativeSummaries =
327 object.value(QStringLiteral("alternative_summaries")).toArray();
328 const QJsonArray alternativeGeometries =
329 object.value(QStringLiteral("alternative_geometries")).toArray();
330 const QJsonArray alternativeInstructions =
331 object.value(QStringLiteral("alternative_instructions")).toArray();
332
333 if (alternativeSummaries.count() == alternativeGeometries.count() &&
334 alternativeSummaries.count() == alternativeInstructions.count()) {
335 for (qsizetype i = 0; i < alternativeSummaries.count(); ++i) {
336 route = constructRoute(alternativeGeometries.at(i).toString().toLatin1(),
337 alternativeInstructions.at(i).toArray(),
338 alternativeSummaries.at(i).toObject(),
339 trafficSide);
340 //routes.append(route);
341 }
342 }
343
344 return QGeoRouteReply::NoError;
345 } else {
346 errorString = QStringLiteral("Couldn't parse json.");
347 return QGeoRouteReply::ParseError;
348 }
349}
350
351QUrl QGeoRouteParserOsrmV4Private::requestUrl(const QGeoRouteRequest &request, const QString &prefix) const
352{
353 QUrl url(prefix);
354 QUrlQuery query;
355
356 query.addQueryItem(QStringLiteral("instructions"), QStringLiteral("true"));
357
358 for (const QGeoCoordinate &c : request.waypoints()) {
359 query.addQueryItem(QStringLiteral("loc"), QString::number(c.latitude()) + QLatin1Char(',') +
360 QString::number(c.longitude()));
361 }
362
363 url.setQuery(query);
364 return url;
365}
366
367QGeoRouteParserOsrmV4::QGeoRouteParserOsrmV4(QObject *parent)
368 : QGeoRouteParser(*new QGeoRouteParserOsrmV4Private(), parent)
369{
370}
371
375
376QT_END_NAMESPACE
QUrl requestUrl(const QGeoRouteRequest &request, const QString &prefix) const override
QGeoRouteReply::Error parseReply(QList< QGeoRoute > &routes, QString &errorString, const QByteArray &reply) const override
\inmodule QtLocation
static QGeoManeuver::InstructionDirection osrmInstructionDirection(const QString &instructionCode, QGeoRouteParser::TrafficSide trafficSide)
static QGeoRoute constructRoute(const QByteArray &geometry, const QJsonArray &instructions, const QJsonObject &summary, QGeoRouteParser::TrafficSide trafficSide)
static QString osrmInstructionText(const QString &instructionCode, const QString &wayname)