1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the QtLocation module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "qgeorouteparserosrmv5_p.h"
38#include "qgeoroute.h"
39#include "qgeoroute_p.h"
40#include "qgeorouteparser_p_p.h"
41#include "qgeoroutesegment.h"
42#include "qgeoroutesegment_p.h"
43#include "qgeomaneuver.h"
44
45#include <QtCore/private/qobject_p.h>
46#include <QtCore/QJsonDocument>
47#include <QtCore/QJsonObject>
48#include <QtCore/QJsonArray>
49#include <QtCore/QUrlQuery>
50#include <QtPositioning/private/qlocationutils_p.h>
51#include <QtPositioning/qgeopath.h>
52
53QT_BEGIN_NAMESPACE
54
55static QList<QGeoCoordinate> decodePolyline(const QString &polylineString)
56{
57 QList<QGeoCoordinate> path;
58 if (polylineString.isEmpty())
59 return path;
60
61 QByteArray data = polylineString.toLatin1();
62
63 bool parsingLatitude = true;
64
65 int shift = 0;
66 int value = 0;
67
68 QGeoCoordinate coord(0, 0);
69
70 for (int i = 0; i < data.length(); ++i) {
71 unsigned char c = data.at(i) - 63;
72
73 value |= (c & 0x1f) << shift;
74 shift += 5;
75
76 // another chunk
77 if (c & 0x20)
78 continue;
79
80 int diff = (value & 1) ? ~(value >> 1) : (value >> 1);
81
82 if (parsingLatitude) {
83 coord.setLatitude(coord.latitude() + (double)diff/1e6);
84 } else {
85 coord.setLongitude(coord.longitude() + (double)diff/1e6);
86 path.append(t: coord);
87 }
88
89 parsingLatitude = !parsingLatitude;
90
91 value = 0;
92 shift = 0;
93 }
94
95 return path;
96}
97
98static QString cardinalDirection4(QLocationUtils::CardinalDirection direction)
99{
100 switch (direction) {
101 case QLocationUtils::CardinalN:
102 //: Translations exist at https://github.com/Project-OSRM/osrm-text-instructions.
103 //: Always used in "Head %1 [onto <street name>]"
104 return QGeoRouteParserOsrmV5::tr(s: "North");
105 case QLocationUtils::CardinalE:
106 return QGeoRouteParserOsrmV5::tr(s: "East");
107 case QLocationUtils::CardinalS:
108 return QGeoRouteParserOsrmV5::tr(s: "South");
109 case QLocationUtils::CardinalW:
110 return QGeoRouteParserOsrmV5::tr(s: "West");
111 default:
112 return QString();
113 }
114}
115
116static QString exitOrdinal(int exit)
117{
118 static QList<QString> ordinals;
119
120 if (!ordinals.size()) {
121 ordinals.append(t: QLatin1String(""));
122 //: always used in " and take the %1 exit [onto <street name>]"
123 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "first", c: "roundabout exit"));
124 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "second", c: "roundabout exit"));
125 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "third", c: "roundabout exit"));
126 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "fourth", c: "roundabout exit"));
127 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "fifth", c: "roundabout exit"));
128 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "sixth", c: "roundabout exit"));
129 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "seventh", c: "roundabout exit"));
130 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "eighth", c: "roundabout exit"));
131 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "ninth", c: "roundabout exit"));
132 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "tenth", c: "roundabout exit"));
133 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "eleventh", c: "roundabout exit"));
134 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "twelfth", c: "roundabout exit"));
135 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "thirteenth", c: "roundabout exit"));
136 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "fourteenth", c: "roundabout exit"));
137 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "fifteenth", c: "roundabout exit"));
138 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "sixteenth", c: "roundabout exit"));
139 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "seventeenth", c: "roundabout exit"));
140 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "eighteenth", c: "roundabout exit"));
141 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "nineteenth", c: "roundabout exit"));
142 ordinals.append(t: QGeoRouteParserOsrmV5::tr(s: "twentieth", c: "roundabout exit"));
143 };
144
145 if (exit < 1 || exit > ordinals.size())
146 return QString();
147 return ordinals[exit];
148}
149
150static QString exitDirection(int exit, const QString &wayName)
151{
152 /*: Always appended to one of the following strings:
153 - "Enter the roundabout"
154 - "Enter the rotary"
155 - "Enter the rotary <rotaryname>"
156 */
157 static QString directionExit = QGeoRouteParserOsrmV5::tr(s: " and take the %1 exit");
158 static QString directionExitOnto = QGeoRouteParserOsrmV5::tr(s: " and take the %1 exit onto %2");
159
160 if (exit < 1 || exit > 20)
161 return QString();
162 if (wayName.isEmpty())
163 return directionExit.arg(a: exitOrdinal(exit));
164 else
165 return directionExitOnto.arg(args: exitOrdinal(exit), args: wayName);
166}
167
168static QString instructionArrive(QGeoManeuver::InstructionDirection direction)
169{
170 switch (direction) {
171 case QGeoManeuver::DirectionForward:
172 return QGeoRouteParserOsrmV5::tr(s: "You have arrived at your destination, straight ahead");
173 case QGeoManeuver::DirectionUTurnLeft:
174 case QGeoManeuver::DirectionHardLeft:
175 case QGeoManeuver::DirectionLeft:
176 case QGeoManeuver::DirectionLightLeft:
177 case QGeoManeuver::DirectionBearLeft:
178 return QGeoRouteParserOsrmV5::tr(s: "You have arrived at your destination, on the left");
179 case QGeoManeuver::DirectionUTurnRight:
180 case QGeoManeuver::DirectionHardRight:
181 case QGeoManeuver::DirectionRight:
182 case QGeoManeuver::DirectionLightRight:
183 case QGeoManeuver::DirectionBearRight:
184 return QGeoRouteParserOsrmV5::tr(s: "You have arrived at your destination, on the right");
185 default:
186 return QGeoRouteParserOsrmV5::tr(s: "You have arrived at your destination");
187 }
188}
189
190static QString instructionContinue(const QString &wayName, QGeoManeuver::InstructionDirection direction)
191{
192 switch (direction) {
193 case QGeoManeuver::DirectionForward:
194 if (wayName.isEmpty())
195 return QGeoRouteParserOsrmV5::tr(s: "Continue straight");
196 else
197 return QGeoRouteParserOsrmV5::tr(s: "Continue straight on %1").arg(a: wayName);
198 case QGeoManeuver::DirectionHardLeft:
199 case QGeoManeuver::DirectionLeft:
200 if (wayName.isEmpty())
201 return QGeoRouteParserOsrmV5::tr(s: "Continue left");
202 else
203 return QGeoRouteParserOsrmV5::tr(s: "Continue left onto %1").arg(a: wayName);
204 case QGeoManeuver::DirectionLightLeft:
205 case QGeoManeuver::DirectionBearLeft:
206 if (wayName.isEmpty())
207 return QGeoRouteParserOsrmV5::tr(s: "Continue slightly left");
208 else
209 return QGeoRouteParserOsrmV5::tr(s: "Continue slightly left on %1").arg(a: wayName);
210 case QGeoManeuver::DirectionHardRight:
211 case QGeoManeuver::DirectionRight:
212 if (wayName.isEmpty())
213 return QGeoRouteParserOsrmV5::tr(s: "Continue right");
214 else
215 return QGeoRouteParserOsrmV5::tr(s: "Continue right onto %1").arg(a: wayName);
216 case QGeoManeuver::DirectionLightRight:
217 case QGeoManeuver::DirectionBearRight:
218 if (wayName.isEmpty())
219 return QGeoRouteParserOsrmV5::tr(s: "Continue slightly right");
220 else
221 return QGeoRouteParserOsrmV5::tr(s: "Continue slightly right on %1").arg(a: wayName);
222 case QGeoManeuver::DirectionUTurnLeft:
223 case QGeoManeuver::DirectionUTurnRight:
224 if (wayName.isEmpty())
225 return QGeoRouteParserOsrmV5::tr(s: "Make a U-turn");
226 else
227 return QGeoRouteParserOsrmV5::tr(s: "Make a U-turn onto %1").arg(a: wayName);
228 default:
229 if (wayName.isEmpty())
230 return QGeoRouteParserOsrmV5::tr(s: "Continue");
231 else
232 return QGeoRouteParserOsrmV5::tr(s: "Continue on %1").arg(a: wayName);
233 }
234}
235
236static QString instructionDepart(const QJsonObject &maneuver, const QString &wayName)
237{
238 double bearing = maneuver.value(key: QLatin1String("bearing_after")).toDouble(defaultValue: -1.0);
239 if (bearing >= 0.0) {
240 if (wayName.isEmpty())
241 //: %1 is "North", "South", "East" or "West"
242 return QGeoRouteParserOsrmV5::tr(s: "Head %1").arg(a: cardinalDirection4(direction: QLocationUtils::azimuthToCardinalDirection4(azimuth: bearing)));
243 else
244 return QGeoRouteParserOsrmV5::tr(s: "Head %1 onto %2").arg(args: cardinalDirection4(direction: QLocationUtils::azimuthToCardinalDirection4(azimuth: bearing)), args: wayName);
245 } else {
246 if (wayName.isEmpty())
247 return QGeoRouteParserOsrmV5::tr(s: "Depart");
248 else
249 return QGeoRouteParserOsrmV5::tr(s: "Depart onto %1").arg(a: wayName);
250 }
251}
252
253static QString instructionEndOfRoad(const QString &wayName, QGeoManeuver::InstructionDirection direction)
254{
255 switch (direction) {
256 case QGeoManeuver::DirectionHardLeft:
257 case QGeoManeuver::DirectionLeft:
258 case QGeoManeuver::DirectionLightLeft:
259 case QGeoManeuver::DirectionBearLeft:
260 if (wayName.isEmpty())
261 return QGeoRouteParserOsrmV5::tr(s: "At the end of the road, turn left");
262 else
263 return QGeoRouteParserOsrmV5::tr(s: "At the end of the road, turn left onto %1").arg(a: wayName);
264 case QGeoManeuver::DirectionHardRight:
265 case QGeoManeuver::DirectionRight:
266 case QGeoManeuver::DirectionLightRight:
267 case QGeoManeuver::DirectionBearRight:
268 if (wayName.isEmpty())
269 return QGeoRouteParserOsrmV5::tr(s: "At the end of the road, turn right");
270 else
271 return QGeoRouteParserOsrmV5::tr(s: "At the end of the road, turn right onto %1").arg(a: wayName);
272 case QGeoManeuver::DirectionUTurnLeft:
273 case QGeoManeuver::DirectionUTurnRight:
274 if (wayName.isEmpty())
275 return QGeoRouteParserOsrmV5::tr(s: "At the end of the road, make a U-turn");
276 else
277 return QGeoRouteParserOsrmV5::tr(s: "At the end of the road, make a U-turn onto %1").arg(a: wayName);
278 case QGeoManeuver::DirectionForward:
279 if (wayName.isEmpty())
280 return QGeoRouteParserOsrmV5::tr(s: "At the end of the road, continue straight");
281 else
282 return QGeoRouteParserOsrmV5::tr(s: "At the end of the road, continue straight onto %1").arg(a: wayName);
283 default:
284 if (wayName.isEmpty())
285 return QGeoRouteParserOsrmV5::tr(s: "At the end of the road, continue");
286 else
287 return QGeoRouteParserOsrmV5::tr(s: "At the end of the road, continue onto %1").arg(a: wayName);
288 }
289}
290
291static QString instructionFerry(const QString &wayName)
292{
293 QString instruction = QGeoRouteParserOsrmV5::tr(s: "Take the ferry");
294 if (!wayName.isEmpty())
295 instruction += QLatin1String(" [") + wayName + QLatin1Char(']');
296
297 return instruction;
298}
299
300static QString instructionFork(const QString &wayName, QGeoManeuver::InstructionDirection direction)
301{
302 switch (direction) {
303 case QGeoManeuver::DirectionHardLeft:
304 if (wayName.isEmpty())
305 return QGeoRouteParserOsrmV5::tr(s: "At the fork, take a sharp left");
306 else
307 return QGeoRouteParserOsrmV5::tr(s: "At the fork, take a sharp left onto %1").arg(a: wayName);
308 case QGeoManeuver::DirectionLeft:
309 if (wayName.isEmpty())
310 return QGeoRouteParserOsrmV5::tr(s: "At the fork, turn left");
311 else
312 return QGeoRouteParserOsrmV5::tr(s: "At the fork, turn left onto %1").arg(a: wayName);
313 case QGeoManeuver::DirectionLightLeft:
314 case QGeoManeuver::DirectionBearLeft:
315 if (wayName.isEmpty())
316 return QGeoRouteParserOsrmV5::tr(s: "At the fork, keep left");
317 else
318 return QGeoRouteParserOsrmV5::tr(s: "At the fork, keep left onto %1").arg(a: wayName);
319 case QGeoManeuver::DirectionHardRight:
320 if (wayName.isEmpty())
321 return QGeoRouteParserOsrmV5::tr(s: "At the fork, take a sharp right");
322 else
323 return QGeoRouteParserOsrmV5::tr(s: "At the fork, take a sharp right onto %1").arg(a: wayName);
324 case QGeoManeuver::DirectionRight:
325 if (wayName.isEmpty())
326 return QGeoRouteParserOsrmV5::tr(s: "At the fork, turn right");
327 else
328 return QGeoRouteParserOsrmV5::tr(s: "At the fork, turn right onto %1").arg(a: wayName);
329 case QGeoManeuver::DirectionLightRight:
330 case QGeoManeuver::DirectionBearRight:
331 if (wayName.isEmpty())
332 return QGeoRouteParserOsrmV5::tr(s: "At the fork, keep right");
333 else
334 return QGeoRouteParserOsrmV5::tr(s: "At the fork, keep right onto %1").arg(a: wayName);
335 case QGeoManeuver::DirectionUTurnLeft:
336 case QGeoManeuver::DirectionUTurnRight:
337 if (wayName.isEmpty())
338 return QGeoRouteParserOsrmV5::tr(s: "Make a U-turn");
339 else
340 return QGeoRouteParserOsrmV5::tr(s: "Make a U-turn onto %1").arg(a: wayName);
341 case QGeoManeuver::DirectionForward:
342 if (wayName.isEmpty())
343 return QGeoRouteParserOsrmV5::tr(s: "At the fork, continue straight ahead");
344 else
345 return QGeoRouteParserOsrmV5::tr(s: "At the fork, continue straight ahead onto %1").arg(a: wayName);
346 default:
347 if (wayName.isEmpty())
348 return QGeoRouteParserOsrmV5::tr(s: "At the fork, continue");
349 else
350 return QGeoRouteParserOsrmV5::tr(s: "At the fork, continue onto %1").arg(a: wayName);
351 }
352}
353
354static QString instructionMerge(const QString &wayName, QGeoManeuver::InstructionDirection direction)
355{
356 switch (direction) {
357 case QGeoManeuver::DirectionUTurnLeft:
358 case QGeoManeuver::DirectionHardLeft:
359 if (wayName.isEmpty())
360 return QGeoRouteParserOsrmV5::tr(s: "Merge sharply left");
361 else
362 return QGeoRouteParserOsrmV5::tr(s: "Merge sharply left onto %1").arg(a: wayName);
363 case QGeoManeuver::DirectionLeft:
364 if (wayName.isEmpty())
365 return QGeoRouteParserOsrmV5::tr(s: "Merge left");
366 else
367 return QGeoRouteParserOsrmV5::tr(s: "Merge left onto %1").arg(a: wayName);
368 case QGeoManeuver::DirectionLightLeft:
369 case QGeoManeuver::DirectionBearLeft:
370 if (wayName.isEmpty())
371 return QGeoRouteParserOsrmV5::tr(s: "Merge slightly left");
372 else
373 return QGeoRouteParserOsrmV5::tr(s: "Merge slightly left on %1").arg(a: wayName);
374 case QGeoManeuver::DirectionUTurnRight:
375 case QGeoManeuver::DirectionHardRight:
376 if (wayName.isEmpty())
377 return QGeoRouteParserOsrmV5::tr(s: "Merge sharply right");
378 else
379 return QGeoRouteParserOsrmV5::tr(s: "Merge sharply right onto %1").arg(a: wayName);
380 case QGeoManeuver::DirectionRight:
381 if (wayName.isEmpty())
382 return QGeoRouteParserOsrmV5::tr(s: "Merge right");
383 else
384 return QGeoRouteParserOsrmV5::tr(s: "Merge right onto %1").arg(a: wayName);
385 case QGeoManeuver::DirectionLightRight:
386 case QGeoManeuver::DirectionBearRight:
387 if (wayName.isEmpty())
388 return QGeoRouteParserOsrmV5::tr(s: "Merge slightly right");
389 else
390 return QGeoRouteParserOsrmV5::tr(s: "Merge slightly right on %1").arg(a: wayName);
391 case QGeoManeuver::DirectionForward:
392 if (wayName.isEmpty())
393 return QGeoRouteParserOsrmV5::tr(s: "Merge straight");
394 else
395 return QGeoRouteParserOsrmV5::tr(s: "Merge straight on %1").arg(a: wayName);
396 default:
397 if (wayName.isEmpty())
398 return QGeoRouteParserOsrmV5::tr(s: "Merge");
399 else
400 return QGeoRouteParserOsrmV5::tr(s: "Merge onto %1").arg(a: wayName);
401 }
402}
403
404static QString instructionNewName(const QString &wayName, QGeoManeuver::InstructionDirection direction)
405{
406 switch (direction) {
407 case QGeoManeuver::DirectionHardLeft:
408 if (wayName.isEmpty())
409 return QGeoRouteParserOsrmV5::tr(s: "Take a sharp left");
410 else
411 return QGeoRouteParserOsrmV5::tr(s: "Take a sharp left onto %1").arg(a: wayName);
412 case QGeoManeuver::DirectionLeft:
413 if (wayName.isEmpty())
414 return QGeoRouteParserOsrmV5::tr(s: "Turn left");
415 else
416 return QGeoRouteParserOsrmV5::tr(s: "Turn left onto %1").arg(a: wayName);
417 case QGeoManeuver::DirectionLightLeft:
418 case QGeoManeuver::DirectionBearLeft:
419 if (wayName.isEmpty())
420 return QGeoRouteParserOsrmV5::tr(s: "Continue slightly left");
421 else
422 return QGeoRouteParserOsrmV5::tr(s: "Continue slightly left onto %1").arg(a: wayName);
423 case QGeoManeuver::DirectionHardRight:
424 if (wayName.isEmpty())
425 return QGeoRouteParserOsrmV5::tr(s: "Take a sharp right");
426 else
427 return QGeoRouteParserOsrmV5::tr(s: "Take a sharp right onto %1").arg(a: wayName);
428 case QGeoManeuver::DirectionRight:
429 if (wayName.isEmpty())
430 return QGeoRouteParserOsrmV5::tr(s: "Turn right");
431 else
432 return QGeoRouteParserOsrmV5::tr(s: "Turn right onto %1").arg(a: wayName);
433 case QGeoManeuver::DirectionLightRight:
434 case QGeoManeuver::DirectionBearRight:
435 if (wayName.isEmpty())
436 return QGeoRouteParserOsrmV5::tr(s: "Continue slightly right");
437 else
438 return QGeoRouteParserOsrmV5::tr(s: "Continue slightly right onto %1").arg(a: wayName);
439 case QGeoManeuver::DirectionUTurnLeft:
440 case QGeoManeuver::DirectionUTurnRight:
441 if (wayName.isEmpty())
442 return QGeoRouteParserOsrmV5::tr(s: "Make a U-turn");
443 else
444 return QGeoRouteParserOsrmV5::tr(s: "Make a U-turn onto %1").arg(a: wayName);
445 case QGeoManeuver::DirectionForward:
446 if (wayName.isEmpty())
447 return QGeoRouteParserOsrmV5::tr(s: "Continue straight");
448 else
449 return QGeoRouteParserOsrmV5::tr(s: "Continue straight onto %1").arg(a: wayName);
450 default:
451 if (wayName.isEmpty())
452 return QGeoRouteParserOsrmV5::tr(s: "Continue");
453 else
454 return QGeoRouteParserOsrmV5::tr(s: "Continue onto %1").arg(a: wayName);
455 }
456}
457
458static QString instructionNotification(const QString &wayName, QGeoManeuver::InstructionDirection direction)
459{
460 switch (direction) {
461 case QGeoManeuver::DirectionUTurnLeft:
462 case QGeoManeuver::DirectionHardLeft:
463 case QGeoManeuver::DirectionLeft:
464 case QGeoManeuver::DirectionLightLeft:
465 case QGeoManeuver::DirectionBearLeft:
466 if (wayName.isEmpty())
467 return QGeoRouteParserOsrmV5::tr(s: "Continue on the left");
468 else
469 return QGeoRouteParserOsrmV5::tr(s: "Continue on the left on %1").arg(a: wayName);
470 case QGeoManeuver::DirectionUTurnRight:
471 case QGeoManeuver::DirectionHardRight:
472 case QGeoManeuver::DirectionRight:
473 case QGeoManeuver::DirectionLightRight:
474 case QGeoManeuver::DirectionBearRight:
475 if (wayName.isEmpty())
476 return QGeoRouteParserOsrmV5::tr(s: "Continue on the right");
477 else
478 return QGeoRouteParserOsrmV5::tr(s: "Continue on the right on %1").arg(a: wayName);
479 case QGeoManeuver::DirectionForward:
480 default:
481 if (wayName.isEmpty())
482 return QGeoRouteParserOsrmV5::tr(s: "Continue");
483 else
484 return QGeoRouteParserOsrmV5::tr(s: "Continue on %1").arg(a: wayName);
485 }
486}
487
488static QString instructionOffRamp(const QString &wayName, QGeoManeuver::InstructionDirection direction)
489{
490 switch (direction) {
491 case QGeoManeuver::DirectionUTurnLeft:
492 case QGeoManeuver::DirectionHardLeft:
493 case QGeoManeuver::DirectionLeft:
494 case QGeoManeuver::DirectionLightLeft:
495 case QGeoManeuver::DirectionBearLeft:
496 if (wayName.isEmpty())
497 return QGeoRouteParserOsrmV5::tr(s: "Take the ramp on the left");
498 else
499 return QGeoRouteParserOsrmV5::tr(s: "Take the ramp on the left onto %1").arg(a: wayName);
500 case QGeoManeuver::DirectionUTurnRight:
501 case QGeoManeuver::DirectionHardRight:
502 case QGeoManeuver::DirectionRight:
503 case QGeoManeuver::DirectionLightRight:
504 case QGeoManeuver::DirectionBearRight:
505 if (wayName.isEmpty())
506 return QGeoRouteParserOsrmV5::tr(s: "Take the ramp on the right");
507 else
508 return QGeoRouteParserOsrmV5::tr(s: "Take the ramp on the right onto %1").arg(a: wayName);
509 case QGeoManeuver::DirectionForward:
510 default:
511 if (wayName.isEmpty())
512 return QGeoRouteParserOsrmV5::tr(s: "Take the ramp");
513 else
514 return QGeoRouteParserOsrmV5::tr(s: "Take the ramp onto %1").arg(a: wayName);
515 }
516}
517
518static QString instructionOnRamp(const QString &wayName, QGeoManeuver::InstructionDirection direction)
519{
520 return instructionOffRamp(wayName, direction);
521}
522
523static QString instructionPushingBike(const QString &wayName)
524{
525 if (wayName.isEmpty())
526 return QGeoRouteParserOsrmV5::tr(s: "Get off the bike and push");
527 else
528 return QGeoRouteParserOsrmV5::tr(s: "Get off the bike and push onto %1").arg(a: wayName);
529}
530
531static QString instructionRotary(const QJsonObject &step, const QJsonObject &maneuver, const QString &wayName)
532{
533 QString instruction;
534 QString rotaryName = step.value(key: QLatin1String("rotary_name")).toString();
535 //QString modifier = maneuver.value(QLatin1String("modifier")).toString(); // Apparently not used for rotaries
536 int exit = maneuver.value(key: QLatin1String("exit")).toInt(defaultValue: 0);
537
538 //: This string will be prepended to " and take the <nth> exit [onto <streetname>]
539 instruction += QGeoRouteParserOsrmV5::tr(s: "Enter the rotary");
540 if (!rotaryName.isEmpty())
541 instruction += QLatin1Char(' ') + rotaryName;
542 instruction += exitDirection(exit, wayName);
543 return instruction;
544}
545
546static QString instructionRoundabout(const QJsonObject &maneuver, const QString &wayName)
547{
548 QString instruction;
549 //QString modifier = maneuver.value(QLatin1String("modifier")).toString(); // Apparently not used for rotaries
550 int exit = maneuver.value(key: QLatin1String("exit")).toInt(defaultValue: 0);
551
552 //: This string will be prepended to " and take the <nth> exit [onto <streetname>]
553 instruction += QGeoRouteParserOsrmV5::tr(s: "Enter the roundabout");
554 instruction += exitDirection(exit, wayName);
555 return instruction;
556}
557
558static QString instructionRoundaboutTurn(const QString &wayName, QGeoManeuver::InstructionDirection direction)
559{
560 switch (direction) {
561 case QGeoManeuver::DirectionForward:
562 if (wayName.isEmpty())
563 return QGeoRouteParserOsrmV5::tr(s: "At the roundabout, continue straight");
564 else
565 return QGeoRouteParserOsrmV5::tr(s: "At the roundabout, continue straight on %1").arg(a: wayName);
566 case QGeoManeuver::DirectionHardLeft:
567 case QGeoManeuver::DirectionLeft:
568 case QGeoManeuver::DirectionLightLeft:
569 case QGeoManeuver::DirectionBearLeft:
570 if (wayName.isEmpty())
571 return QGeoRouteParserOsrmV5::tr(s: "At the roundabout, turn left");
572 else
573 return QGeoRouteParserOsrmV5::tr(s: "At the roundabout, turn left onto %1").arg(a: wayName);
574 case QGeoManeuver::DirectionHardRight:
575 case QGeoManeuver::DirectionRight:
576 case QGeoManeuver::DirectionLightRight:
577 case QGeoManeuver::DirectionBearRight:
578 if (wayName.isEmpty())
579 return QGeoRouteParserOsrmV5::tr(s: "At the roundabout, turn right");
580 else
581 return QGeoRouteParserOsrmV5::tr(s: "At the roundabout, turn right onto %1").arg(a: wayName);
582 case QGeoManeuver::DirectionUTurnLeft:
583 case QGeoManeuver::DirectionUTurnRight:
584 if (wayName.isEmpty())
585 return QGeoRouteParserOsrmV5::tr(s: "At the roundabout, turn around");
586 else
587 return QGeoRouteParserOsrmV5::tr(s: "At the roundabout, turn around onto %1").arg(a: wayName);
588 default:
589 if (wayName.isEmpty())
590 return QGeoRouteParserOsrmV5::tr(s: "At the roundabout, continue");
591 else
592 return QGeoRouteParserOsrmV5::tr(s: "At the roundabout, continue onto %1").arg(a: wayName);
593 }
594}
595
596static QString instructionTrain(const QString &wayName)
597{
598 return wayName.isEmpty()
599 ? QGeoRouteParserOsrmV5::tr(s: "Take the train")
600 : QGeoRouteParserOsrmV5::tr(s: "Take the train [%1]").arg(a: wayName);
601}
602
603static QString instructionTurn(const QString &wayName, QGeoManeuver::InstructionDirection direction)
604{
605 switch (direction) {
606 case QGeoManeuver::DirectionForward:
607 if (wayName.isEmpty())
608 return QGeoRouteParserOsrmV5::tr(s: "Go straight");
609 else
610 return QGeoRouteParserOsrmV5::tr(s: "Go straight onto %1").arg(a: wayName);
611 case QGeoManeuver::DirectionHardLeft:
612 case QGeoManeuver::DirectionLeft:
613 if (wayName.isEmpty())
614 return QGeoRouteParserOsrmV5::tr(s: "Turn left");
615 else
616 return QGeoRouteParserOsrmV5::tr(s: "Turn left onto %1").arg(a: wayName);
617 case QGeoManeuver::DirectionLightLeft:
618 case QGeoManeuver::DirectionBearLeft:
619 if (wayName.isEmpty())
620 return QGeoRouteParserOsrmV5::tr(s: "Turn slightly left");
621 else
622 return QGeoRouteParserOsrmV5::tr(s: "Turn slightly left onto %1").arg(a: wayName);
623 case QGeoManeuver::DirectionHardRight:
624 case QGeoManeuver::DirectionRight:
625 if (wayName.isEmpty())
626 return QGeoRouteParserOsrmV5::tr(s: "Turn right");
627 else
628 return QGeoRouteParserOsrmV5::tr(s: "Turn right onto %1").arg(a: wayName);
629 case QGeoManeuver::DirectionLightRight:
630 case QGeoManeuver::DirectionBearRight:
631 if (wayName.isEmpty())
632 return QGeoRouteParserOsrmV5::tr(s: "Turn slightly right");
633 else
634 return QGeoRouteParserOsrmV5::tr(s: "Turn slightly right onto %1").arg(a: wayName);
635 case QGeoManeuver::DirectionUTurnLeft:
636 case QGeoManeuver::DirectionUTurnRight:
637 if (wayName.isEmpty())
638 return QGeoRouteParserOsrmV5::tr(s: "Make a U-turn");
639 else
640 return QGeoRouteParserOsrmV5::tr(s: "Make a U-turn onto %1").arg(a: wayName);
641 default:
642 if (wayName.isEmpty())
643 return QGeoRouteParserOsrmV5::tr(s: "Turn");
644 else
645 return QGeoRouteParserOsrmV5::tr(s: "Turn onto %1").arg(a: wayName);
646 }
647}
648
649static QString instructionUseLane(const QJsonObject &maneuver, const QString &wayName, QGeoManeuver::InstructionDirection direction)
650{
651 QString laneTypes = maneuver.value(key: QLatin1String("laneTypes")).toString();
652 QString laneInstruction;
653 if (laneTypes == QLatin1String("xo") || laneTypes == QLatin1String("xoo") || laneTypes == QLatin1String("xxo"))
654 //: "and <instruction direction> [onto <street name>] will be appended to this string. E.g., "Keep right and make a sharp left"
655 laneInstruction = QLatin1String("Keep right");
656 else if (laneTypes == QLatin1String("ox") || laneTypes == QLatin1String("oox") || laneTypes == QLatin1String("oxx"))
657 laneInstruction = QLatin1String("Keep left");
658 else if (laneTypes == QLatin1String("xox"))
659 laneInstruction = QLatin1String("Use the middle lane");
660 else if (laneTypes == QLatin1String("oxo"))
661 laneInstruction = QLatin1String("Use the left or the right lane");
662
663 if (laneInstruction.isEmpty()) {
664 if (wayName.isEmpty())
665 return QGeoRouteParserOsrmV5::tr(s: "Continue straight");
666 else
667 return QGeoRouteParserOsrmV5::tr(s: "Continue straight onto %1").arg(a: wayName);
668 }
669
670 switch (direction) {
671 case QGeoManeuver::DirectionForward:
672 if (wayName.isEmpty())
673 //: This string will be prepended with lane instructions. E.g., "Use the left or the right lane and continue straight"
674 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and continue straight");
675 else
676 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and continue straight onto %1").arg(a: wayName);
677 case QGeoManeuver::DirectionHardLeft:
678 if (wayName.isEmpty())
679 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and make a sharp left");
680 else
681 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and make a sharp left onto %1").arg(a: wayName);
682 case QGeoManeuver::DirectionLeft:
683 if (wayName.isEmpty())
684 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and turn left");
685 else
686 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and turn left onto %1").arg(a: wayName);
687 case QGeoManeuver::DirectionLightLeft:
688 case QGeoManeuver::DirectionBearLeft:
689 if (wayName.isEmpty())
690 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and make a slight left");
691 else
692 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and make a slight left onto %1").arg(a: wayName);
693 case QGeoManeuver::DirectionHardRight:
694 if (wayName.isEmpty())
695 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and make a sharp right");
696 else
697 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and make a sharp right onto %1").arg(a: wayName);
698 case QGeoManeuver::DirectionRight:
699 if (wayName.isEmpty())
700 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and turn right");
701 else
702 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and turn right onto %1").arg(a: wayName);
703 case QGeoManeuver::DirectionLightRight:
704 case QGeoManeuver::DirectionBearRight:
705 if (wayName.isEmpty())
706 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and make a slight right");
707 else
708 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and make a slight right onto %1").arg(a: wayName);
709 case QGeoManeuver::DirectionUTurnLeft:
710 case QGeoManeuver::DirectionUTurnRight:
711 if (wayName.isEmpty())
712 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and make a U-turn");
713 else
714 return laneInstruction + QGeoRouteParserOsrmV5::tr(s: " and make a U-turn onto %1").arg(a: wayName);
715 default:
716 return laneInstruction;
717 }
718}
719
720static QString instructionText(const QJsonObject &step, const QJsonObject &maneuver, QGeoManeuver::InstructionDirection direction) {
721 QString modifier;
722 if (maneuver.value(key: QLatin1String("modifier")).isString())
723 modifier = maneuver.value(key: QLatin1String("modifier")).toString();
724 QString maneuverType;
725 if (maneuver.value(key: QLatin1String("type")).isString())
726 maneuverType = maneuver.value(key: QLatin1String("type")).toString();
727 QString wayName = QLatin1String("unknown street");
728 if (step.value(key: QLatin1String("name")).isString())
729 wayName = step.value(key: QLatin1String("name")).toString();
730
731
732 if (maneuverType == QLatin1String("arrive"))
733 return instructionArrive(direction);
734 else if (maneuverType == QLatin1String("continue"))
735 return instructionContinue(wayName, direction);
736 else if (maneuverType == QLatin1String("depart"))
737 return instructionDepart(maneuver, wayName);
738 else if (maneuverType == QLatin1String("end of road"))
739 return instructionEndOfRoad(wayName, direction);
740 else if (maneuverType == QLatin1String("ferry"))
741 return instructionFerry(wayName);
742 else if (maneuverType == QLatin1String("fork"))
743 return instructionFork(wayName, direction);
744 else if (maneuverType == QLatin1String("merge"))
745 return instructionMerge(wayName, direction);
746 else if (maneuverType == QLatin1String("new name"))
747 return instructionNewName(wayName, direction);
748 else if (maneuverType == QLatin1String("notification"))
749 return instructionNotification(wayName, direction);
750 else if (maneuverType == QLatin1String("off ramp"))
751 return instructionOffRamp(wayName, direction);
752 else if (maneuverType == QLatin1String("on ramp"))
753 return instructionOnRamp(wayName, direction);
754 else if (maneuverType == QLatin1String("pushing bike"))
755 return instructionPushingBike(wayName);
756 else if (maneuverType == QLatin1String("rotary"))
757 return instructionRotary(step, maneuver, wayName);
758 else if (maneuverType == QLatin1String("roundabout"))
759 return instructionRoundabout(maneuver, wayName);
760 else if (maneuverType == QLatin1String("roundabout turn"))
761 return instructionRoundaboutTurn(wayName, direction);
762 else if (maneuverType == QLatin1String("train"))
763 return instructionTrain(wayName);
764 else if (maneuverType == QLatin1String("turn"))
765 return instructionTurn(wayName, direction);
766 else if (maneuverType == QLatin1String("use lane"))
767 return instructionUseLane(maneuver, wayName, direction);
768 else
769 return maneuverType + QLatin1String(" to/onto ") + wayName;
770}
771
772static QGeoManeuver::InstructionDirection instructionDirection(const QJsonObject &maneuver, QGeoRouteParser::TrafficSide trafficSide)
773{
774 QString modifier;
775 if (maneuver.value(key: QLatin1String("modifier")).isString())
776 modifier = maneuver.value(key: QLatin1String("modifier")).toString();
777
778 if (modifier.isEmpty())
779 return QGeoManeuver::NoDirection;
780 else if (modifier == QLatin1String("straight"))
781 return QGeoManeuver::DirectionForward;
782 else if (modifier == QLatin1String("right"))
783 return QGeoManeuver::DirectionRight;
784 else if (modifier == QLatin1String("sharp right"))
785 return QGeoManeuver::DirectionHardRight;
786 else if (modifier == QLatin1String("slight right"))
787 return QGeoManeuver::DirectionLightRight;
788 else if (modifier == QLatin1String("uturn")) {
789 switch (trafficSide) {
790 case QGeoRouteParser::RightHandTraffic:
791 return QGeoManeuver::DirectionUTurnLeft;
792 case QGeoRouteParser::LeftHandTraffic:
793 return QGeoManeuver::DirectionUTurnRight;
794 }
795 return QGeoManeuver::DirectionUTurnLeft;
796 } else if (modifier == QLatin1String("left"))
797 return QGeoManeuver::DirectionLeft;
798 else if (modifier == QLatin1String("sharp left"))
799 return QGeoManeuver::DirectionHardLeft;
800 else if (modifier == QLatin1String("slight left"))
801 return QGeoManeuver::DirectionLightLeft;
802 else
803 return QGeoManeuver::NoDirection;
804}
805
806class QGeoRouteParserOsrmV5Private : public QGeoRouteParserPrivate
807{
808 Q_DECLARE_PUBLIC(QGeoRouteParserOsrmV5)
809public:
810 QGeoRouteParserOsrmV5Private();
811 virtual ~QGeoRouteParserOsrmV5Private();
812
813 QGeoRouteSegment parseStep(const QJsonObject &step, int legIndex, int stepIndex) const;
814
815 // QGeoRouteParserPrivate
816
817 QGeoRouteReply::Error parseReply(QList<QGeoRoute> &routes, QString &errorString, const QByteArray &reply) const override;
818 QUrl requestUrl(const QGeoRouteRequest &request, const QString &prefix) const override;
819
820 QVariantMap m_vendorParams;
821 const QGeoRouteParserOsrmV5Extension *m_extension = nullptr;
822};
823
824QGeoRouteParserOsrmV5Private::QGeoRouteParserOsrmV5Private()
825 : QGeoRouteParserPrivate()
826{
827}
828
829QGeoRouteParserOsrmV5Private::~QGeoRouteParserOsrmV5Private()
830{
831 delete m_extension;
832}
833
834QGeoRouteSegment QGeoRouteParserOsrmV5Private::parseStep(const QJsonObject &step, int legIndex, int stepIndex) const {
835 // OSRM Instructions documentation: https://github.com/Project-OSRM/osrm-text-instructions
836 // This goes on top of OSRM: https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md
837 // Mapbox however, includes this in the reply, under "instruction".
838 QGeoRouteSegment segment;
839 if (!step.value(key: QLatin1String("maneuver")).isObject())
840 return segment;
841 QJsonObject maneuver = step.value(key: QLatin1String("maneuver")).toObject();
842 if (!step.value(key: QLatin1String("duration")).isDouble())
843 return segment;
844 if (!step.value(key: QLatin1String("distance")).isDouble())
845 return segment;
846 if (!step.value(key: QLatin1String("intersections")).isArray())
847 return segment;
848 if (!maneuver.value(key: QLatin1String("location")).isArray())
849 return segment;
850
851 double time = step.value(key: QLatin1String("duration")).toDouble();
852 double distance = step.value(key: QLatin1String("distance")).toDouble();
853
854 QJsonArray position = maneuver.value(key: QLatin1String("location")).toArray();
855 if (position.isEmpty())
856 return segment;
857 double latitude = position[1].toDouble();
858 double longitude = position[0].toDouble();
859 QGeoCoordinate coord(latitude, longitude);
860
861 QString geometry = step.value(key: QLatin1String("geometry")).toString();
862 QList<QGeoCoordinate> path = decodePolyline(polylineString: geometry);
863
864 QGeoManeuver::InstructionDirection maneuverInstructionDirection = instructionDirection(maneuver, trafficSide);
865
866 QString maneuverInstructionText = instructionText(step, maneuver, direction: maneuverInstructionDirection);
867
868 QGeoManeuver geoManeuver;
869 geoManeuver.setDirection(maneuverInstructionDirection);
870 geoManeuver.setDistanceToNextInstruction(distance);
871 geoManeuver.setTimeToNextInstruction(time);
872 geoManeuver.setInstructionText(maneuverInstructionText);
873 geoManeuver.setPosition(coord);
874 geoManeuver.setWaypoint(coord);
875
876 QVariantMap extraAttributes;
877 static const QStringList extras {
878 QLatin1String("bearing_before"),
879 QLatin1String("bearing_after"),
880 QLatin1String("instruction"),
881 QLatin1String("type"),
882 QLatin1String("modifier") };
883 for (const QString &e: extras) {
884 if (maneuver.find(key: e) != maneuver.end())
885 extraAttributes.insert(akey: e, avalue: maneuver.value(key: e).toVariant());
886 }
887 // These should be removed as soon as route leg support is introduced.
888 // Ref: http://project-osrm.org/docs/v5.15.2/api/#routeleg-object
889 extraAttributes.insert(akey: QLatin1String("leg_index"), avalue: legIndex);
890 extraAttributes.insert(akey: QLatin1String("step_index"), avalue: stepIndex);
891
892 geoManeuver.setExtendedAttributes(extraAttributes);
893
894 segment.setDistance(distance);
895 segment.setPath(path);
896 segment.setTravelTime(time);
897 segment.setManeuver(geoManeuver);
898 if (m_extension)
899 m_extension->updateSegment(segment, step, maneuver);
900 return segment;
901}
902
903QGeoRouteReply::Error QGeoRouteParserOsrmV5Private::parseReply(QList<QGeoRoute> &routes, QString &errorString, const QByteArray &reply) const
904{
905 // OSRM v5 specs: https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md
906 // Mapbox Directions API spec: https://www.mapbox.com/api-documentation/#directions
907 QJsonDocument document = QJsonDocument::fromJson(json: reply);
908 if (document.isObject()) {
909 QJsonObject object = document.object();
910
911 QString status = object.value(key: QLatin1String("code")).toString();
912 if (status != QLatin1String("Ok")) {
913 errorString = status;
914 return QGeoRouteReply::UnknownError;
915 }
916 if (!object.value(key: QLatin1String("routes")).isArray()) {
917 errorString = QLatin1String("No routes found");
918 return QGeoRouteReply::ParseError;
919 }
920
921 QJsonArray osrmRoutes = object.value(key: QLatin1String("routes")).toArray();
922 foreach (const QJsonValue &r, osrmRoutes) {
923 if (!r.isObject())
924 continue;
925 QJsonObject routeObject = r.toObject();
926 if (!routeObject.value(key: QLatin1String("legs")).isArray())
927 continue;
928 if (!routeObject.value(key: QLatin1String("duration")).isDouble())
929 continue;
930 if (!routeObject.value(key: QLatin1String("distance")).isDouble())
931 continue;
932
933 double distance = routeObject.value(key: QLatin1String("distance")).toDouble();
934 double travelTime = routeObject.value(key: QLatin1String("duration")).toDouble();
935 bool error = false;
936 QList<QGeoRouteSegment> segments;
937
938 QJsonArray legs = routeObject.value(key: QLatin1String("legs")).toArray();
939 QList<QGeoRouteLeg> routeLegs;
940 QGeoRoute route;
941 for (int legIndex = 0; legIndex < legs.size(); ++legIndex) {
942 const QJsonValue &l = legs.at(i: legIndex);
943 QGeoRouteLeg routeLeg;
944 QList<QGeoRouteSegment> legSegments;
945 if (!l.isObject()) { // invalid leg record
946 error = true;
947 break;
948 }
949 QJsonObject leg = l.toObject();
950 if (!leg.value(key: QLatin1String("steps")).isArray()) { // Invalid steps field
951 error = true;
952 break;
953 }
954 const double legDistance = leg.value(key: QLatin1String("distance")).toDouble();
955 const double legTravelTime = leg.value(key: QLatin1String("duration")).toDouble();
956 QJsonArray steps = leg.value(key: QLatin1String("steps")).toArray();
957 QGeoRouteSegment segment;
958 for (int stepIndex = 0; stepIndex < steps.size(); ++stepIndex) {
959 const QJsonValue &s = steps.at(i: stepIndex);
960 if (!s.isObject()) {
961 error = true;
962 break;
963 }
964 segment = parseStep(step: s.toObject(), legIndex, stepIndex);
965 if (segment.isValid()) {
966 // setNextRouteSegment done below for all segments in the route.
967 legSegments.append(t: segment);
968 } else {
969 error = true;
970 break;
971 }
972 }
973 if (error)
974 break;
975
976 QGeoRouteSegmentPrivate *segmentPrivate = QGeoRouteSegmentPrivate::get(segment);
977 segmentPrivate->setLegLastSegment(true);
978 QList<QGeoCoordinate> path;
979 for (const QGeoRouteSegment &s: qAsConst(t&: legSegments))
980 path.append(t: s.path());
981 routeLeg.setLegIndex(legIndex);
982 routeLeg.setOverallRoute(route); // QGeoRoute::d_ptr is explicitlySharedDataPointer. Modifiers below won't detach it.
983 routeLeg.setDistance(legDistance);
984 routeLeg.setTravelTime(legTravelTime);
985 if (!path.isEmpty()) {
986 routeLeg.setPath(path);
987 routeLeg.setFirstRouteSegment(legSegments.first());
988 }
989 routeLegs << routeLeg;
990
991 segments.append(t: legSegments);
992 }
993
994 if (!error) {
995 QList<QGeoCoordinate> path;
996 foreach (const QGeoRouteSegment &s, segments)
997 path.append(t: s.path());
998
999 for (int i = segments.size() - 1; i > 0; --i)
1000 segments[i-1].setNextRouteSegment(segments[i]);
1001
1002 route.setDistance(distance);
1003 route.setTravelTime(travelTime);
1004 if (!path.isEmpty()) {
1005 route.setPath(path);
1006 route.setBounds(QGeoPath(path).boundingGeoRectangle());
1007 route.setFirstRouteSegment(segments.first());
1008 }
1009 route.setRouteLegs(routeLegs);
1010 //r.setTravelMode(QGeoRouteRequest::CarTravel); // The only one supported by OSRM demo service, but other OSRM servers might do cycle or pedestrian too
1011 routes.append(t: route);
1012 }
1013 }
1014
1015 // setError(QGeoRouteReply::NoError, status); // can't do this, or NoError is emitted and does damages
1016 return QGeoRouteReply::NoError;
1017 } else {
1018 errorString = QLatin1String("Couldn't parse json.");
1019 return QGeoRouteReply::ParseError;
1020 }
1021}
1022
1023QUrl QGeoRouteParserOsrmV5Private::requestUrl(const QGeoRouteRequest &request, const QString &prefix) const
1024{
1025 QString routingUrl = prefix;
1026 int notFirst = 0;
1027 QString bearings;
1028 const QList<QVariantMap> metadata = request.waypointsMetadata();
1029 const QList<QGeoCoordinate> waypoints = request.waypoints();
1030 for (int i = 0; i < waypoints.size(); i++) {
1031 const QGeoCoordinate &c = waypoints.at(i);
1032 if (notFirst) {
1033 routingUrl.append(c: QLatin1Char(';'));
1034 bearings.append(c: QLatin1Char(';'));
1035 }
1036 routingUrl.append(s: QString::number(c.longitude(), f: 'f', prec: 7)).append(c: QLatin1Char(',')).append(s: QString::number(c.latitude(), f: 'f', prec: 7));
1037 if (metadata.size() > i) {
1038 const QVariantMap &meta = metadata.at(i);
1039 if (meta.contains(akey: QLatin1String("bearing"))) {
1040 qreal bearing = meta.value(akey: QLatin1String("bearing")).toDouble();
1041 bearings.append(s: QString::number(int(bearing))).append(c: QLatin1Char(',')).append(s: QLatin1String("90")); // 90 is the angle of maneuver allowed.
1042 } else {
1043 bearings.append(s: QLatin1String("0,180")); // 180 here means anywhere
1044 }
1045 }
1046 ++notFirst;
1047 }
1048
1049 QUrl url(routingUrl);
1050 QUrlQuery query;
1051 query.addQueryItem(key: QLatin1String("overview"), value: QLatin1String("full"));
1052 query.addQueryItem(key: QLatin1String("steps"), value: QLatin1String("true"));
1053 query.addQueryItem(key: QLatin1String("geometries"), value: QLatin1String("polyline6"));
1054 query.addQueryItem(key: QLatin1String("alternatives"), value: QLatin1String("true"));
1055 query.addQueryItem(key: QLatin1String("bearings"), value: bearings);
1056 if (m_extension)
1057 m_extension->updateQuery(query);
1058 url.setQuery(query);
1059 return url;
1060}
1061
1062QGeoRouteParserOsrmV5::QGeoRouteParserOsrmV5(QObject *parent)
1063 : QGeoRouteParser(*new QGeoRouteParserOsrmV5Private(), parent)
1064{
1065}
1066
1067QGeoRouteParserOsrmV5::~QGeoRouteParserOsrmV5()
1068{
1069}
1070
1071void QGeoRouteParserOsrmV5::setExtension(const QGeoRouteParserOsrmV5Extension *extension)
1072{
1073 Q_D(QGeoRouteParserOsrmV5);
1074 if (extension)
1075 d->m_extension = extension;
1076}
1077
1078QT_END_NAMESPACE
1079

source code of qtlocation/src/location/maps/qgeorouteparserosrmv5.cpp