diff options
author | Vlad Seryakov <vseryakov@gmail.com> | 2016-06-30 00:58:31 -0400 |
---|---|---|
committer | Paolo Angelelli <paolo.angelelli@theqtcompany.com> | 2016-07-22 12:51:08 +0000 |
commit | 1753409f92a6b865b71612465b887f808568ada2 (patch) | |
tree | 945297e08821e357a1c501abc3fe1dccdc3b28c5 /src/plugins/geoservices | |
parent | 3f3481205c19a253cc9f37590eddaf302f3d91b7 (diff) | |
download | qtlocation-1753409f92a6b865b71612465b887f808568ada2.tar.gz |
Add routing support for the mapbox geoservice plugin
This patch adds routing support for the mapbox plugin
Change-Id: Ibaf9d9d8a845b49cb8a6fc0d3530ffe57f4989db
Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
Diffstat (limited to 'src/plugins/geoservices')
7 files changed, 518 insertions, 7 deletions
diff --git a/src/plugins/geoservices/mapbox/mapbox.pro b/src/plugins/geoservices/mapbox/mapbox.pro index e4601500..ea011131 100644 --- a/src/plugins/geoservices/mapbox/mapbox.pro +++ b/src/plugins/geoservices/mapbox/mapbox.pro @@ -7,14 +7,18 @@ HEADERS += \ qgeotiledmappingmanagerenginemapbox.h \ qgeotilefetchermapbox.h \ qgeomapreplymapbox.h \ - qgeofiletilecachemapbox.h + qgeofiletilecachemapbox.h \ + qgeoroutingmanagerenginemapbox.h \ + qgeoroutereplymapbox.h SOURCES += \ qgeoserviceproviderpluginmapbox.cpp \ qgeotiledmappingmanagerenginemapbox.cpp \ qgeotilefetchermapbox.cpp \ qgeomapreplymapbox.cpp \ - qgeofiletilecachemapbox.cpp + qgeofiletilecachemapbox.cpp \ + qgeoroutingmanagerenginemapbox.cpp \ + qgeoroutereplymapbox.cpp OTHER_FILES += \ mapbox_plugin.json diff --git a/src/plugins/geoservices/mapbox/mapbox_plugin.json b/src/plugins/geoservices/mapbox/mapbox_plugin.json index 0b8d08af..f886458f 100644 --- a/src/plugins/geoservices/mapbox/mapbox_plugin.json +++ b/src/plugins/geoservices/mapbox/mapbox_plugin.json @@ -4,6 +4,7 @@ "Version": 100, "Experimental": false, "Features": [ - "OnlineMappingFeature" + "OnlineMappingFeature", + "OnlineRoutingFeature" ] } diff --git a/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp b/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp new file mode 100644 index 00000000..4c98412b --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Vlad Seryakov <vseryakov@gmail.com> +** Copyright (C) 2016 Aaron McCarthy <mccarthy.aaron@gmail.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoroutereplymapbox.h" + +#include <QtCore/QJsonDocument> +#include <QtCore/QJsonObject> +#include <QtCore/QJsonArray> +#include <QtLocation/QGeoRouteSegment> +#include <QtLocation/QGeoManeuver> + +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), m_reply(reply) +{ + connect(m_reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); + connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(networkReplyError(QNetworkReply::NetworkError))); +} + +QGeoRouteReplyMapbox::~QGeoRouteReplyMapbox() +{ + if (m_reply) + m_reply->deleteLater(); +} + +void QGeoRouteReplyMapbox::abort() +{ + if (!m_reply) + return; + + m_reply->abort(); + + m_reply->deleteLater(); + m_reply = 0; +} + +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() +{ + if (!m_reply) + return; + + if (m_reply->error() != QNetworkReply::NoError) { + setError(QGeoRouteReply::CommunicationError, m_reply->errorString()); + m_reply->deleteLater(); + m_reply = 0; + return; + } + + QJsonDocument document = QJsonDocument::fromJson(m_reply->readAll()); + if (document.isObject()) { + QJsonObject object = document.object(); + + QString status = object.value(QStringLiteral("code")).toString(); + if (status != QStringLiteral("Ok")) { + setError(QGeoRouteReply::UnknownError, object.value(QStringLiteral("message")).toString()); + m_reply->deleteLater(); + m_reply = 0; + return; + } + + 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); + setFinished(true); + } else { + setError(QGeoRouteReply::ParseError, QStringLiteral("Couldn't parse json.")); + } + + m_reply->deleteLater(); + m_reply = 0; +} + +void QGeoRouteReplyMapbox::networkReplyError(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + + if (!m_reply) + return; + + setError(QGeoRouteReply::CommunicationError, m_reply->errorString()); + + m_reply->deleteLater(); + m_reply = 0; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.h b/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.h new file mode 100644 index 00000000..9df45ac4 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Vlad Seryakov <vseryakov@gmail.com> +** Copyright (C) 2016 Aaron McCarthy <mccarthy.aaron@gmail.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTEREPLYMAPBOX_H +#define QGEOROUTEREPLYMAPBOX_H + +#include <QtNetwork/QNetworkReply> +#include <QtLocation/QGeoRouteReply> + +QT_BEGIN_NAMESPACE + +class QGeoRouteReplyMapbox : public QGeoRouteReply +{ + Q_OBJECT + +public: + explicit QGeoRouteReplyMapbox(QObject *parent = 0); + QGeoRouteReplyMapbox(QNetworkReply *reply, const QGeoRouteRequest &request, QObject *parent = 0); + ~QGeoRouteReplyMapbox(); + + void abort() Q_DECL_OVERRIDE; + +private Q_SLOTS: + void networkReplyFinished(); + void networkReplyError(QNetworkReply::NetworkError error); + +private: + QNetworkReply *m_reply; +}; + +QT_END_NAMESPACE + +#endif // QGEOROUTEREPLYMAPBOX_H + diff --git a/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp b/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp new file mode 100644 index 00000000..d6ef8f0a --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Vlad Seryakov <vseryakov@gmail.com> +** Copyright (C) 2016 Aaron McCarthy <mccarthy.aaron@gmail.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoroutingmanagerenginemapbox.h" +#include "qgeoroutereplymapbox.h" + +#include <QtCore/QUrlQuery> +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE + +QGeoRoutingManagerEngineMapbox::QGeoRoutingManagerEngineMapbox(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) + : QGeoRoutingManagerEngine(parameters), + m_networkManager(new QNetworkAccessManager(this)), + m_userAgent("Qt Location based application") +{ + 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(); + } + + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +QGeoRoutingManagerEngineMapbox::~QGeoRoutingManagerEngineMapbox() +{ +} + +QGeoRouteReply* QGeoRoutingManagerEngineMapbox::calculateRoute(const QGeoRouteRequest &request) +{ + QNetworkRequest networkRequest; + networkRequest.setRawHeader("User-Agent", m_userAgent); + + QString url("https://api.mapbox.com/directions/v5/mapbox/"); + + QGeoRouteRequest::TravelModes travelModes = request.travelModes(); + if (travelModes.testFlag(QGeoRouteRequest::PedestrianTravel)) + url += "walking/"; + else + if (travelModes.testFlag(QGeoRouteRequest::BicycleTravel)) + url += "cycling/"; + else + if (travelModes.testFlag(QGeoRouteRequest::CarTravel)) + url += "driving/"; + + foreach (const QGeoCoordinate &c, request.waypoints()) { + url += QString("%1,%2;").arg(c.longitude()).arg(c.latitude()); + } + if (url.right(1) == ";") url.chop(1); + url += QString("?steps=true&overview=full&geometries=geojson&access_token=%1").arg(m_accessToken); + + networkRequest.setUrl(QUrl(url)); + + QNetworkReply *reply = m_networkManager->get(networkRequest); + QGeoRouteReplyMapbox *routeReply = new QGeoRouteReplyMapbox(reply, request, this); + + connect(routeReply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(routeReply, SIGNAL(error(QGeoRouteReply::Error,QString)), + this, SLOT(replyError(QGeoRouteReply::Error,QString))); + + return routeReply; +} + +void QGeoRoutingManagerEngineMapbox::replyFinished() +{ + QGeoRouteReply *reply = qobject_cast<QGeoRouteReply *>(sender()); + if (reply) + emit finished(reply); +} + +void QGeoRoutingManagerEngineMapbox::replyError(QGeoRouteReply::Error errorCode, + const QString &errorString) +{ + QGeoRouteReply *reply = qobject_cast<QGeoRouteReply *>(sender()); + if (reply) + emit error(reply, errorCode, errorString); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.h b/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.h new file mode 100644 index 00000000..5b440147 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Vlad Seryakov <vseryakov@gmail.com> +** Copyright (C) 2016 Aaron McCarthy <mccarthy.aaron@gmail.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTINGMANAGERENGINEMAPBOX_H +#define QGEOROUTINGMANAGERENGINEMAPBOX_H + +#include <QtLocation/QGeoServiceProvider> +#include <QtLocation/QGeoRoutingManagerEngine> + +QT_BEGIN_NAMESPACE + +class QNetworkAccessManager; + +class QGeoRoutingManagerEngineMapbox : public QGeoRoutingManagerEngine +{ + Q_OBJECT + +public: + QGeoRoutingManagerEngineMapbox(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString); + ~QGeoRoutingManagerEngineMapbox(); + + QGeoRouteReply *calculateRoute(const QGeoRouteRequest &request); + +private Q_SLOTS: + void replyFinished(); + void replyError(QGeoRouteReply::Error errorCode, const QString &errorString); + +private: + QNetworkAccessManager *m_networkManager; + QByteArray m_userAgent; + QString m_accessToken; +}; + +QT_END_NAMESPACE + +#endif // QGEOROUTINGMANAGERENGINEOSM_H + diff --git a/src/plugins/geoservices/mapbox/qgeoserviceproviderpluginmapbox.cpp b/src/plugins/geoservices/mapbox/qgeoserviceproviderpluginmapbox.cpp index 8638ad91..4bc8d7b9 100644 --- a/src/plugins/geoservices/mapbox/qgeoserviceproviderpluginmapbox.cpp +++ b/src/plugins/geoservices/mapbox/qgeoserviceproviderpluginmapbox.cpp @@ -36,6 +36,7 @@ #include "qgeoserviceproviderpluginmapbox.h" #include "qgeotiledmappingmanagerenginemapbox.h" +#include "qgeoroutingmanagerenginemapbox.h" #include <QtLocation/private/qgeotiledmappingmanagerengine_p.h> @@ -69,11 +70,16 @@ QGeoMappingManagerEngine *QGeoServiceProviderFactoryMapbox::createMappingManager QGeoRoutingManagerEngine *QGeoServiceProviderFactoryMapbox::createRoutingManagerEngine( const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const { - Q_UNUSED(parameters) - Q_UNUSED(error) - Q_UNUSED(errorString) + const QString accessToken = parameters.value(QStringLiteral("mapbox.access_token")).toString(); - return 0; + if (!accessToken.isEmpty()) { + return new QGeoRoutingManagerEngineMapbox(parameters, error, errorString); + } else { + *error = QGeoServiceProvider::MissingRequiredParameterError; + *errorString = tr("Mapbox plugin requires 'mapbox.access_token' parameters.\n" + "Please visit https://www.mapbox.com"); + return 0; + } } QPlaceManagerEngine *QGeoServiceProviderFactoryMapbox::createPlaceManagerEngine( |