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