1/****************************************************************************
2**
3** Copyright (C) 2016 Vlad Seryakov <vseryakov@gmail.com>
4** Copyright (C) 2016 Aaron McCarthy <mccarthy.aaron@gmail.com>
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtLocation module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qgeoroutingmanagerenginemapbox.h"
42#include "qgeoroutereplymapbox.h"
43#include "qmapboxcommon.h"
44#include <QtLocation/private/qgeorouteparserosrmv5_p.h>
45#include <QtLocation/qgeoroutesegment.h>
46#include <QtLocation/qgeomaneuver.h>
47
48#include <QtCore/QJsonDocument>
49#include <QtCore/QJsonObject>
50#include <QtCore/QJsonArray>
51#include <QtCore/QUrlQuery>
52#include <QtCore/QDebug>
53
54QT_BEGIN_NAMESPACE
55
56class QGeoRouteParserOsrmV5ExtensionMapbox: public QGeoRouteParserOsrmV5Extension
57{
58public:
59 QGeoRouteParserOsrmV5ExtensionMapbox(const QString &accessToken, bool useMapboxTextInstructions);
60 void updateQuery(QUrlQuery &query) const override;
61 void updateSegment(QGeoRouteSegment &segment, const QJsonObject &step, const QJsonObject &maneuver) const override;
62
63 QString m_accessToken;
64 bool m_useMapboxTextInstructions = false;
65};
66
67QGeoRouteParserOsrmV5ExtensionMapbox::QGeoRouteParserOsrmV5ExtensionMapbox(const QString &accessToken, bool useMapboxTextInstructions)
68 : QGeoRouteParserOsrmV5Extension(), m_accessToken(accessToken), m_useMapboxTextInstructions(useMapboxTextInstructions)
69{
70
71}
72
73void QGeoRouteParserOsrmV5ExtensionMapbox::updateQuery(QUrlQuery &query) const
74{
75 if (!m_accessToken.isEmpty())
76 query.addQueryItem(key: QLatin1String("access_token"), value: m_accessToken);
77
78 query.addQueryItem(key: QLatin1String("annotations"), value: QLatin1String("duration,distance,speed,congestion"));
79
80 query.addQueryItem(key: QLatin1String("voice_instructions"), value: QLatin1String("true"));
81 query.addQueryItem(key: QLatin1String("banner_instructions"), value: QLatin1String("true"));
82 query.addQueryItem(key: QLatin1String("roundabout_exits"), value: QLatin1String("true"));
83
84 QLocale::MeasurementSystem unit = QLocale::system().measurementSystem();
85 query.addQueryItem(key: QLatin1String("voice_units"), value: unit == QLocale::MetricSystem ? QLatin1String("metric") : QLatin1String("imperial"));
86}
87
88static QVariantMap parseMapboxVoiceInstruction(const QJsonObject &voiceInstruction)
89{
90 QVariantMap map;
91
92 if (voiceInstruction.value(key: QLatin1String("distanceAlongGeometry")).isDouble())
93 map.insert(akey: QLatin1String("distance_along_geometry"), avalue: voiceInstruction.value(key: QLatin1String("distanceAlongGeometry")).toDouble());
94
95 if (voiceInstruction.value(key: QLatin1String("announcement")).isString())
96 map.insert(akey: QLatin1String("announcement"), avalue: voiceInstruction.value(key: QLatin1String("announcement")).toString());
97
98 if (voiceInstruction.value(key: QLatin1String("ssmlAnnouncement")).isString())
99 map.insert(akey: QLatin1String("ssml_announcement"), avalue: voiceInstruction.value(key: QLatin1String("ssmlAnnouncement")).toString());
100
101 return map;
102}
103
104static QVariantList parseMapboxVoiceInstructions(const QJsonArray &voiceInstructions)
105{
106 QVariantList list;
107 for (const QJsonValue &voiceInstructionValue : voiceInstructions) {
108 if (voiceInstructionValue.isObject())
109 list << parseMapboxVoiceInstruction(voiceInstruction: voiceInstructionValue.toObject());
110 }
111 return list;
112}
113
114static QVariantMap parseMapboxBannerComponent(const QJsonObject &bannerComponent)
115{
116 QVariantMap map;
117
118 if (bannerComponent.value(key: QLatin1String("type")).isString())
119 map.insert(akey: QLatin1String("type"), avalue: bannerComponent.value(key: QLatin1String("type")).toString());
120
121 if (bannerComponent.value(key: QLatin1String("text")).isString())
122 map.insert(akey: QLatin1String("text"), avalue: bannerComponent.value(key: QLatin1String("text")).toString());
123
124 if (bannerComponent.value(key: QLatin1String("abbr")).isString())
125 map.insert(akey: QLatin1String("abbr"), avalue: bannerComponent.value(key: QLatin1String("abbr")).toString());
126
127 if (bannerComponent.value(key: QLatin1String("abbr_priority")).isDouble())
128 map.insert(akey: QLatin1String("abbr_priority"), avalue: bannerComponent.value(key: QLatin1String("abbr_priority")).toInt());
129
130 return map;
131}
132
133static QVariantList parseMapboxBannerComponents(const QJsonArray &bannerComponents)
134{
135 QVariantList list;
136 for (const QJsonValue &bannerComponentValue : bannerComponents) {
137 if (bannerComponentValue.isObject())
138 list << parseMapboxBannerComponent(bannerComponent: bannerComponentValue.toObject());
139 }
140 return list;
141}
142
143static QVariantMap parseMapboxBanner(const QJsonObject &banner)
144{
145 QVariantMap map;
146
147 if (banner.value(key: QLatin1String("text")).isString())
148 map.insert(akey: QLatin1String("text"), avalue: banner.value(key: QLatin1String("text")).toString());
149
150 if (banner.value(key: QLatin1String("components")).isArray())
151 map.insert(akey: QLatin1String("components"), avalue: parseMapboxBannerComponents(bannerComponents: banner.value(key: QLatin1String("components")).toArray()));
152
153 if (banner.value(key: QLatin1String("type")).isString())
154 map.insert(akey: QLatin1String("type"), avalue: banner.value(key: QLatin1String("type")).toString());
155
156 if (banner.value(key: QLatin1String("modifier")).isString())
157 map.insert(akey: QLatin1String("modifier"), avalue: banner.value(key: QLatin1String("modifier")).toString());
158
159 if (banner.value(key: QLatin1String("degrees")).isDouble())
160 map.insert(akey: QLatin1String("degrees"), avalue: banner.value(key: QLatin1String("degrees")).toDouble());
161
162 if (banner.value(key: QLatin1String("driving_side")).isString())
163 map.insert(akey: QLatin1String("driving_side"), avalue: banner.value(key: QLatin1String("driving_side")).toString());
164
165 return map;
166}
167
168static QVariantMap parseMapboxBannerInstruction(const QJsonObject &bannerInstruction)
169{
170 QVariantMap map;
171
172 if (bannerInstruction.value(key: QLatin1String("distanceAlongGeometry")).isDouble())
173 map.insert(akey: QLatin1String("distance_along_geometry"), avalue: bannerInstruction.value(key: QLatin1String("distanceAlongGeometry")).toDouble());
174
175 if (bannerInstruction.value(key: QLatin1String("primary")).isObject())
176 map.insert(akey: QLatin1String("primary"), avalue: parseMapboxBanner(banner: bannerInstruction.value(key: QLatin1String("primary")).toObject()));
177
178 if (bannerInstruction.value(key: QLatin1String("secondary")).isObject())
179 map.insert(akey: QLatin1String("secondary"), avalue: parseMapboxBanner(banner: bannerInstruction.value(key: QLatin1String("secondary")).toObject()));
180
181 if (bannerInstruction.value(key: QLatin1String("then")).isObject())
182 map.insert(akey: QLatin1String("then"), avalue: parseMapboxBanner(banner: bannerInstruction.value(key: QLatin1String("then")).toObject()));
183
184 return map;
185}
186
187static QVariantList parseMapboxBannerInstructions(const QJsonArray &bannerInstructions)
188{
189 QVariantList list;
190 for (const QJsonValue &bannerInstructionValue : bannerInstructions) {
191 if (bannerInstructionValue.isObject())
192 list << parseMapboxBannerInstruction(bannerInstruction: bannerInstructionValue.toObject());
193 }
194 return list;
195}
196
197void QGeoRouteParserOsrmV5ExtensionMapbox::updateSegment(QGeoRouteSegment &segment, const QJsonObject &step, const QJsonObject &maneuver) const
198{
199 QGeoManeuver m = segment.maneuver();
200 QVariantMap extendedAttributes = m.extendedAttributes();
201 if (m_useMapboxTextInstructions && maneuver.value(key: QLatin1String("instruction")).isString()) {
202 QString maneuverInstructionText = maneuver.value(key: QLatin1String("instruction")).toString();
203 if (!maneuverInstructionText.isEmpty())
204 m.setInstructionText(maneuverInstructionText);
205 }
206
207 if (step.value(key: QLatin1String("voiceInstructions")).isArray())
208 extendedAttributes.insert(akey: QLatin1String("mapbox.voice_instructions"),
209 avalue: parseMapboxVoiceInstructions(voiceInstructions: step.value(key: QLatin1String("voiceInstructions")).toArray()));
210 if (step.value(key: QLatin1String("bannerInstructions")).isArray())
211 extendedAttributes.insert(akey: QLatin1String("mapbox.banner_instructions"),
212 avalue: parseMapboxBannerInstructions(bannerInstructions: step.value(key: QLatin1String("bannerInstructions")).toArray()));
213
214 m.setExtendedAttributes(extendedAttributes);
215 segment.setManeuver(m);
216}
217
218
219QGeoRoutingManagerEngineMapbox::QGeoRoutingManagerEngineMapbox(const QVariantMap &parameters,
220 QGeoServiceProvider::Error *error,
221 QString *errorString)
222 : QGeoRoutingManagerEngine(parameters),
223 m_networkManager(new QNetworkAccessManager(this)),
224 m_userAgent(mapboxDefaultUserAgent)
225{
226 if (parameters.contains(QStringLiteral("mapbox.useragent"))) {
227 m_userAgent = parameters.value(QStringLiteral("mapbox.useragent")).toString().toLatin1();
228 }
229
230 if (parameters.contains(QStringLiteral("mapbox.access_token"))) {
231 m_accessToken = parameters.value(QStringLiteral("mapbox.access_token")).toString();
232 }
233
234 bool use_mapbox_text_instructions = true;
235 if (parameters.contains(QStringLiteral("mapbox.routing.use_mapbox_text_instructions"))) {
236 use_mapbox_text_instructions = parameters.value(QStringLiteral("mapbox.routing.use_mapbox_text_instructions")).toBool();
237 }
238
239 QGeoRouteParserOsrmV5 *parser = new QGeoRouteParserOsrmV5(this);
240 parser->setExtension(new QGeoRouteParserOsrmV5ExtensionMapbox(m_accessToken, use_mapbox_text_instructions));
241 if (parameters.contains(QStringLiteral("mapbox.routing.traffic_side"))) {
242 QString trafficSide = parameters.value(QStringLiteral("mapbox.routing.traffic_side")).toString();
243 if (trafficSide == QStringLiteral("right"))
244 parser->setTrafficSide(QGeoRouteParser::RightHandTraffic);
245 else if (trafficSide == QStringLiteral("left"))
246 parser->setTrafficSide(QGeoRouteParser::LeftHandTraffic);
247 }
248 m_routeParser = parser;
249
250 *error = QGeoServiceProvider::NoError;
251 errorString->clear();
252}
253
254QGeoRoutingManagerEngineMapbox::~QGeoRoutingManagerEngineMapbox()
255{
256}
257
258QGeoRouteReply* QGeoRoutingManagerEngineMapbox::calculateRoute(const QGeoRouteRequest &request)
259{
260 QNetworkRequest networkRequest;
261 networkRequest.setHeader(header: QNetworkRequest::UserAgentHeader, value: m_userAgent);
262
263 QString url = mapboxDirectionsApiPath;
264
265 QGeoRouteRequest::TravelModes travelModes = request.travelModes();
266 if (travelModes.testFlag(flag: QGeoRouteRequest::PedestrianTravel)) {
267 url += QStringLiteral("walking/");
268 } else if (travelModes.testFlag(flag: QGeoRouteRequest::BicycleTravel)) {
269 url += QStringLiteral("cycling/");
270 } else if (travelModes.testFlag(flag: QGeoRouteRequest::CarTravel)) {
271 const QList<QGeoRouteRequest::FeatureType> &featureTypes = request.featureTypes();
272 int trafficFeatureIdx = featureTypes.indexOf(t: QGeoRouteRequest::TrafficFeature);
273 QGeoRouteRequest::FeatureWeight trafficWeight = request.featureWeight(featureType: QGeoRouteRequest::TrafficFeature);
274 if (trafficFeatureIdx >= 0 &&
275 (trafficWeight == QGeoRouteRequest::AvoidFeatureWeight || trafficWeight == QGeoRouteRequest::DisallowFeatureWeight)) {
276 url += QStringLiteral("driving-traffic/");
277 } else {
278 url += QStringLiteral("driving/");
279 }
280 }
281
282 networkRequest.setUrl(m_routeParser->requestUrl(request, prefix: url));
283
284 QNetworkReply *reply = m_networkManager->get(request: networkRequest);
285
286 QGeoRouteReplyMapbox *routeReply = new QGeoRouteReplyMapbox(reply, request, this);
287
288 connect(sender: routeReply, SIGNAL(finished()), receiver: this, SLOT(replyFinished()));
289 connect(sender: routeReply, SIGNAL(error(QGeoRouteReply::Error,QString)),
290 receiver: this, SLOT(replyError(QGeoRouteReply::Error,QString)));
291
292 return routeReply;
293}
294
295const QGeoRouteParser *QGeoRoutingManagerEngineMapbox::routeParser() const
296{
297 return m_routeParser;
298}
299
300void QGeoRoutingManagerEngineMapbox::replyFinished()
301{
302 QGeoRouteReply *reply = qobject_cast<QGeoRouteReply *>(object: sender());
303 if (reply)
304 emit finished(reply);
305}
306
307void QGeoRoutingManagerEngineMapbox::replyError(QGeoRouteReply::Error errorCode,
308 const QString &errorString)
309{
310 QGeoRouteReply *reply = qobject_cast<QGeoRouteReply *>(object: sender());
311 if (reply)
312 emit error(reply, error: errorCode, errorString);
313}
314
315QT_END_NAMESPACE
316

source code of qtlocation/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp