diff options
6 files changed, 68 insertions, 141 deletions
diff --git a/src/location/doc/src/plugins/mapbox.qdoc b/src/location/doc/src/plugins/mapbox.qdoc index aef0a981..14fe5f80 100644 --- a/src/location/doc/src/plugins/mapbox.qdoc +++ b/src/location/doc/src/plugins/mapbox.qdoc @@ -153,5 +153,13 @@ The following table lists optional parameters that can be passed to the Mapbox p \tt{OneNeighbourLayer} only prefetches the one layer closest to the current zoom level. Finally, \tt{NoPrefetching} allows to disable the prefetching, so only tiles that are visible will be fetched. Note that, depending on the active map type, this hint might be ignored. +\row + \li mapbox.routing.use_mapbox_text_instructions + \li Whether to use the instruction text that came with the response from the server (true) or the + text generated by the plugin. The default value is true. + Note that if instructions in a language that is not directly supported by Mapbox are needed (see + \l{https://www.mapbox.com/api-documentation/#instructions-languages}{here} for the supported languages), + it is possible to use the \l{Qt Linguist} to translate QtLocation to the desired language, and set this parameter to + false in order to use the translated built-in instructions. \endtable */ diff --git a/src/location/maps/qgeorouteparserosrmv5.cpp b/src/location/maps/qgeorouteparserosrmv5.cpp index 75daefda..38eeb243 100644 --- a/src/location/maps/qgeorouteparserosrmv5.cpp +++ b/src/location/maps/qgeorouteparserosrmv5.cpp @@ -793,8 +793,10 @@ static QGeoManeuver::InstructionDirection instructionDirection(const QJsonObject return QGeoManeuver::NoDirection; } -static QGeoRouteSegment parseStep(const QJsonObject &step) { - // OSRM Instructions documentation: https://github.com/Project-OSRM/osrm-text-instructions/blob/master/instructions.json +static QGeoRouteSegment parseStep(const QJsonObject &step, bool useServerText) { + // OSRM Instructions documentation: https://github.com/Project-OSRM/osrm-text-instructions + // This goes on top of OSRM: https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md + // Mapbox however, includes this in the reply, under "instruction". QGeoRouteSegment segment; if (!step.value(QLatin1String("maneuver")).isObject()) return segment; @@ -808,6 +810,10 @@ static QGeoRouteSegment parseStep(const QJsonObject &step) { if (!maneuver.value(QLatin1String("location")).isArray()) return segment; + QString instruction_text; + if (maneuver.value(QLatin1String("instruction")).isString()) + instruction_text = maneuver.value(QLatin1String("instruction")).toString(); + double time = step.value(QLatin1String("duration")).toDouble(); double distance = step.value(QLatin1String("distance")).toDouble(); @@ -825,7 +831,7 @@ static QGeoRouteSegment parseStep(const QJsonObject &step) { geoManeuver.setDirection(instructionDirection(maneuver)); geoManeuver.setDistanceToNextInstruction(distance); geoManeuver.setTimeToNextInstruction(time); - geoManeuver.setInstructionText(instructionText(step, maneuver, geoManeuver.direction())); + geoManeuver.setInstructionText((useServerText && !instruction_text.isEmpty()) ? instruction_text : instructionText(step, maneuver, geoManeuver.direction())); geoManeuver.setPosition(coord); geoManeuver.setWaypoint(coord); @@ -845,6 +851,9 @@ public: QGeoRouteReply::Error parseReply(QList<QGeoRoute> &routes, QString &errorString, const QByteArray &reply) const Q_DECL_OVERRIDE; QUrl requestUrl(const QGeoRouteRequest &request, const QString &prefix) const Q_DECL_OVERRIDE; + + bool m_useServerText = false; + QString m_accessToken; }; QGeoRouteParserOsrmV5Private::QGeoRouteParserOsrmV5Private() : QGeoRouteParserPrivate() @@ -906,7 +915,7 @@ QGeoRouteReply::Error QGeoRouteParserOsrmV5Private::parseReply(QList<QGeoRoute> error = true; break; } - QGeoRouteSegment segment = parseStep(s.toObject()); + QGeoRouteSegment segment = parseStep(s.toObject(), m_useServerText); if (segment.isValid()) { segments.append(segment); } else { @@ -963,16 +972,26 @@ QUrl QGeoRouteParserOsrmV5Private::requestUrl(const QGeoRouteRequest &request, c query.addQueryItem(QStringLiteral("steps"), QStringLiteral("true")); query.addQueryItem(QStringLiteral("geometries"), QStringLiteral("polyline")); query.addQueryItem(QStringLiteral("alternatives"), QStringLiteral("true")); + if (!m_accessToken.isEmpty()) + query.addQueryItem(QStringLiteral("access_token"), m_accessToken); url.setQuery(query); return url; } -QGeoRouteParserOsrmV5::QGeoRouteParserOsrmV5(QObject *parent) : QGeoRouteParser(*new QGeoRouteParserOsrmV5Private(), parent) +QGeoRouteParserOsrmV5::QGeoRouteParserOsrmV5(QObject *parent, bool useServerText) : QGeoRouteParser(*new QGeoRouteParserOsrmV5Private(), parent) { + Q_D(QGeoRouteParserOsrmV5); + d->m_useServerText = useServerText; } QGeoRouteParserOsrmV5::~QGeoRouteParserOsrmV5() { } +void QGeoRouteParserOsrmV5::setAccessToken(const QString &token) +{ + Q_D(QGeoRouteParserOsrmV5); + d->m_accessToken = token; +} + QT_END_NAMESPACE diff --git a/src/location/maps/qgeorouteparserosrmv5_p.h b/src/location/maps/qgeorouteparserosrmv5_p.h index d2c59165..c6ad0367 100644 --- a/src/location/maps/qgeorouteparserosrmv5_p.h +++ b/src/location/maps/qgeorouteparserosrmv5_p.h @@ -60,9 +60,11 @@ class Q_LOCATION_PRIVATE_EXPORT QGeoRouteParserOsrmV5 : public QGeoRouteParser Q_DECLARE_PRIVATE(QGeoRouteParserOsrmV5) public: - QGeoRouteParserOsrmV5(QObject *parent = Q_NULLPTR); + QGeoRouteParserOsrmV5(QObject *parent = Q_NULLPTR, bool useServerText = false); virtual ~QGeoRouteParserOsrmV5(); + void setAccessToken(const QString &token); + private: Q_DISABLE_COPY(QGeoRouteParserOsrmV5) }; diff --git a/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp b/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp index 8fc3386a..43b18454 100644 --- a/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp +++ b/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp @@ -39,7 +39,8 @@ ****************************************************************************/ #include "qgeoroutereplymapbox.h" - +#include "qgeoroutingmanagerenginemapbox.h" +#include <QtLocation/private/qgeorouteparser_p.h> #include <QtCore/QJsonDocument> #include <QtCore/QJsonObject> #include <QtCore/QJsonArray> @@ -48,40 +49,6 @@ QT_BEGIN_NAMESPACE -static QList<QGeoCoordinate> parsePolyline(const QString &line) -{ - QList<QGeoCoordinate> path; - QByteArray data(line.toLocal8Bit()); - - int mode = 0, shift = 0, value = 0, coord[2] = {0, 0}; - for (int i = 0; i < data.length(); ++i) { - int c = data.at(i) - 63; - value |= (c & 0x1f) << shift; - shift += 5; - if (c & 0x20) continue; - coord[mode] += (value & 1) ? ~(value >> 1) : (value >> 1); - if (mode) path.append(QGeoCoordinate((double)coord[0]/1e5, (double)coord[1]/1e5)); - mode = 1 - mode; - value = shift = 0; - } - return path; -} - -static QList<QGeoCoordinate> parseGeometry(const QJsonValue &geometry) -{ - QList<QGeoCoordinate> path; - if (geometry.isString()) path = parsePolyline(geometry.toString()); - if (geometry.isObject()) { - QJsonArray coords = geometry.toObject().value(QStringLiteral("coordinates")).toArray(); - for (int i = 0; i < coords.count(); i++) { - QJsonArray coord = coords.at(i).toArray(); - if (coord.count() != 2) continue; - path.append(QGeoCoordinate(coord.at(1).toDouble(), coord.at(0).toDouble())); - } - } - return path; -} - QGeoRouteReplyMapbox::QGeoRouteReplyMapbox(QNetworkReply *reply, const QGeoRouteRequest &request, QObject *parent) : QGeoRouteReply(request, parent) @@ -101,73 +68,6 @@ QGeoRouteReplyMapbox::~QGeoRouteReplyMapbox() { } -static QGeoRoute constructRoute(const QJsonObject &obj) -{ - QGeoRoute route; - route.setDistance(obj.value(QStringLiteral("distance")).toDouble()); - route.setTravelTime(obj.value(QStringLiteral("duration")).toDouble()); - - QList<QGeoCoordinate> path = parseGeometry(obj.value(QStringLiteral("geometry"))); - route.setPath(path); - - QGeoRouteSegment firstSegment, lastSegment; - QJsonArray legs = obj.value(QStringLiteral("legs")).toArray(); - - for (int i = 0; i < legs.count(); i++) { - QJsonObject leg = legs.at(i).toObject(); - QJsonArray steps = leg.value("steps").toArray(); - - for (int j = 0; j < steps.count(); j++) { - QJsonObject step = steps.at(j).toObject(); - QJsonObject stepManeuver = step.value("maneuver").toObject(); - - QGeoRouteSegment segment; - segment.setDistance(step.value("distance").toDouble()); - segment.setTravelTime(step.value(QStringLiteral("duration")).toDouble()); - - QGeoManeuver maneuver; - maneuver.setDistanceToNextInstruction(step.value("distance").toDouble()); - maneuver.setInstructionText(stepManeuver.value("instruction").toString()); - maneuver.setTimeToNextInstruction(step.value(QStringLiteral("duration")).toDouble()); - QJsonArray location = stepManeuver.value(QStringLiteral("location")).toArray(); - if (location.count() > 1) - maneuver.setPosition(QGeoCoordinate(location.at(0).toDouble(), location.at(1).toDouble())); - - QString modifier = stepManeuver.value("modifier").toString(); - int bearing1 = stepManeuver.value("bearing_before").toInt(); - int bearing2 = stepManeuver.value("bearing_after").toInt(); - - if (modifier == "straight") - maneuver.setDirection(QGeoManeuver::DirectionForward); - else if (modifier == "slight right") - maneuver.setDirection(QGeoManeuver::DirectionLightRight); - else if (modifier == "right") - maneuver.setDirection(QGeoManeuver::DirectionRight); - else if (modifier == "sharp right") - maneuver.setDirection(QGeoManeuver::DirectionHardRight); - else if (modifier == "uturn") - maneuver.setDirection(bearing2 - bearing1 > 180 ? QGeoManeuver::DirectionUTurnLeft : QGeoManeuver::DirectionUTurnRight); - else if (modifier == "sharp left") - maneuver.setDirection(QGeoManeuver::DirectionHardLeft); - else if (modifier == "left") - maneuver.setDirection(QGeoManeuver::DirectionLeft); - else if (modifier == "slight left") - maneuver.setDirection(QGeoManeuver::DirectionLightLeft); - else - maneuver.setDirection(QGeoManeuver::NoDirection); - - segment.setManeuver(maneuver); - segment.setPath(parseGeometry(step.value(QStringLiteral("geometry")))); - - if (!firstSegment.isValid()) firstSegment = segment; - if (lastSegment.isValid()) lastSegment.setNextRouteSegment(segment); - lastSegment = segment; - } - } - route.setFirstRouteSegment(firstSegment); - return route; -} - void QGeoRouteReplyMapbox::networkReplyFinished() { QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); @@ -176,26 +76,19 @@ void QGeoRouteReplyMapbox::networkReplyFinished() if (reply->error() != QNetworkReply::NoError) return; - QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); - if (document.isObject()) { - QJsonObject object = document.object(); + QGeoRoutingManagerEngineMapbox *engine = qobject_cast<QGeoRoutingManagerEngineMapbox *>(parent()); + const QGeoRouteParser *parser = engine->routeParser(); - QString status = object.value(QStringLiteral("code")).toString(); - if (status != QStringLiteral("Ok")) { - setError(QGeoRouteReply::UnknownError, object.value(QStringLiteral("message")).toString()); - return; - } + QList<QGeoRoute> routes; + QString errorString; + QGeoRouteReply::Error error = parser->parseReply(routes, errorString, reply->readAll()); - QList<QGeoRoute> list; - QJsonArray routes = object.value(QStringLiteral("routes")).toArray(); - for (int i = 0; i < routes.count(); i++) { - QGeoRoute route = constructRoute(routes.at(i).toObject()); - list.append(route); - } - setRoutes(list); + if (error == QGeoRouteReply::NoError) { + setRoutes(routes.mid(0, request().numberAlternativeRoutes() + 1)); + // setError(QGeoRouteReply::NoError, status); // can't do this, or NoError is emitted and does damages setFinished(true); } else { - setError(QGeoRouteReply::ParseError, QStringLiteral("Couldn't parse json.")); + setError(error, errorString); } } diff --git a/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp b/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp index 1b7cc1b3..2697114d 100644 --- a/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp +++ b/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp @@ -41,6 +41,7 @@ #include "qgeoroutingmanagerenginemapbox.h" #include "qgeoroutereplymapbox.h" #include "qmapboxcommon.h" +#include "QtLocation/private/qgeorouteparserosrmv5_p.h" #include <QtCore/QUrlQuery> #include <QtCore/QDebug> @@ -62,6 +63,15 @@ QGeoRoutingManagerEngineMapbox::QGeoRoutingManagerEngineMapbox(const QVariantMap m_accessToken = parameters.value(QStringLiteral("mapbox.access_token")).toString(); } + bool use_mapbox_text_instructions = true; + if (parameters.contains(QStringLiteral("mapbox.routing.use_mapbox_text_instructions"))) { + use_mapbox_text_instructions = parameters.value(QStringLiteral("mapbox.use_mapbox_text_instructions")).toBool(); + } + + QGeoRouteParserOsrmV5 *parser = new QGeoRouteParserOsrmV5(this, use_mapbox_text_instructions); + parser->setAccessToken(m_accessToken); + m_routeParser = parser; + *error = QGeoServiceProvider::NoError; errorString->clear(); } @@ -73,7 +83,7 @@ QGeoRoutingManagerEngineMapbox::~QGeoRoutingManagerEngineMapbox() QGeoRouteReply* QGeoRoutingManagerEngineMapbox::calculateRoute(const QGeoRouteRequest &request) { QNetworkRequest networkRequest; - networkRequest.setRawHeader("User-Agent", m_userAgent); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); QString url = mapboxDirectionsApiPath; @@ -94,24 +104,10 @@ QGeoRouteReply* QGeoRoutingManagerEngineMapbox::calculateRoute(const QGeoRouteRe } } - foreach (const QGeoCoordinate &c, request.waypoints()) { - url += QString("%1,%2;").arg(c.longitude()).arg(c.latitude()); - } - if (url.right(1) == QLatin1Char(';')) - url.chop(1); - - QUrlQuery query; - query.addQueryItem(QStringLiteral("steps"), QStringLiteral("true")); - query.addQueryItem(QStringLiteral("alternatives"), QStringLiteral("true")); - query.addQueryItem(QStringLiteral("overview"), QStringLiteral("full")); - query.addQueryItem(QStringLiteral("geometries"), QStringLiteral("geojson")); - query.addQueryItem(QStringLiteral("access_token"), m_accessToken); - - QUrl u(url); - u.setQuery(query); - networkRequest.setUrl(u); + networkRequest.setUrl(m_routeParser->requestUrl(request, url)); QNetworkReply *reply = m_networkManager->get(networkRequest); + QGeoRouteReplyMapbox *routeReply = new QGeoRouteReplyMapbox(reply, request, this); connect(routeReply, SIGNAL(finished()), this, SLOT(replyFinished())); @@ -121,6 +117,11 @@ QGeoRouteReply* QGeoRoutingManagerEngineMapbox::calculateRoute(const QGeoRouteRe return routeReply; } +const QGeoRouteParser *QGeoRoutingManagerEngineMapbox::routeParser() const +{ + return m_routeParser; +} + void QGeoRoutingManagerEngineMapbox::replyFinished() { QGeoRouteReply *reply = qobject_cast<QGeoRouteReply *>(sender()); diff --git a/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.h b/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.h index 5b440147..61ab9a4a 100644 --- a/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.h +++ b/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.h @@ -47,6 +47,7 @@ QT_BEGIN_NAMESPACE class QNetworkAccessManager; +class QGeoRouteParser; class QGeoRoutingManagerEngineMapbox : public QGeoRoutingManagerEngine { @@ -59,6 +60,7 @@ public: ~QGeoRoutingManagerEngineMapbox(); QGeoRouteReply *calculateRoute(const QGeoRouteRequest &request); + const QGeoRouteParser *routeParser() const; private Q_SLOTS: void replyFinished(); @@ -68,6 +70,8 @@ private: QNetworkAccessManager *m_networkManager; QByteArray m_userAgent; QString m_accessToken; + bool m_useMapboxText = false; + QGeoRouteParser *m_routeParser = nullptr; }; QT_END_NAMESPACE |