1/****************************************************************************
2**
3** Copyright (C) 2015 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 "qgeoroutingmanagerengine_nokia.h"
38#include "qgeoroutereply_nokia.h"
39#include "qgeonetworkaccessmanager.h"
40#include "qgeouriprovider.h"
41#include "uri_constants.h"
42
43#include <QStringList>
44#include <QUrl>
45#include <QLocale>
46#include <QtPositioning/QGeoRectangle>
47
48QT_BEGIN_NAMESPACE
49
50QGeoRoutingManagerEngineNokia::QGeoRoutingManagerEngineNokia(
51 QGeoNetworkAccessManager *networkManager,
52 const QVariantMap &parameters,
53 QGeoServiceProvider::Error *error,
54 QString *errorString)
55 : QGeoRoutingManagerEngine(parameters)
56 , m_networkManager(networkManager)
57 , m_uriProvider(new QGeoUriProvider(this, parameters, QStringLiteral("here.routing.host"), ROUTING_HOST))
58
59{
60 Q_ASSERT(networkManager);
61 m_networkManager->setParent(this);
62
63 m_appId = parameters.value(QStringLiteral("here.app_id")).toString();
64 m_token = parameters.value(QStringLiteral("here.token")).toString();
65
66 QGeoRouteRequest::FeatureTypes featureTypes;
67 featureTypes |= QGeoRouteRequest::TollFeature;
68 featureTypes |= QGeoRouteRequest::HighwayFeature;
69 featureTypes |= QGeoRouteRequest::FerryFeature;
70 featureTypes |= QGeoRouteRequest::TunnelFeature;
71 featureTypes |= QGeoRouteRequest::DirtRoadFeature;
72 featureTypes |= QGeoRouteRequest::ParksFeature;
73 setSupportedFeatureTypes(featureTypes);
74
75 QGeoRouteRequest::FeatureWeights featureWeights;
76 featureWeights |= QGeoRouteRequest::DisallowFeatureWeight;
77 featureWeights |= QGeoRouteRequest::AvoidFeatureWeight;
78 featureWeights |= QGeoRouteRequest::PreferFeatureWeight;
79 setSupportedFeatureWeights(featureWeights);
80
81 QGeoRouteRequest::ManeuverDetails maneuverDetails;
82 maneuverDetails |= QGeoRouteRequest::BasicManeuvers;
83 setSupportedManeuverDetails(maneuverDetails);
84
85 QGeoRouteRequest::RouteOptimizations optimizations;
86 optimizations |= QGeoRouteRequest::ShortestRoute;
87 optimizations |= QGeoRouteRequest::FastestRoute;
88 setSupportedRouteOptimizations(optimizations);
89
90 QGeoRouteRequest::TravelModes travelModes;
91 travelModes |= QGeoRouteRequest::CarTravel;
92 travelModes |= QGeoRouteRequest::PedestrianTravel;
93 travelModes |= QGeoRouteRequest::PublicTransitTravel;
94 travelModes |= QGeoRouteRequest::BicycleTravel;
95 setSupportedTravelModes(travelModes);
96
97 QGeoRouteRequest::SegmentDetails segmentDetails;
98 segmentDetails |= QGeoRouteRequest::BasicSegmentData;
99 setSupportedSegmentDetails(segmentDetails);
100
101 if (error)
102 *error = QGeoServiceProvider::NoError;
103
104 if (errorString)
105 *errorString = QString();
106}
107
108QGeoRoutingManagerEngineNokia::~QGeoRoutingManagerEngineNokia() {}
109
110QGeoRouteReply *QGeoRoutingManagerEngineNokia::calculateRoute(const QGeoRouteRequest &request)
111{
112 const QStringList reqStrings = calculateRouteRequestString(request);
113
114 if (reqStrings.isEmpty()) {
115 QGeoRouteReply *reply = new QGeoRouteReply(QGeoRouteReply::UnsupportedOptionError, "The given route request options are not supported by this service provider.", this);
116 emit error(reply, error: reply->error(), errorString: reply->errorString());
117 return reply;
118 }
119
120 QList<QNetworkReply*> replies;
121 foreach (const QString &reqString, reqStrings)
122 replies.append(t: m_networkManager->get(request: QNetworkRequest(QUrl(reqString))));
123
124 QGeoRouteReplyNokia *reply = new QGeoRouteReplyNokia(request, replies, this);
125
126 connect(sender: reply,
127 SIGNAL(finished()),
128 receiver: this,
129 SLOT(routeFinished()));
130
131 connect(sender: reply,
132 SIGNAL(error(QGeoRouteReply::Error,QString)),
133 receiver: this,
134 SLOT(routeError(QGeoRouteReply::Error,QString)));
135
136 return reply;
137}
138
139QGeoRouteReply *QGeoRoutingManagerEngineNokia::updateRoute(const QGeoRoute &route, const QGeoCoordinate &position)
140{
141 const QStringList reqStrings = updateRouteRequestString(route, position);
142
143 if (reqStrings.isEmpty()) {
144 QGeoRouteReply *reply = new QGeoRouteReply(QGeoRouteReply::UnsupportedOptionError, "The given route request options are not supported by this service provider.", this);
145 emit error(reply, error: reply->error(), errorString: reply->errorString());
146 return reply;
147 }
148
149 QList<QNetworkReply*> replies;
150 foreach (const QString &reqString, reqStrings)
151 replies.append(t: m_networkManager->get(request: QNetworkRequest(QUrl(reqString))));
152
153 QGeoRouteRequest updateRequest(route.request());
154 updateRequest.setTravelModes(route.travelMode());
155 QGeoRouteReplyNokia *reply = new QGeoRouteReplyNokia(updateRequest, replies, this);
156
157 connect(sender: reply,
158 SIGNAL(finished()),
159 receiver: this,
160 SLOT(routeFinished()));
161
162 connect(sender: reply,
163 SIGNAL(error(QGeoRouteReply::Error,QString)),
164 receiver: this,
165 SLOT(routeError(QGeoRouteReply::Error,QString)));
166
167 return reply;
168}
169
170bool QGeoRoutingManagerEngineNokia::checkEngineSupport(const QGeoRouteRequest &request,
171 QGeoRouteRequest::TravelModes travelModes) const
172{
173 QList<QGeoRouteRequest::FeatureType> featureTypeList = request.featureTypes();
174 QGeoRouteRequest::FeatureTypes featureTypeFlag = QGeoRouteRequest::NoFeature;
175 QGeoRouteRequest::FeatureWeights featureWeightFlag = QGeoRouteRequest::NeutralFeatureWeight;
176
177 for (int i = 0; i < featureTypeList.size(); ++i) {
178 featureTypeFlag |= featureTypeList.at(i);
179 featureWeightFlag |= request.featureWeight(featureType: featureTypeList.at(i));
180 }
181
182 if ((featureTypeFlag & supportedFeatureTypes()) != featureTypeFlag)
183 return false;
184
185 if ((featureWeightFlag & supportedFeatureWeights()) != featureWeightFlag)
186 return false;
187
188
189 if ((request.maneuverDetail() & supportedManeuverDetails()) != request.maneuverDetail())
190 return false;
191
192 if ((request.segmentDetail() & supportedSegmentDetails()) != request.segmentDetail())
193 return false;
194
195 if ((request.routeOptimization() & supportedRouteOptimizations()) != request.routeOptimization())
196 return false;
197
198 if ((travelModes & supportedTravelModes()) != travelModes)
199 return false;
200
201 // Count the number of set bits (= number of travel modes) (popcount)
202 int count = 0;
203
204 for (unsigned bits = travelModes; bits; bits >>= 1)
205 count += (bits & 1);
206
207 // We only allow one travel mode at a time
208 if (count != 1)
209 return false;
210
211 return true;
212}
213
214QStringList QGeoRoutingManagerEngineNokia::calculateRouteRequestString(const QGeoRouteRequest &request)
215{
216 bool supported = checkEngineSupport(request, travelModes: request.travelModes());
217
218 if (!supported)
219 return QStringList();
220 QStringList requests;
221
222 QString baseRequest = QStringLiteral("http://");
223 baseRequest += m_uriProvider->getCurrentHost();
224 baseRequest += QStringLiteral("/routing/7.2/calculateroute.xml");
225
226 baseRequest += QStringLiteral("?alternatives=");
227 baseRequest += QString::number(request.numberAlternativeRoutes());
228
229 if (!m_appId.isEmpty() && !m_token.isEmpty()) {
230 baseRequest += QStringLiteral("&app_id=");
231 baseRequest += m_appId;
232 baseRequest += QStringLiteral("&token=");
233 baseRequest += m_token;
234 }
235
236 const QList<QVariantMap> metadata = request.waypointsMetadata();
237 const QList<QGeoCoordinate> waypoints = request.waypoints();
238 int numWaypoints = waypoints.size();
239 if (numWaypoints < 2)
240 return QStringList();
241 // Details: https://developer.here.com/documentation/routing/topics/resource-param-type-waypoint.html
242 for (int i = 0;i < numWaypoints;++i) {
243 const QGeoCoordinate &c = waypoints.at(i);
244 baseRequest += QStringLiteral("&waypoint");
245 baseRequest += QString::number(i);
246 baseRequest += QStringLiteral("=geo!");
247 baseRequest += trimDouble(degree: c.latitude());
248 baseRequest += ',';
249 baseRequest += trimDouble(degree: c.longitude());
250 baseRequest += QStringLiteral(";;"); // ;<TransitRadius>;<UserLabel>
251 if (metadata.size() > i) {
252 const QVariantMap &meta = metadata.at(i);
253 if (meta.contains(QStringLiteral("bearing"))) {
254 qreal bearing = meta.value(QStringLiteral("bearing")).toDouble();
255 baseRequest += ';' + QString::number(int(bearing));
256 }
257 }
258 }
259
260 QGeoRouteRequest::RouteOptimizations optimization = request.routeOptimization();
261
262 QStringList types;
263 if (optimization.testFlag(flag: QGeoRouteRequest::ShortestRoute))
264 types.append(t: "shortest");
265 if (optimization.testFlag(flag: QGeoRouteRequest::FastestRoute))
266 types.append(t: "fastest");
267
268 foreach (const QString &optimization, types) {
269 QString requestString = baseRequest;
270 requestString += modesRequestString(request, travelModes: request.travelModes(), optimization);
271 requestString += routeRequestString(request);
272 requests << requestString;
273 }
274
275 return requests;
276}
277
278QStringList QGeoRoutingManagerEngineNokia::updateRouteRequestString(const QGeoRoute &route, const QGeoCoordinate &position)
279{
280 if (!checkEngineSupport(request: route.request(), travelModes: route.travelMode()))
281 return QStringList();
282 QStringList requests;
283
284 QString baseRequest = "http://";
285 baseRequest += m_uriProvider->getCurrentHost();
286 baseRequest += "/routing/7.2/getroute.xml";
287
288 baseRequest += "?routeid=";
289 baseRequest += route.routeId();
290
291 baseRequest += "&pos=";
292 baseRequest += QString::number(position.latitude());
293 baseRequest += ',';
294 baseRequest += QString::number(position.longitude());
295
296 QGeoRouteRequest::RouteOptimizations optimization = route.request().routeOptimization();
297
298 QStringList types;
299 if (optimization.testFlag(flag: QGeoRouteRequest::ShortestRoute))
300 types.append(t: "shortest");
301 if (optimization.testFlag(flag: QGeoRouteRequest::FastestRoute))
302 types.append(t: "fastest");
303
304 foreach (const QString &optimization, types) {
305 QString requestString = baseRequest;
306 requestString += modesRequestString(request: route.request(), travelModes: route.travelMode(), optimization);
307 requestString += routeRequestString(request: route.request());
308 requests << requestString;
309 }
310
311 return requests;
312}
313
314QString QGeoRoutingManagerEngineNokia::modesRequestString(const QGeoRouteRequest &request,
315 QGeoRouteRequest::TravelModes travelModes, const QString &optimization) const
316{
317 QString requestString;
318
319 QStringList modes;
320 if (travelModes.testFlag(flag: QGeoRouteRequest::CarTravel))
321 modes.append(t: "car");
322 if (travelModes.testFlag(flag: QGeoRouteRequest::PedestrianTravel))
323 modes.append(t: "pedestrian");
324 if (travelModes.testFlag(flag: QGeoRouteRequest::PublicTransitTravel))
325 modes.append(t: "publicTransport");
326
327 QStringList featureStrings;
328 QList<QGeoRouteRequest::FeatureType> featureTypes = request.featureTypes();
329 for (int i = 0; i < featureTypes.size(); ++i) {
330 QGeoRouteRequest::FeatureWeight weight = request.featureWeight(featureType: featureTypes.at(i));
331
332 if (weight == QGeoRouteRequest::NeutralFeatureWeight)
333 continue;
334
335 QString weightString = "";
336 switch (weight) {
337 case QGeoRouteRequest::PreferFeatureWeight:
338 weightString = '1';
339 break;
340 case QGeoRouteRequest::AvoidFeatureWeight:
341 weightString = "-1";
342 break;
343 case QGeoRouteRequest::DisallowFeatureWeight:
344 weightString = "-3";
345 break;
346 case QGeoRouteRequest::NeutralFeatureWeight:
347 case QGeoRouteRequest::RequireFeatureWeight:
348 break;
349 }
350
351 if (weightString.isEmpty())
352 continue;
353
354 switch (featureTypes.at(i)) {
355 case QGeoRouteRequest::TollFeature:
356 featureStrings.append(t: "tollroad:" + weightString);
357 break;
358 case QGeoRouteRequest::HighwayFeature:
359 featureStrings.append(t: "motorway:" + weightString);
360 break;
361 case QGeoRouteRequest::FerryFeature:
362 featureStrings.append(t: "boatFerry:" + weightString);
363 featureStrings.append(t: "railFerry:" + weightString);
364 break;
365 case QGeoRouteRequest::TunnelFeature:
366 featureStrings.append(t: "tunnel:" + weightString);
367 break;
368 case QGeoRouteRequest::DirtRoadFeature:
369 featureStrings.append(t: "dirtRoad:" + weightString);
370 break;
371 case QGeoRouteRequest::PublicTransitFeature:
372 case QGeoRouteRequest::ParksFeature:
373 case QGeoRouteRequest::MotorPoolLaneFeature:
374 case QGeoRouteRequest::TrafficFeature:
375 case QGeoRouteRequest::NoFeature:
376 break;
377 }
378 }
379
380 requestString += "&mode=";
381 requestString += optimization + ';' + modes.join(sep: ',');
382 if (featureStrings.count())
383 requestString += ';' + featureStrings.join(sep: ',');
384 return requestString;
385}
386
387QString QGeoRoutingManagerEngineNokia::routeRequestString(const QGeoRouteRequest &request) const
388{
389 QString requestString;
390
391 foreach (const QGeoRectangle &area, request.excludeAreas()) {
392 requestString += QLatin1String("&avoidareas=");
393 requestString += trimDouble(degree: area.topLeft().latitude());
394 requestString += QLatin1String(",");
395 requestString += trimDouble(degree: area.topLeft().longitude());
396 requestString += QLatin1String(";");
397 requestString += trimDouble(degree: area.bottomRight().latitude());
398 requestString += QLatin1String(",");
399 requestString += trimDouble(degree: area.bottomRight().longitude());
400 }
401
402 QStringList legAttributes;
403// if (request.segmentDetail() & QGeoRouteRequest::BasicSegmentData) // QTBUG-70501, this code expects to find links
404 {
405 requestString += "&linkattributes=sh,le"; //shape,length
406 legAttributes.append(t: "links");
407 }
408
409// if (request.maneuverDetail() & QGeoRouteRequest::BasicManeuvers) // QTBUG-70501, this code expects to find maneuvers
410 {
411 legAttributes.append(t: "maneuvers");
412 //requestString += "&maneuverattributes=po,tt,le,di"; //position,traveltime,length,direction
413 requestString += "&maneuverattributes=all";
414 if (!(request.segmentDetail() & QGeoRouteRequest::NoSegmentData))
415 requestString += ",li"; //link
416 }
417
418 // Handle QTBUG-70502, when API fixes it
419 requestString += "&routeattributes=sm,sh,bb,lg"; //summary,shape,boundingBox,legs
420 if (legAttributes.count() > 0) {
421 requestString += "&legattributes=";
422 requestString += legAttributes.join(sep: ",");
423 }
424
425 // Handle QTBUG-70503, when API fixes it
426 requestString += "&departure=";
427 requestString += QDateTime::currentDateTime().toUTC().toString(format: "yyyy-MM-ddThh:mm:ssZ");
428
429 requestString += "&instructionformat=text";
430
431 // ToDo: make this request-able
432 requestString += "&metricSystem=";
433 if (QLocale::MetricSystem == measurementSystem())
434 requestString += "metric";
435 else
436 requestString += "imperial";
437
438 const QLocale loc(locale());
439
440 // ToDo: make this request-able
441 if (QLocale::C != loc.language() && QLocale::AnyLanguage != loc.language()) {
442 requestString += "&language=";
443 requestString += loc.name();
444 //If the first language isn't supported, english will be selected automatically
445 if (QLocale::English != loc.language())
446 requestString += ",en_US";
447 }
448
449 return requestString;
450}
451
452QString QGeoRoutingManagerEngineNokia::trimDouble(double degree, int decimalDigits)
453{
454 QString sDegree = QString::number(degree, f: 'g', prec: decimalDigits);
455
456 int index = sDegree.indexOf(c: '.');
457
458 if (index == -1)
459 return sDegree;
460 else
461 return QString::number(degree, f: 'g', prec: decimalDigits + index);
462}
463
464void QGeoRoutingManagerEngineNokia::routeFinished()
465{
466 QGeoRouteReply *reply = qobject_cast<QGeoRouteReply *>(object: sender());
467
468 if (!reply)
469 return;
470
471 if (receivers(SIGNAL(finished(QGeoRouteReply*))) == 0) {
472 reply->deleteLater();
473 return;
474 }
475
476 emit finished(reply);
477}
478
479void QGeoRoutingManagerEngineNokia::routeError(QGeoRouteReply::Error error, const QString &errorString)
480{
481 QGeoRouteReply *reply = qobject_cast<QGeoRouteReply *>(object: sender());
482
483 if (!reply)
484 return;
485
486 if (receivers(SIGNAL(error(QGeoRouteReply*,QGeoRouteReply::Error,QString))) == 0) {
487 reply->deleteLater();
488 return;
489 }
490
491 emit this->error(reply, error, errorString);
492}
493
494QT_END_NAMESPACE
495

source code of qtlocation/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp