diff options
author | Bruno de Oliveira Abinader <bruno@mapbox.com> | 2018-04-09 16:37:38 +0300 |
---|---|---|
committer | Paolo Angelelli <paolo.angelelli@qt.io> | 2018-04-11 09:26:40 +0000 |
commit | fdce040790a070701b89aed8c1c798ba183215dd (patch) | |
tree | 2f83240370617a08e8745b9cef3669e15a7fc541 | |
parent | a4d9f8cbf2220fbc8aafd8f491313e39e0bf4a5f (diff) | |
download | qtlocation-fdce040790a070701b89aed8c1c798ba183215dd.tar.gz |
Support Mapbox Directions API voice & banner instructions
Change-Id: I89f4f9940c2a9a862ffaec066e5a7841bc00bd05
Reviewed-by: Bruno de Oliveira Abinader <brunoabinader@gmail.com>
Reviewed-by: Paolo Angelelli <paolo.angelelli@qt.io>
-rw-r--r-- | src/location/maps/qgeorouteparserosrmv5.cpp | 110 | ||||
-rw-r--r-- | src/location/maps/qgeorouteparserosrmv5_p.h | 20 | ||||
-rw-r--r-- | src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp | 169 |
3 files changed, 244 insertions, 55 deletions
diff --git a/src/location/maps/qgeorouteparserosrmv5.cpp b/src/location/maps/qgeorouteparserosrmv5.cpp index 7cd29f92..58299d09 100644 --- a/src/location/maps/qgeorouteparserosrmv5.cpp +++ b/src/location/maps/qgeorouteparserosrmv5.cpp @@ -114,7 +114,7 @@ static QString exitOrdinal(int exit) static QList<QString> ordinals; if (!ordinals.size()) { - ordinals.append(QStringLiteral("")); + ordinals.append(QLatin1String("")); //: always used in " and take the %1 exit [onto <street name>]" ordinals.append(QGeoRouteParserOsrmV5::tr("first", "roundabout exit")); ordinals.append(QGeoRouteParserOsrmV5::tr("second", "roundabout exit")); @@ -720,7 +720,7 @@ static QString instructionText(const QJsonObject &step, const QJsonObject &maneu QString maneuverType; if (maneuver.value(QLatin1String("type")).isString()) maneuverType = maneuver.value(QLatin1String("type")).toString(); - QString wayName = QStringLiteral("unknown street"); + QString wayName = QLatin1String("unknown street"); if (step.value(QLatin1String("name")).isString()) wayName = step.value(QLatin1String("name")).toString(); @@ -793,7 +793,35 @@ static QGeoManeuver::InstructionDirection instructionDirection(const QJsonObject return QGeoManeuver::NoDirection; } -static QGeoRouteSegment parseStep(const QJsonObject &step, bool useServerText, int legIndex, int stepIndex) { +class QGeoRouteParserOsrmV5Private : public QGeoRouteParserPrivate +{ + Q_DECLARE_PUBLIC(QGeoRouteParserOsrmV5) +public: + QGeoRouteParserOsrmV5Private(); + virtual ~QGeoRouteParserOsrmV5Private(); + + QGeoRouteSegment parseStep(const QJsonObject &step, int legIndex, int stepIndex) const; + + // QGeoRouteParserPrivate + + QGeoRouteReply::Error parseReply(QList<QGeoRoute> &routes, QString &errorString, const QByteArray &reply) const override; + QUrl requestUrl(const QGeoRouteRequest &request, const QString &prefix) const override; + + QVariantMap m_vendorParams; + const QGeoRouteParserOsrmV5Extension *m_extension = nullptr; +}; + +QGeoRouteParserOsrmV5Private::QGeoRouteParserOsrmV5Private() + : QGeoRouteParserPrivate() +{ +} + +QGeoRouteParserOsrmV5Private::~QGeoRouteParserOsrmV5Private() +{ + delete m_extension; +} + +QGeoRouteSegment QGeoRouteParserOsrmV5Private::parseStep(const QJsonObject &step, int legIndex, int stepIndex) const { // 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". @@ -810,10 +838,6 @@ static QGeoRouteSegment parseStep(const QJsonObject &step, bool useServerText, i 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(); @@ -827,11 +851,15 @@ static QGeoRouteSegment parseStep(const QJsonObject &step, bool useServerText, i QString geometry = step.value(QLatin1String("geometry")).toString(); QList<QGeoCoordinate> path = decodePolyline(geometry); + QGeoManeuver::InstructionDirection maneuverInstructionDirection = instructionDirection(maneuver); + + QString maneuverInstructionText = instructionText(step, maneuver, maneuverInstructionDirection); + QGeoManeuver geoManeuver; - geoManeuver.setDirection(instructionDirection(maneuver)); + geoManeuver.setDirection(maneuverInstructionDirection); geoManeuver.setDistanceToNextInstruction(distance); geoManeuver.setTimeToNextInstruction(time); - geoManeuver.setInstructionText((useServerText && !instruction_text.isEmpty()) ? instruction_text : instructionText(step, maneuver, geoManeuver.direction())); + geoManeuver.setInstructionText(maneuverInstructionText); geoManeuver.setPosition(coord); geoManeuver.setWaypoint(coord); @@ -850,45 +878,27 @@ static QGeoRouteSegment parseStep(const QJsonObject &step, bool useServerText, i // Ref: http://project-osrm.org/docs/v5.15.2/api/#routeleg-object extraAttributes.insert(QLatin1String("leg_index"), legIndex); extraAttributes.insert(QLatin1String("step_index"), stepIndex); + geoManeuver.setExtendedAttributes(extraAttributes); segment.setDistance(distance); segment.setPath(path); segment.setTravelTime(time); segment.setManeuver(geoManeuver); + if (m_extension) + m_extension->updateSegment(segment, step, maneuver); return segment; } -class QGeoRouteParserOsrmV5Private : public QGeoRouteParserPrivate -{ - Q_DECLARE_PUBLIC(QGeoRouteParserOsrmV5) -public: - QGeoRouteParserOsrmV5Private(); - virtual ~QGeoRouteParserOsrmV5Private(); - - QGeoRouteReply::Error parseReply(QList<QGeoRoute> &routes, QString &errorString, const QByteArray &reply) const override; - QUrl requestUrl(const QGeoRouteRequest &request, const QString &prefix) const override; - - bool m_useServerText = false; - QString m_accessToken; -}; - -QGeoRouteParserOsrmV5Private::QGeoRouteParserOsrmV5Private() : QGeoRouteParserPrivate() -{ -} - -QGeoRouteParserOsrmV5Private::~QGeoRouteParserOsrmV5Private() -{ -} - QGeoRouteReply::Error QGeoRouteParserOsrmV5Private::parseReply(QList<QGeoRoute> &routes, QString &errorString, const QByteArray &reply) const { // OSRM v5 specs: https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md + // Mapbox Directions API spec: https://www.mapbox.com/api-documentation/#directions QJsonDocument document = QJsonDocument::fromJson(reply); if (document.isObject()) { QJsonObject object = document.object(); - QString status = object.value(QStringLiteral("code")).toString(); + QString status = object.value(QLatin1String("code")).toString(); if (status != QLatin1String("Ok")) { errorString = status; return QGeoRouteReply::UnknownError; @@ -934,7 +944,7 @@ QGeoRouteReply::Error QGeoRouteParserOsrmV5Private::parseReply(QList<QGeoRoute> error = true; break; } - QGeoRouteSegment segment = parseStep(s.toObject(), m_useServerText, legIndex, stepIndex); + QGeoRouteSegment segment = parseStep(s.toObject(), legIndex, stepIndex); if (segment.isValid()) { segments.append(segment); } else { @@ -969,7 +979,7 @@ QGeoRouteReply::Error QGeoRouteParserOsrmV5Private::parseReply(QList<QGeoRoute> // setError(QGeoRouteReply::NoError, status); // can't do this, or NoError is emitted and does damages return QGeoRouteReply::NoError; } else { - errorString = QStringLiteral("Couldn't parse json."); + errorString = QLatin1String("Couldn't parse json."); return QGeoRouteReply::ParseError; } } @@ -990,11 +1000,11 @@ QUrl QGeoRouteParserOsrmV5Private::requestUrl(const QGeoRouteRequest &request, c routingUrl.append(QString::number(c.longitude(), 'f', 7)).append(QLatin1Char(',')).append(QString::number(c.latitude(), 'f', 7)); if (metadata.size() > i) { const QVariantMap &meta = metadata.at(i); - if (meta.contains(QStringLiteral("bearing"))) { - qreal bearing = meta.value(QStringLiteral("bearing")).toDouble(); - bearings.append(QString::number(int(bearing))).append(QLatin1Char(',')).append(QStringLiteral("90")); // 90 is the angle of maneuver allowed. + if (meta.contains(QLatin1String("bearing"))) { + qreal bearing = meta.value(QLatin1String("bearing")).toDouble(); + bearings.append(QString::number(int(bearing))).append(QLatin1Char(',')).append(QLatin1String("90")); // 90 is the angle of maneuver allowed. } else { - bearings.append(QStringLiteral("0,180")); // 180 here means anywhere + bearings.append(QLatin1String("0,180")); // 180 here means anywhere } } ++notFirst; @@ -1002,31 +1012,31 @@ QUrl QGeoRouteParserOsrmV5Private::requestUrl(const QGeoRouteRequest &request, c QUrl url(routingUrl); QUrlQuery query; - query.addQueryItem(QStringLiteral("overview"), QStringLiteral("full")); - query.addQueryItem(QStringLiteral("steps"), QStringLiteral("true")); - query.addQueryItem(QStringLiteral("geometries"), QStringLiteral("polyline6")); - query.addQueryItem(QStringLiteral("alternatives"), QStringLiteral("true")); - query.addQueryItem(QStringLiteral("bearings"), bearings); - if (!m_accessToken.isEmpty()) - query.addQueryItem(QStringLiteral("access_token"), m_accessToken); + query.addQueryItem(QLatin1String("overview"), QLatin1String("full")); + query.addQueryItem(QLatin1String("steps"), QLatin1String("true")); + query.addQueryItem(QLatin1String("geometries"), QLatin1String("polyline6")); + query.addQueryItem(QLatin1String("alternatives"), QLatin1String("true")); + query.addQueryItem(QLatin1String("bearings"), bearings); + if (m_extension) + m_extension->updateQuery(query); url.setQuery(query); return url; } -QGeoRouteParserOsrmV5::QGeoRouteParserOsrmV5(QObject *parent, bool useServerText) : QGeoRouteParser(*new QGeoRouteParserOsrmV5Private(), parent) +QGeoRouteParserOsrmV5::QGeoRouteParserOsrmV5(QObject *parent) + : QGeoRouteParser(*new QGeoRouteParserOsrmV5Private(), parent) { - Q_D(QGeoRouteParserOsrmV5); - d->m_useServerText = useServerText; } QGeoRouteParserOsrmV5::~QGeoRouteParserOsrmV5() { } -void QGeoRouteParserOsrmV5::setAccessToken(const QString &token) +void QGeoRouteParserOsrmV5::setExtension(const QGeoRouteParserOsrmV5Extension *extension) { Q_D(QGeoRouteParserOsrmV5); - d->m_accessToken = token; + if (extension) + d->m_extension = extension; } QT_END_NAMESPACE diff --git a/src/location/maps/qgeorouteparserosrmv5_p.h b/src/location/maps/qgeorouteparserosrmv5_p.h index 598dcde7..7f33af54 100644 --- a/src/location/maps/qgeorouteparserosrmv5_p.h +++ b/src/location/maps/qgeorouteparserosrmv5_p.h @@ -54,16 +54,32 @@ QT_BEGIN_NAMESPACE class QGeoRouteParserOsrmV5Private; + +class Q_LOCATION_PRIVATE_EXPORT QGeoRouteParserOsrmV5Extension +{ +public: + QGeoRouteParserOsrmV5Extension() + { + } + + virtual ~QGeoRouteParserOsrmV5Extension() + { + } + + virtual void updateQuery(QUrlQuery &query) const = 0; + virtual void updateSegment(QGeoRouteSegment &segment, const QJsonObject &step, const QJsonObject &maneuver) const = 0; +}; + class Q_LOCATION_PRIVATE_EXPORT QGeoRouteParserOsrmV5 : public QGeoRouteParser { Q_OBJECT Q_DECLARE_PRIVATE(QGeoRouteParserOsrmV5) public: - QGeoRouteParserOsrmV5(QObject *parent = nullptr, bool useServerText = false); + QGeoRouteParserOsrmV5(QObject *parent = nullptr); virtual ~QGeoRouteParserOsrmV5(); - void setAccessToken(const QString &token); + void setExtension(const QGeoRouteParserOsrmV5Extension *extension); private: Q_DISABLE_COPY(QGeoRouteParserOsrmV5) diff --git a/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp b/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp index 2697114d..73bca2f7 100644 --- a/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp +++ b/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp @@ -41,13 +41,175 @@ #include "qgeoroutingmanagerenginemapbox.h" #include "qgeoroutereplymapbox.h" #include "qmapboxcommon.h" -#include "QtLocation/private/qgeorouteparserosrmv5_p.h" +#include <QtLocation/private/qgeorouteparserosrmv5_p.h> +#include <QtLocation/qgeoroutesegment.h> +#include <QtLocation/qgeomaneuver.h> +#include <QtCore/QJsonDocument> +#include <QtCore/QJsonObject> +#include <QtCore/QJsonArray> #include <QtCore/QUrlQuery> #include <QtCore/QDebug> QT_BEGIN_NAMESPACE +class QGeoRouteParserOsrmV5ExtensionMapbox: public QGeoRouteParserOsrmV5Extension +{ +public: + QGeoRouteParserOsrmV5ExtensionMapbox(const QString &accessToken, bool useMapboxTextInstructions); + void updateQuery(QUrlQuery &query) const override; + void updateSegment(QGeoRouteSegment &segment, const QJsonObject &step, const QJsonObject &maneuver) const override; + + QString m_accessToken; + bool m_useMapboxTextInstructions = false; +}; + +QGeoRouteParserOsrmV5ExtensionMapbox::QGeoRouteParserOsrmV5ExtensionMapbox(const QString &accessToken, bool useMapboxTextInstructions) + : QGeoRouteParserOsrmV5Extension(), m_accessToken(accessToken), m_useMapboxTextInstructions(useMapboxTextInstructions) +{ + +} + +void QGeoRouteParserOsrmV5ExtensionMapbox::updateQuery(QUrlQuery &query) const +{ + if (!m_accessToken.isEmpty()) + query.addQueryItem(QLatin1String("access_token"), m_accessToken); + + query.addQueryItem(QLatin1String("voice_instructions"), QLatin1String("true")); + query.addQueryItem(QLatin1String("banner_instructions"), QLatin1String("true")); +} + +static QVariantMap parseMapboxVoiceInstruction(const QJsonObject &voiceInstruction) +{ + QVariantMap map; + + if (voiceInstruction.value(QLatin1String("distanceAlongGeometry")).isDouble()) + map.insert(QLatin1String("distance_along_geometry"), voiceInstruction.value(QLatin1String("distanceAlongGeometry")).toDouble()); + + if (voiceInstruction.value(QLatin1String("announcement")).isString()) + map.insert(QLatin1String("announcement"), voiceInstruction.value(QLatin1String("announcement")).toString()); + + if (voiceInstruction.value(QLatin1String("ssmlAnnouncement")).isString()) + map.insert(QLatin1String("ssml_announcement"), voiceInstruction.value(QLatin1String("ssmlAnnouncement")).toString()); + + return map; +} + +static QVariantList parseMapboxVoiceInstructions(const QJsonArray &voiceInstructions) +{ + QVariantList list; + for (const QJsonValue &voiceInstructionValue : voiceInstructions) { + if (voiceInstructionValue.isObject()) + list << parseMapboxVoiceInstruction(voiceInstructionValue.toObject()); + } + return list; +} + +static QVariantMap parseMapboxBannerComponent(const QJsonObject &bannerComponent) +{ + QVariantMap map; + + if (bannerComponent.value(QLatin1String("type")).isString()) + map.insert(QLatin1String("type"), bannerComponent.value(QLatin1String("type")).toString()); + + if (bannerComponent.value(QLatin1String("text")).isString()) + map.insert(QLatin1String("text"), bannerComponent.value(QLatin1String("text")).toString()); + + if (bannerComponent.value(QLatin1String("abbr")).isString()) + map.insert(QLatin1String("abbr"), bannerComponent.value(QLatin1String("abbr")).toString()); + + if (bannerComponent.value(QLatin1String("abbr_priority")).isDouble()) + map.insert(QLatin1String("abbr_priority"), bannerComponent.value(QLatin1String("abbr_priority")).toInt()); + + return map; +} + +static QVariantList parseMapboxBannerComponents(const QJsonArray &bannerComponents) +{ + QVariantList list; + for (const QJsonValue &bannerComponentValue : bannerComponents) { + if (bannerComponentValue.isObject()) + list << parseMapboxBannerComponent(bannerComponentValue.toObject()); + } + return list; +} + +static QVariantMap parseMapboxBanner(const QJsonObject &banner) +{ + QVariantMap map; + + if (banner.value(QLatin1String("text")).isString()) + map.insert(QLatin1String("text"), banner.value(QLatin1String("text")).toString()); + + if (banner.value(QLatin1String("components")).isArray()) + map.insert(QLatin1String("components"), parseMapboxBannerComponents(banner.value(QLatin1String("components")).toArray())); + + if (banner.value(QLatin1String("type")).isString()) + map.insert(QLatin1String("type"), banner.value(QLatin1String("type")).toString()); + + if (banner.value(QLatin1String("modifier")).isString()) + map.insert(QLatin1String("modifier"), banner.value(QLatin1String("modifier")).toString()); + + if (banner.value(QLatin1String("degrees")).isDouble()) + map.insert(QLatin1String("degrees"), banner.value(QLatin1String("degrees")).toDouble()); + + if (banner.value(QLatin1String("driving_side")).isString()) + map.insert(QLatin1String("driving_side"), banner.value(QLatin1String("driving_side")).toString()); + + return map; +} + +static QVariantMap parseMapboxBannerInstruction(const QJsonObject &bannerInstruction) +{ + QVariantMap map; + + if (bannerInstruction.value(QLatin1String("distanceAlongGeometry")).isDouble()) + map.insert(QLatin1String("distance_along_geometry"), bannerInstruction.value(QLatin1String("distanceAlongGeometry")).toDouble()); + + if (bannerInstruction.value(QLatin1String("primary")).isObject()) + map.insert(QLatin1String("primary"), parseMapboxBanner(bannerInstruction.value(QLatin1String("primary")).toObject())); + + if (bannerInstruction.value(QLatin1String("secondary")).isObject()) + map.insert(QLatin1String("secondary"), parseMapboxBanner(bannerInstruction.value(QLatin1String("secondary")).toObject())); + + if (bannerInstruction.value(QLatin1String("then")).isObject()) + map.insert(QLatin1String("then"), parseMapboxBanner(bannerInstruction.value(QLatin1String("then")).toObject())); + + return map; +} + +static QVariantList parseMapboxBannerInstructions(const QJsonArray &bannerInstructions) +{ + QVariantList list; + for (const QJsonValue &bannerInstructionValue : bannerInstructions) { + if (bannerInstructionValue.isObject()) + list << parseMapboxBannerInstruction(bannerInstructionValue.toObject()); + } + return list; +} + +void QGeoRouteParserOsrmV5ExtensionMapbox::updateSegment(QGeoRouteSegment &segment, const QJsonObject &step, const QJsonObject &maneuver) const +{ + QGeoManeuver m = segment.maneuver(); + QVariantMap extendedAttributes = m.extendedAttributes(); + if (m_useMapboxTextInstructions && maneuver.value(QLatin1String("instruction")).isString()) { + QString maneuverInstructionText = maneuver.value(QLatin1String("instruction")).toString(); + if (!maneuverInstructionText.isEmpty()) + m.setInstructionText(maneuverInstructionText); + } + + if (step.value(QLatin1String("voiceInstructions")).isArray()) + extendedAttributes.insert(QLatin1String("mapbox.voice_instructions"), + parseMapboxVoiceInstructions(step.value(QLatin1String("voiceInstructions")).toArray())); + if (step.value(QLatin1String("bannerInstructions")).isArray()) + extendedAttributes.insert(QLatin1String("mapbox.banner_instructions"), + parseMapboxBannerInstructions(step.value(QLatin1String("bannerInstructions")).toArray())); + + m.setExtendedAttributes(extendedAttributes); + segment.setManeuver(m); +} + + QGeoRoutingManagerEngineMapbox::QGeoRoutingManagerEngineMapbox(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) @@ -68,8 +230,9 @@ QGeoRoutingManagerEngineMapbox::QGeoRoutingManagerEngineMapbox(const QVariantMap 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); + QGeoRouteParserOsrmV5 *parser = new QGeoRouteParserOsrmV5(this); + parser->setExtension(new QGeoRouteParserOsrmV5ExtensionMapbox(m_accessToken, use_mapbox_text_instructions)); + m_routeParser = parser; *error = QGeoServiceProvider::NoError; |