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