// Copyright (C) 2016 Vlad Seryakov // Copyright (C) 2016 Aaron McCarthy // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qgeoroutingmanagerenginemapbox.h" #include "qgeoroutereplymapbox.h" #include "qmapboxcommon.h" #include #include #include #include #include #include #include #include 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("annotations"), QLatin1String("duration,distance,speed,congestion")); query.addQueryItem(QLatin1String("voice_instructions"), QLatin1String("true")); query.addQueryItem(QLatin1String("banner_instructions"), QLatin1String("true")); query.addQueryItem(QLatin1String("roundabout_exits"), QLatin1String("true")); QLocale::MeasurementSystem unit = QLocale::system().measurementSystem(); query.addQueryItem(QLatin1String("voice_units"), unit == QLocale::MetricSystem ? QLatin1String("metric") : QLatin1String("imperial")); } 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 QJsonValueConstRef 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 QJsonValueConstRef 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 QJsonValueConstRef 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) : QGeoRoutingManagerEngine(parameters), m_networkManager(new QNetworkAccessManager(this)), m_userAgent(mapboxDefaultUserAgent) { if (parameters.contains(QStringLiteral("mapbox.useragent"))) { m_userAgent = parameters.value(QStringLiteral("mapbox.useragent")).toString().toLatin1(); } if (parameters.contains(QStringLiteral("mapbox.access_token"))) { 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.routing.use_mapbox_text_instructions")).toBool(); } QGeoRouteParserOsrmV5 *parser = new QGeoRouteParserOsrmV5(this); parser->setExtension(new QGeoRouteParserOsrmV5ExtensionMapbox(m_accessToken, use_mapbox_text_instructions)); if (parameters.contains(QStringLiteral("mapbox.routing.traffic_side"))) { QString trafficSide = parameters.value(QStringLiteral("mapbox.routing.traffic_side")).toString(); if (trafficSide == QStringLiteral("right")) parser->setTrafficSide(QGeoRouteParser::RightHandTraffic); else if (trafficSide == QStringLiteral("left")) parser->setTrafficSide(QGeoRouteParser::LeftHandTraffic); } m_routeParser = parser; *error = QGeoServiceProvider::NoError; errorString->clear(); } QGeoRoutingManagerEngineMapbox::~QGeoRoutingManagerEngineMapbox() { } QGeoRouteReply* QGeoRoutingManagerEngineMapbox::calculateRoute(const QGeoRouteRequest &request) { QNetworkRequest networkRequest; networkRequest.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); QString url = mapboxDirectionsApiPath; QGeoRouteRequest::TravelModes travelModes = request.travelModes(); if (travelModes.testFlag(QGeoRouteRequest::PedestrianTravel)) { url += QStringLiteral("walking/"); } else if (travelModes.testFlag(QGeoRouteRequest::BicycleTravel)) { url += QStringLiteral("cycling/"); } else if (travelModes.testFlag(QGeoRouteRequest::CarTravel)) { const QList &featureTypes = request.featureTypes(); int trafficFeatureIdx = featureTypes.indexOf(QGeoRouteRequest::TrafficFeature); QGeoRouteRequest::FeatureWeight trafficWeight = request.featureWeight(QGeoRouteRequest::TrafficFeature); if (trafficFeatureIdx >= 0 && (trafficWeight == QGeoRouteRequest::AvoidFeatureWeight || trafficWeight == QGeoRouteRequest::DisallowFeatureWeight)) { url += QStringLiteral("driving-traffic/"); } else { url += QStringLiteral("driving/"); } } networkRequest.setUrl(m_routeParser->requestUrl(request, url)); QNetworkReply *reply = m_networkManager->get(networkRequest); QGeoRouteReplyMapbox *routeReply = new QGeoRouteReplyMapbox(reply, request, this); connect(routeReply, &QGeoRouteReplyMapbox::finished, this, &QGeoRoutingManagerEngineMapbox::replyFinished); connect(routeReply, &QGeoRouteReplyMapbox::errorOccurred, this, &QGeoRoutingManagerEngineMapbox::replyError); return routeReply; } const QGeoRouteParser *QGeoRoutingManagerEngineMapbox::routeParser() const { return m_routeParser; } void QGeoRoutingManagerEngineMapbox::replyFinished() { QGeoRouteReply *reply = qobject_cast(sender()); if (reply) emit finished(reply); } void QGeoRoutingManagerEngineMapbox::replyError(QGeoRouteReply::Error errorCode, const QString &errorString) { QGeoRouteReply *reply = qobject_cast(sender()); if (reply) emit errorOccurred(reply, errorCode, errorString); } QT_END_NAMESPACE