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 
53 QT_BEGIN_NAMESPACE
54 
decodePolyline(const QString & polylineString)55 static 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(coord);
87         }
88 
89         parsingLatitude = !parsingLatitude;
90 
91         value = 0;
92         shift = 0;
93     }
94 
95     return path;
96 }
97 
cardinalDirection4(QLocationUtils::CardinalDirection direction)98 static 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("North");
105         case QLocationUtils::CardinalE:
106             return QGeoRouteParserOsrmV5::tr("East");
107         case QLocationUtils::CardinalS:
108             return QGeoRouteParserOsrmV5::tr("South");
109         case QLocationUtils::CardinalW:
110             return QGeoRouteParserOsrmV5::tr("West");
111         default:
112             return QString();
113     }
114 }
115 
exitOrdinal(int exit)116 static QString exitOrdinal(int exit)
117 {
118     static QList<QString> ordinals;
119 
120     if (!ordinals.size()) {
121         ordinals.append(QLatin1String(""));
122         //: always used in " and take the %1 exit [onto <street name>]"
123         ordinals.append(QGeoRouteParserOsrmV5::tr("first", "roundabout exit"));
124         ordinals.append(QGeoRouteParserOsrmV5::tr("second", "roundabout exit"));
125         ordinals.append(QGeoRouteParserOsrmV5::tr("third", "roundabout exit"));
126         ordinals.append(QGeoRouteParserOsrmV5::tr("fourth", "roundabout exit"));
127         ordinals.append(QGeoRouteParserOsrmV5::tr("fifth", "roundabout exit"));
128         ordinals.append(QGeoRouteParserOsrmV5::tr("sixth", "roundabout exit"));
129         ordinals.append(QGeoRouteParserOsrmV5::tr("seventh", "roundabout exit"));
130         ordinals.append(QGeoRouteParserOsrmV5::tr("eighth", "roundabout exit"));
131         ordinals.append(QGeoRouteParserOsrmV5::tr("ninth", "roundabout exit"));
132         ordinals.append(QGeoRouteParserOsrmV5::tr("tenth", "roundabout exit"));
133         ordinals.append(QGeoRouteParserOsrmV5::tr("eleventh", "roundabout exit"));
134         ordinals.append(QGeoRouteParserOsrmV5::tr("twelfth", "roundabout exit"));
135         ordinals.append(QGeoRouteParserOsrmV5::tr("thirteenth", "roundabout exit"));
136         ordinals.append(QGeoRouteParserOsrmV5::tr("fourteenth", "roundabout exit"));
137         ordinals.append(QGeoRouteParserOsrmV5::tr("fifteenth", "roundabout exit"));
138         ordinals.append(QGeoRouteParserOsrmV5::tr("sixteenth", "roundabout exit"));
139         ordinals.append(QGeoRouteParserOsrmV5::tr("seventeenth", "roundabout exit"));
140         ordinals.append(QGeoRouteParserOsrmV5::tr("eighteenth", "roundabout exit"));
141         ordinals.append(QGeoRouteParserOsrmV5::tr("nineteenth", "roundabout exit"));
142         ordinals.append(QGeoRouteParserOsrmV5::tr("twentieth", "roundabout exit"));
143     };
144 
145     if (exit < 1 || exit > ordinals.size())
146         return QString();
147     return ordinals[exit];
148 }
149 
exitDirection(int exit,const QString & wayName)150 static 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(" and take the %1 exit");
158     static QString directionExitOnto = QGeoRouteParserOsrmV5::tr(" 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(exitOrdinal(exit));
164     else
165         return directionExitOnto.arg(exitOrdinal(exit), wayName);
166 }
167 
instructionArrive(QGeoManeuver::InstructionDirection direction)168 static QString instructionArrive(QGeoManeuver::InstructionDirection direction)
169 {
170     switch (direction) {
171         case QGeoManeuver::DirectionForward:
172             return QGeoRouteParserOsrmV5::tr("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("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("You have arrived at your destination, on the right");
185         default:
186             return QGeoRouteParserOsrmV5::tr("You have arrived at your destination");
187     }
188 }
189 
instructionContinue(const QString & wayName,QGeoManeuver::InstructionDirection direction)190 static QString instructionContinue(const QString &wayName, QGeoManeuver::InstructionDirection direction)
191 {
192     switch (direction) {
193         case QGeoManeuver::DirectionForward:
194             if (wayName.isEmpty())
195                 return QGeoRouteParserOsrmV5::tr("Continue straight");
196             else
197                 return QGeoRouteParserOsrmV5::tr("Continue straight on %1").arg(wayName);
198         case QGeoManeuver::DirectionHardLeft:
199         case QGeoManeuver::DirectionLeft:
200             if (wayName.isEmpty())
201                 return QGeoRouteParserOsrmV5::tr("Continue left");
202             else
203                 return QGeoRouteParserOsrmV5::tr("Continue left onto %1").arg(wayName);
204         case QGeoManeuver::DirectionLightLeft:
205         case QGeoManeuver::DirectionBearLeft:
206             if (wayName.isEmpty())
207                 return QGeoRouteParserOsrmV5::tr("Continue slightly left");
208             else
209                 return QGeoRouteParserOsrmV5::tr("Continue slightly left on %1").arg(wayName);
210         case QGeoManeuver::DirectionHardRight:
211         case QGeoManeuver::DirectionRight:
212             if (wayName.isEmpty())
213                 return QGeoRouteParserOsrmV5::tr("Continue right");
214             else
215                 return QGeoRouteParserOsrmV5::tr("Continue right onto %1").arg(wayName);
216         case QGeoManeuver::DirectionLightRight:
217         case QGeoManeuver::DirectionBearRight:
218             if (wayName.isEmpty())
219                 return QGeoRouteParserOsrmV5::tr("Continue slightly right");
220             else
221                 return QGeoRouteParserOsrmV5::tr("Continue slightly right on %1").arg(wayName);
222         case QGeoManeuver::DirectionUTurnLeft:
223         case QGeoManeuver::DirectionUTurnRight:
224             if (wayName.isEmpty())
225                 return QGeoRouteParserOsrmV5::tr("Make a U-turn");
226             else
227                 return QGeoRouteParserOsrmV5::tr("Make a U-turn onto %1").arg(wayName);
228         default:
229             if (wayName.isEmpty())
230                 return QGeoRouteParserOsrmV5::tr("Continue");
231             else
232                 return QGeoRouteParserOsrmV5::tr("Continue on %1").arg(wayName);
233     }
234 }
235 
instructionDepart(const QJsonObject & maneuver,const QString & wayName)236 static QString instructionDepart(const QJsonObject &maneuver, const QString &wayName)
237 {
238     double bearing = maneuver.value(QLatin1String("bearing_after")).toDouble(-1.0);
239     if (bearing >= 0.0) {
240         if (wayName.isEmpty())
241             //: %1 is "North", "South", "East" or "West"
242             return QGeoRouteParserOsrmV5::tr("Head %1").arg(cardinalDirection4(QLocationUtils::azimuthToCardinalDirection4(bearing)));
243         else
244             return QGeoRouteParserOsrmV5::tr("Head %1 onto %2").arg(cardinalDirection4(QLocationUtils::azimuthToCardinalDirection4(bearing)), wayName);
245     } else {
246         if (wayName.isEmpty())
247             return QGeoRouteParserOsrmV5::tr("Depart");
248         else
249             return QGeoRouteParserOsrmV5::tr("Depart onto %1").arg(wayName);
250     }
251 }
252 
instructionEndOfRoad(const QString & wayName,QGeoManeuver::InstructionDirection direction)253 static 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("At the end of the road, turn left");
262             else
263                 return QGeoRouteParserOsrmV5::tr("At the end of the road, turn left onto %1").arg(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("At the end of the road, turn right");
270             else
271                 return QGeoRouteParserOsrmV5::tr("At the end of the road, turn right onto %1").arg(wayName);
272         case QGeoManeuver::DirectionUTurnLeft:
273         case QGeoManeuver::DirectionUTurnRight:
274             if (wayName.isEmpty())
275                 return QGeoRouteParserOsrmV5::tr("At the end of the road, make a U-turn");
276             else
277                 return QGeoRouteParserOsrmV5::tr("At the end of the road, make a U-turn onto %1").arg(wayName);
278         case QGeoManeuver::DirectionForward:
279             if (wayName.isEmpty())
280                 return QGeoRouteParserOsrmV5::tr("At the end of the road, continue straight");
281             else
282                 return QGeoRouteParserOsrmV5::tr("At the end of the road, continue straight onto %1").arg(wayName);
283         default:
284             if (wayName.isEmpty())
285                 return QGeoRouteParserOsrmV5::tr("At the end of the road, continue");
286             else
287                 return QGeoRouteParserOsrmV5::tr("At the end of the road, continue onto %1").arg(wayName);
288     }
289 }
290 
instructionFerry(const QString & wayName)291 static QString instructionFerry(const QString &wayName)
292 {
293     QString instruction = QGeoRouteParserOsrmV5::tr("Take the ferry");
294     if (!wayName.isEmpty())
295         instruction += QLatin1String(" [") + wayName + QLatin1Char(']');
296 
297     return instruction;
298 }
299 
instructionFork(const QString & wayName,QGeoManeuver::InstructionDirection direction)300 static QString instructionFork(const QString &wayName, QGeoManeuver::InstructionDirection direction)
301 {
302     switch (direction) {
303         case QGeoManeuver::DirectionHardLeft:
304             if (wayName.isEmpty())
305                 return QGeoRouteParserOsrmV5::tr("At the fork, take a sharp left");
306             else
307                 return QGeoRouteParserOsrmV5::tr("At the fork, take a sharp left onto %1").arg(wayName);
308         case QGeoManeuver::DirectionLeft:
309             if (wayName.isEmpty())
310                 return QGeoRouteParserOsrmV5::tr("At the fork, turn left");
311             else
312                 return QGeoRouteParserOsrmV5::tr("At the fork, turn left onto %1").arg(wayName);
313         case QGeoManeuver::DirectionLightLeft:
314         case QGeoManeuver::DirectionBearLeft:
315             if (wayName.isEmpty())
316                 return QGeoRouteParserOsrmV5::tr("At the fork, keep left");
317             else
318                 return QGeoRouteParserOsrmV5::tr("At the fork, keep left onto %1").arg(wayName);
319         case QGeoManeuver::DirectionHardRight:
320             if (wayName.isEmpty())
321                 return QGeoRouteParserOsrmV5::tr("At the fork, take a sharp right");
322             else
323                 return QGeoRouteParserOsrmV5::tr("At the fork, take a sharp right onto %1").arg(wayName);
324         case QGeoManeuver::DirectionRight:
325             if (wayName.isEmpty())
326                 return QGeoRouteParserOsrmV5::tr("At the fork, turn right");
327             else
328                 return QGeoRouteParserOsrmV5::tr("At the fork, turn right onto %1").arg(wayName);
329         case QGeoManeuver::DirectionLightRight:
330         case QGeoManeuver::DirectionBearRight:
331             if (wayName.isEmpty())
332                 return QGeoRouteParserOsrmV5::tr("At the fork, keep right");
333             else
334                 return QGeoRouteParserOsrmV5::tr("At the fork, keep right onto %1").arg(wayName);
335         case QGeoManeuver::DirectionUTurnLeft:
336         case QGeoManeuver::DirectionUTurnRight:
337             if (wayName.isEmpty())
338                 return QGeoRouteParserOsrmV5::tr("Make a U-turn");
339             else
340                 return QGeoRouteParserOsrmV5::tr("Make a U-turn onto %1").arg(wayName);
341         case QGeoManeuver::DirectionForward:
342             if (wayName.isEmpty())
343                 return QGeoRouteParserOsrmV5::tr("At the fork, continue straight ahead");
344             else
345                 return QGeoRouteParserOsrmV5::tr("At the fork, continue straight ahead onto %1").arg(wayName);
346         default:
347             if (wayName.isEmpty())
348                 return QGeoRouteParserOsrmV5::tr("At the fork, continue");
349             else
350                 return QGeoRouteParserOsrmV5::tr("At the fork, continue onto %1").arg(wayName);
351     }
352 }
353 
instructionMerge(const QString & wayName,QGeoManeuver::InstructionDirection direction)354 static 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("Merge sharply left");
361             else
362                 return QGeoRouteParserOsrmV5::tr("Merge sharply left onto %1").arg(wayName);
363         case QGeoManeuver::DirectionLeft:
364             if (wayName.isEmpty())
365                 return QGeoRouteParserOsrmV5::tr("Merge left");
366             else
367                 return QGeoRouteParserOsrmV5::tr("Merge left onto %1").arg(wayName);
368         case QGeoManeuver::DirectionLightLeft:
369         case QGeoManeuver::DirectionBearLeft:
370             if (wayName.isEmpty())
371                 return QGeoRouteParserOsrmV5::tr("Merge slightly left");
372             else
373                 return QGeoRouteParserOsrmV5::tr("Merge slightly left on %1").arg(wayName);
374         case QGeoManeuver::DirectionUTurnRight:
375         case QGeoManeuver::DirectionHardRight:
376             if (wayName.isEmpty())
377                 return QGeoRouteParserOsrmV5::tr("Merge sharply right");
378             else
379                 return QGeoRouteParserOsrmV5::tr("Merge sharply right onto %1").arg(wayName);
380         case QGeoManeuver::DirectionRight:
381             if (wayName.isEmpty())
382                 return QGeoRouteParserOsrmV5::tr("Merge right");
383             else
384                 return QGeoRouteParserOsrmV5::tr("Merge right onto %1").arg(wayName);
385         case QGeoManeuver::DirectionLightRight:
386         case QGeoManeuver::DirectionBearRight:
387             if (wayName.isEmpty())
388                 return QGeoRouteParserOsrmV5::tr("Merge slightly right");
389             else
390                 return QGeoRouteParserOsrmV5::tr("Merge slightly right on %1").arg(wayName);
391         case QGeoManeuver::DirectionForward:
392             if (wayName.isEmpty())
393                 return QGeoRouteParserOsrmV5::tr("Merge straight");
394             else
395                 return QGeoRouteParserOsrmV5::tr("Merge straight on %1").arg(wayName);
396         default:
397             if (wayName.isEmpty())
398                 return QGeoRouteParserOsrmV5::tr("Merge");
399             else
400                 return QGeoRouteParserOsrmV5::tr("Merge onto %1").arg(wayName);
401     }
402 }
403 
instructionNewName(const QString & wayName,QGeoManeuver::InstructionDirection direction)404 static QString instructionNewName(const QString &wayName, QGeoManeuver::InstructionDirection direction)
405 {
406     switch (direction) {
407         case QGeoManeuver::DirectionHardLeft:
408             if (wayName.isEmpty())
409                 return QGeoRouteParserOsrmV5::tr("Take a sharp left");
410             else
411                 return QGeoRouteParserOsrmV5::tr("Take a sharp left onto %1").arg(wayName);
412         case QGeoManeuver::DirectionLeft:
413             if (wayName.isEmpty())
414                 return QGeoRouteParserOsrmV5::tr("Turn left");
415             else
416                 return QGeoRouteParserOsrmV5::tr("Turn left onto %1").arg(wayName);
417         case QGeoManeuver::DirectionLightLeft:
418         case QGeoManeuver::DirectionBearLeft:
419             if (wayName.isEmpty())
420                 return QGeoRouteParserOsrmV5::tr("Continue slightly left");
421             else
422                 return QGeoRouteParserOsrmV5::tr("Continue slightly left onto %1").arg(wayName);
423         case QGeoManeuver::DirectionHardRight:
424             if (wayName.isEmpty())
425                 return QGeoRouteParserOsrmV5::tr("Take a sharp right");
426             else
427                 return QGeoRouteParserOsrmV5::tr("Take a sharp right onto %1").arg(wayName);
428         case QGeoManeuver::DirectionRight:
429             if (wayName.isEmpty())
430                 return QGeoRouteParserOsrmV5::tr("Turn right");
431             else
432                 return QGeoRouteParserOsrmV5::tr("Turn right onto %1").arg(wayName);
433         case QGeoManeuver::DirectionLightRight:
434         case QGeoManeuver::DirectionBearRight:
435             if (wayName.isEmpty())
436                 return QGeoRouteParserOsrmV5::tr("Continue slightly right");
437             else
438                 return QGeoRouteParserOsrmV5::tr("Continue slightly right onto %1").arg(wayName);
439         case QGeoManeuver::DirectionUTurnLeft:
440         case QGeoManeuver::DirectionUTurnRight:
441             if (wayName.isEmpty())
442                 return QGeoRouteParserOsrmV5::tr("Make a U-turn");
443             else
444                 return QGeoRouteParserOsrmV5::tr("Make a U-turn onto %1").arg(wayName);
445         case QGeoManeuver::DirectionForward:
446             if (wayName.isEmpty())
447                 return QGeoRouteParserOsrmV5::tr("Continue straight");
448             else
449                 return QGeoRouteParserOsrmV5::tr("Continue straight onto %1").arg(wayName);
450         default:
451             if (wayName.isEmpty())
452                 return QGeoRouteParserOsrmV5::tr("Continue");
453             else
454                 return QGeoRouteParserOsrmV5::tr("Continue onto %1").arg(wayName);
455     }
456 }
457 
instructionNotification(const QString & wayName,QGeoManeuver::InstructionDirection direction)458 static 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("Continue on the left");
468             else
469                 return QGeoRouteParserOsrmV5::tr("Continue on the left on %1").arg(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("Continue on the right");
477             else
478                 return QGeoRouteParserOsrmV5::tr("Continue on the right on %1").arg(wayName);
479         case QGeoManeuver::DirectionForward:
480         default:
481             if (wayName.isEmpty())
482                 return QGeoRouteParserOsrmV5::tr("Continue");
483             else
484                 return QGeoRouteParserOsrmV5::tr("Continue on %1").arg(wayName);
485     }
486 }
487 
instructionOffRamp(const QString & wayName,QGeoManeuver::InstructionDirection direction)488 static 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("Take the ramp on the left");
498             else
499                 return QGeoRouteParserOsrmV5::tr("Take the ramp on the left onto %1").arg(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("Take the ramp on the right");
507             else
508                 return QGeoRouteParserOsrmV5::tr("Take the ramp on the right onto %1").arg(wayName);
509         case QGeoManeuver::DirectionForward:
510         default:
511             if (wayName.isEmpty())
512                 return QGeoRouteParserOsrmV5::tr("Take the ramp");
513             else
514                 return QGeoRouteParserOsrmV5::tr("Take the ramp onto %1").arg(wayName);
515     }
516 }
517 
instructionOnRamp(const QString & wayName,QGeoManeuver::InstructionDirection direction)518 static QString instructionOnRamp(const QString &wayName, QGeoManeuver::InstructionDirection direction)
519 {
520     return instructionOffRamp(wayName, direction);
521 }
522 
instructionPushingBike(const QString & wayName)523 static QString instructionPushingBike(const QString &wayName)
524 {
525     if (wayName.isEmpty())
526         return QGeoRouteParserOsrmV5::tr("Get off the bike and push");
527     else
528         return QGeoRouteParserOsrmV5::tr("Get off the bike and push onto %1").arg(wayName);
529 }
530 
instructionRotary(const QJsonObject & step,const QJsonObject & maneuver,const QString & wayName)531 static QString instructionRotary(const QJsonObject &step, const QJsonObject &maneuver, const QString &wayName)
532 {
533     QString instruction;
534     QString rotaryName = step.value(QLatin1String("rotary_name")).toString();
535     //QString modifier = maneuver.value(QLatin1String("modifier")).toString(); // Apparently not used for rotaries
536     int exit = maneuver.value(QLatin1String("exit")).toInt(0);
537 
538     //: This string will be prepended to " and take the <nth> exit [onto <streetname>]
539     instruction += QGeoRouteParserOsrmV5::tr("Enter the rotary");
540     if (!rotaryName.isEmpty())
541         instruction +=  QLatin1Char(' ') + rotaryName;
542     instruction += exitDirection(exit, wayName);
543     return instruction;
544 }
545 
instructionRoundabout(const QJsonObject & maneuver,const QString & wayName)546 static 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(QLatin1String("exit")).toInt(0);
551 
552     //: This string will be prepended to " and take the <nth> exit [onto <streetname>]
553     instruction += QGeoRouteParserOsrmV5::tr("Enter the roundabout");
554     instruction += exitDirection(exit, wayName);
555     return instruction;
556 }
557 
instructionRoundaboutTurn(const QString & wayName,QGeoManeuver::InstructionDirection direction)558 static QString instructionRoundaboutTurn(const QString &wayName, QGeoManeuver::InstructionDirection direction)
559 {
560     switch (direction) {
561         case QGeoManeuver::DirectionForward:
562             if (wayName.isEmpty())
563                 return QGeoRouteParserOsrmV5::tr("At the roundabout, continue straight");
564             else
565                 return QGeoRouteParserOsrmV5::tr("At the roundabout, continue straight on %1").arg(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("At the roundabout, turn left");
572             else
573                 return QGeoRouteParserOsrmV5::tr("At the roundabout, turn left onto %1").arg(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("At the roundabout, turn right");
580             else
581                 return QGeoRouteParserOsrmV5::tr("At the roundabout, turn right onto %1").arg(wayName);
582         case QGeoManeuver::DirectionUTurnLeft:
583         case QGeoManeuver::DirectionUTurnRight:
584             if (wayName.isEmpty())
585                 return QGeoRouteParserOsrmV5::tr("At the roundabout, turn around");
586             else
587                 return QGeoRouteParserOsrmV5::tr("At the roundabout, turn around onto %1").arg(wayName);
588         default:
589             if (wayName.isEmpty())
590                 return QGeoRouteParserOsrmV5::tr("At the roundabout, continue");
591             else
592                 return QGeoRouteParserOsrmV5::tr("At the roundabout, continue onto %1").arg(wayName);
593     }
594 }
595 
instructionTrain(const QString & wayName)596 static QString instructionTrain(const QString &wayName)
597 {
598     return wayName.isEmpty()
599         ? QGeoRouteParserOsrmV5::tr("Take the train")
600         : QGeoRouteParserOsrmV5::tr("Take the train [%1]").arg(wayName);
601 }
602 
instructionTurn(const QString & wayName,QGeoManeuver::InstructionDirection direction)603 static QString instructionTurn(const QString &wayName, QGeoManeuver::InstructionDirection direction)
604 {
605     switch (direction) {
606         case QGeoManeuver::DirectionForward:
607             if (wayName.isEmpty())
608                 return QGeoRouteParserOsrmV5::tr("Go straight");
609             else
610                 return QGeoRouteParserOsrmV5::tr("Go straight onto %1").arg(wayName);
611         case QGeoManeuver::DirectionHardLeft:
612         case QGeoManeuver::DirectionLeft:
613             if (wayName.isEmpty())
614                 return QGeoRouteParserOsrmV5::tr("Turn left");
615             else
616                 return QGeoRouteParserOsrmV5::tr("Turn left onto %1").arg(wayName);
617         case QGeoManeuver::DirectionLightLeft:
618         case QGeoManeuver::DirectionBearLeft:
619             if (wayName.isEmpty())
620                 return QGeoRouteParserOsrmV5::tr("Turn slightly left");
621             else
622                 return QGeoRouteParserOsrmV5::tr("Turn slightly left onto %1").arg(wayName);
623         case QGeoManeuver::DirectionHardRight:
624         case QGeoManeuver::DirectionRight:
625             if (wayName.isEmpty())
626                 return QGeoRouteParserOsrmV5::tr("Turn right");
627             else
628                 return QGeoRouteParserOsrmV5::tr("Turn right onto %1").arg(wayName);
629         case QGeoManeuver::DirectionLightRight:
630         case QGeoManeuver::DirectionBearRight:
631             if (wayName.isEmpty())
632                 return QGeoRouteParserOsrmV5::tr("Turn slightly right");
633             else
634                 return QGeoRouteParserOsrmV5::tr("Turn slightly right onto %1").arg(wayName);
635         case QGeoManeuver::DirectionUTurnLeft:
636         case QGeoManeuver::DirectionUTurnRight:
637             if (wayName.isEmpty())
638                 return QGeoRouteParserOsrmV5::tr("Make a U-turn");
639             else
640                 return QGeoRouteParserOsrmV5::tr("Make a U-turn onto %1").arg(wayName);
641         default:
642             if (wayName.isEmpty())
643                 return QGeoRouteParserOsrmV5::tr("Turn");
644             else
645                 return QGeoRouteParserOsrmV5::tr("Turn onto %1").arg(wayName);
646     }
647 }
648 
instructionUseLane(const QJsonObject & maneuver,const QString & wayName,QGeoManeuver::InstructionDirection direction)649 static QString instructionUseLane(const QJsonObject &maneuver, const QString &wayName, QGeoManeuver::InstructionDirection direction)
650 {
651     QString laneTypes = maneuver.value(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("Continue straight");
666         else
667             return QGeoRouteParserOsrmV5::tr("Continue straight onto %1").arg(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(" and continue straight");
675             else
676                 return laneInstruction + QGeoRouteParserOsrmV5::tr(" and continue straight onto %1").arg(wayName);
677         case QGeoManeuver::DirectionHardLeft:
678             if (wayName.isEmpty())
679                 return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a sharp left");
680             else
681                 return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a sharp left onto %1").arg(wayName);
682         case QGeoManeuver::DirectionLeft:
683             if (wayName.isEmpty())
684                 return laneInstruction + QGeoRouteParserOsrmV5::tr(" and turn left");
685             else
686                 return laneInstruction + QGeoRouteParserOsrmV5::tr(" and turn left onto %1").arg(wayName);
687         case QGeoManeuver::DirectionLightLeft:
688         case QGeoManeuver::DirectionBearLeft:
689             if (wayName.isEmpty())
690                 return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a slight left");
691             else
692                 return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a slight left onto %1").arg(wayName);
693         case QGeoManeuver::DirectionHardRight:
694             if (wayName.isEmpty())
695                 return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a sharp right");
696             else
697                 return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a sharp right onto %1").arg(wayName);
698         case QGeoManeuver::DirectionRight:
699             if (wayName.isEmpty())
700                 return laneInstruction + QGeoRouteParserOsrmV5::tr(" and turn right");
701             else
702                 return laneInstruction + QGeoRouteParserOsrmV5::tr(" and turn right onto %1").arg(wayName);
703         case QGeoManeuver::DirectionLightRight:
704         case QGeoManeuver::DirectionBearRight:
705             if (wayName.isEmpty())
706                 return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a slight right");
707             else
708                 return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a slight right onto %1").arg(wayName);
709         case QGeoManeuver::DirectionUTurnLeft:
710         case QGeoManeuver::DirectionUTurnRight:
711             if (wayName.isEmpty())
712                 return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a U-turn");
713             else
714                 return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a U-turn onto %1").arg(wayName);
715         default:
716             return laneInstruction;
717     }
718 }
719 
instructionText(const QJsonObject & step,const QJsonObject & maneuver,QGeoManeuver::InstructionDirection direction)720 static QString instructionText(const QJsonObject &step, const QJsonObject &maneuver, QGeoManeuver::InstructionDirection direction) {
721     QString modifier;
722     if (maneuver.value(QLatin1String("modifier")).isString())
723         modifier = maneuver.value(QLatin1String("modifier")).toString();
724     QString maneuverType;
725     if (maneuver.value(QLatin1String("type")).isString())
726         maneuverType = maneuver.value(QLatin1String("type")).toString();
727     QString wayName = QLatin1String("unknown street");
728     if (step.value(QLatin1String("name")).isString())
729         wayName = step.value(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 
instructionDirection(const QJsonObject & maneuver,QGeoRouteParser::TrafficSide trafficSide)772 static QGeoManeuver::InstructionDirection instructionDirection(const QJsonObject &maneuver, QGeoRouteParser::TrafficSide trafficSide)
773 {
774     QString modifier;
775     if (maneuver.value(QLatin1String("modifier")).isString())
776         modifier = maneuver.value(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 
806 class QGeoRouteParserOsrmV5Private :  public QGeoRouteParserPrivate
807 {
808     Q_DECLARE_PUBLIC(QGeoRouteParserOsrmV5)
809 public:
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 
QGeoRouteParserOsrmV5Private()824 QGeoRouteParserOsrmV5Private::QGeoRouteParserOsrmV5Private()
825     : QGeoRouteParserPrivate()
826 {
827 }
828 
~QGeoRouteParserOsrmV5Private()829 QGeoRouteParserOsrmV5Private::~QGeoRouteParserOsrmV5Private()
830 {
831     delete m_extension;
832 }
833 
parseStep(const QJsonObject & step,int legIndex,int stepIndex) const834 QGeoRouteSegment 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(QLatin1String("maneuver")).isObject())
840         return segment;
841     QJsonObject maneuver = step.value(QLatin1String("maneuver")).toObject();
842     if (!step.value(QLatin1String("duration")).isDouble())
843         return segment;
844     if (!step.value(QLatin1String("distance")).isDouble())
845         return segment;
846     if (!step.value(QLatin1String("intersections")).isArray())
847         return segment;
848     if (!maneuver.value(QLatin1String("location")).isArray())
849         return segment;
850 
851     double time = step.value(QLatin1String("duration")).toDouble();
852     double distance = step.value(QLatin1String("distance")).toDouble();
853 
854     QJsonArray position = maneuver.value(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(QLatin1String("geometry")).toString();
862     QList<QGeoCoordinate> path = decodePolyline(geometry);
863 
864     QGeoManeuver::InstructionDirection maneuverInstructionDirection = instructionDirection(maneuver, trafficSide);
865 
866     QString maneuverInstructionText = instructionText(step, maneuver, 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(e) != maneuver.end())
885             extraAttributes.insert(e, maneuver.value(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(QLatin1String("leg_index"), legIndex);
890     extraAttributes.insert(QLatin1String("step_index"), 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 
parseReply(QList<QGeoRoute> & routes,QString & errorString,const QByteArray & reply) const903 QGeoRouteReply::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(reply);
908     if (document.isObject()) {
909         QJsonObject object = document.object();
910 
911         QString status = object.value(QLatin1String("code")).toString();
912         if (status != QLatin1String("Ok")) {
913             errorString = status;
914             return QGeoRouteReply::UnknownError;
915         }
916         if (!object.value(QLatin1String("routes")).isArray()) {
917             errorString = QLatin1String("No routes found");
918             return QGeoRouteReply::ParseError;
919         }
920 
921         QJsonArray osrmRoutes = object.value(QLatin1String("routes")).toArray();
922         foreach (const QJsonValue &r, osrmRoutes) {
923             if (!r.isObject())
924                 continue;
925             QJsonObject routeObject = r.toObject();
926             if (!routeObject.value(QLatin1String("legs")).isArray())
927                 continue;
928             if (!routeObject.value(QLatin1String("duration")).isDouble())
929                 continue;
930             if (!routeObject.value(QLatin1String("distance")).isDouble())
931                 continue;
932 
933             double distance = routeObject.value(QLatin1String("distance")).toDouble();
934             double travelTime = routeObject.value(QLatin1String("duration")).toDouble();
935             bool error = false;
936             QList<QGeoRouteSegment> segments;
937 
938             QJsonArray legs = routeObject.value(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(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(QLatin1String("steps")).isArray()) { // Invalid steps field
951                     error = true;
952                     break;
953                 }
954                 const double legDistance = leg.value(QLatin1String("distance")).toDouble();
955                 const double legTravelTime = leg.value(QLatin1String("duration")).toDouble();
956                 QJsonArray steps = leg.value(QLatin1String("steps")).toArray();
957                 QGeoRouteSegment segment;
958                 for (int stepIndex = 0; stepIndex < steps.size(); ++stepIndex) {
959                     const QJsonValue &s = steps.at(stepIndex);
960                     if (!s.isObject()) {
961                         error = true;
962                         break;
963                     }
964                     segment = parseStep(s.toObject(), legIndex, stepIndex);
965                     if (segment.isValid()) {
966                         // setNextRouteSegment done below for all segments in the route.
967                         legSegments.append(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(legSegments))
980                     path.append(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(legSegments);
992             }
993 
994             if (!error) {
995                 QList<QGeoCoordinate> path;
996                 foreach (const QGeoRouteSegment &s, segments)
997                     path.append(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(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 
requestUrl(const QGeoRouteRequest & request,const QString & prefix) const1023 QUrl 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(QLatin1Char(';'));
1034             bearings.append(QLatin1Char(';'));
1035         }
1036         routingUrl.append(QString::number(c.longitude(), 'f', 7)).append(QLatin1Char(',')).append(QString::number(c.latitude(), 'f', 7));
1037         if (metadata.size() > i) {
1038             const QVariantMap &meta = metadata.at(i);
1039             if (meta.contains(QLatin1String("bearing"))) {
1040                 qreal bearing = meta.value(QLatin1String("bearing")).toDouble();
1041                 bearings.append(QString::number(int(bearing))).append(QLatin1Char(',')).append(QLatin1String("90")); // 90 is the angle of maneuver allowed.
1042             } else {
1043                 bearings.append(QLatin1String("0,180")); // 180 here means anywhere
1044             }
1045         }
1046         ++notFirst;
1047     }
1048 
1049     QUrl url(routingUrl);
1050     QUrlQuery query;
1051     query.addQueryItem(QLatin1String("overview"), QLatin1String("full"));
1052     query.addQueryItem(QLatin1String("steps"), QLatin1String("true"));
1053     query.addQueryItem(QLatin1String("geometries"), QLatin1String("polyline6"));
1054     query.addQueryItem(QLatin1String("alternatives"), QLatin1String("true"));
1055     query.addQueryItem(QLatin1String("bearings"), bearings);
1056     if (m_extension)
1057         m_extension->updateQuery(query);
1058     url.setQuery(query);
1059     return url;
1060 }
1061 
QGeoRouteParserOsrmV5(QObject * parent)1062 QGeoRouteParserOsrmV5::QGeoRouteParserOsrmV5(QObject *parent)
1063     : QGeoRouteParser(*new QGeoRouteParserOsrmV5Private(), parent)
1064 {
1065 }
1066 
~QGeoRouteParserOsrmV5()1067 QGeoRouteParserOsrmV5::~QGeoRouteParserOsrmV5()
1068 {
1069 }
1070 
setExtension(const QGeoRouteParserOsrmV5Extension * extension)1071 void QGeoRouteParserOsrmV5::setExtension(const QGeoRouteParserOsrmV5Extension *extension)
1072 {
1073     Q_D(QGeoRouteParserOsrmV5);
1074     if (extension)
1075         d->m_extension = extension;
1076 }
1077 
1078 QT_END_NAMESPACE
1079