summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruno de Oliveira Abinader <bruno@mapbox.com>2018-04-09 16:37:38 +0300
committerPaolo Angelelli <paolo.angelelli@qt.io>2018-04-11 09:26:40 +0000
commitfdce040790a070701b89aed8c1c798ba183215dd (patch)
tree2f83240370617a08e8745b9cef3669e15a7fc541
parenta4d9f8cbf2220fbc8aafd8f491313e39e0bf4a5f (diff)
downloadqtlocation-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.cpp110
-rw-r--r--src/location/maps/qgeorouteparserosrmv5_p.h20
-rw-r--r--src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp169
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 &parameters,
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;