diff options
Diffstat (limited to 'src/plugins')
76 files changed, 5239 insertions, 487 deletions
diff --git a/src/plugins/geoservices/esri/esri.pro b/src/plugins/geoservices/esri/esri.pro new file mode 100644 index 00000000..3642ddaf --- /dev/null +++ b/src/plugins/geoservices/esri/esri.pro @@ -0,0 +1,39 @@ +TARGET = qtgeoservices_esri + +QT += location-private positioning-private network + +HEADERS += \ + geocodereply_esri.h \ + geocodingmanagerengine_esri.h \ + geomapsource.h \ + georoutejsonparser_esri.h \ + georoutereply_esri.h \ + georoutingmanagerengine_esri.h \ + geoserviceproviderfactory_esri.h \ + geotiledmap_esri.h \ + geotiledmappingmanagerengine_esri.h \ + geotiledmapreply_esri.h \ + geotilefetcher_esri.h + +SOURCES += \ + geocodereply_esri.cpp \ + geocodingmanagerengine_esri.cpp \ + geomapsource.cpp \ + georoutejsonparser_esri.cpp \ + georoutereply_esri.cpp \ + georoutingmanagerengine_esri.cpp \ + geoserviceproviderfactory_esri.cpp \ + geotiledmap_esri.cpp \ + geotiledmappingmanagerengine_esri.cpp \ + geotiledmapreply_esri.cpp \ + geotilefetcher_esri.cpp + +RESOURCES += \ + esri.qrc + +OTHER_FILES += \ + esri_plugin.json + +PLUGIN_TYPE = geoservices +PLUGIN_CLASS_NAME = GeoServiceProviderFactoryEsri +load(qt_plugin) diff --git a/src/plugins/geoservices/esri/esri.qrc b/src/plugins/geoservices/esri/esri.qrc new file mode 100644 index 00000000..43b0857f --- /dev/null +++ b/src/plugins/geoservices/esri/esri.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>maps.json</file> + </qresource> +</RCC> diff --git a/src/plugins/geoservices/esri/esri_plugin.json b/src/plugins/geoservices/esri/esri_plugin.json new file mode 100644 index 00000000..3398648e --- /dev/null +++ b/src/plugins/geoservices/esri/esri_plugin.json @@ -0,0 +1,13 @@ +{ + "Keys": ["esri"], + "Provider": "esri", + "Version": 100, + "Experimental": false, + "Features": [ + "OnlineMappingFeature", + "OnlineGeocodingFeature", + "ReverseGeocodingFeature", + "OnlineRoutingFeature" + ], + "Priority": 1000 +} diff --git a/src/plugins/geoservices/esri/geocodereply_esri.cpp b/src/plugins/geoservices/esri/geocodereply_esri.cpp new file mode 100644 index 00000000..a7ad9368 --- /dev/null +++ b/src/plugins/geoservices/esri/geocodereply_esri.cpp @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 "geocodereply_esri.h" + +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> +#include <QGeoCoordinate> +#include <QGeoAddress> +#include <QGeoLocation> +#include <QGeoRectangle> + +QT_BEGIN_NAMESPACE + +GeoCodeReplyEsri::GeoCodeReplyEsri(QNetworkReply *reply, OperationType operationType, + QObject *parent) : + QGeoCodeReply(parent), m_reply(reply), m_operationType(operationType) +{ + connect(m_reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); + connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(networkReplyError(QNetworkReply::NetworkError))); + + setLimit(1); + setOffset(0); +} + +GeoCodeReplyEsri::~GeoCodeReplyEsri() +{ + if (m_reply) + m_reply->deleteLater(); +} + +void GeoCodeReplyEsri::abort() +{ + if (!m_reply) + return; + + m_reply->abort(); + QGeoCodeReply::abort(); + + m_reply->deleteLater(); + m_reply = Q_NULLPTR; +} + +void GeoCodeReplyEsri::networkReplyError(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + + if (!m_reply) + return; + + setError(QGeoCodeReply::CommunicationError, m_reply->errorString()); + + m_reply->deleteLater(); + m_reply = Q_NULLPTR; +} + +void GeoCodeReplyEsri::networkReplyFinished() +{ + if (!m_reply) + return; + + if (m_reply->error() != QNetworkReply::NoError) + { + setError(QGeoCodeReply::CommunicationError, m_reply->errorString()); + m_reply->deleteLater(); + m_reply = Q_NULLPTR; + return; + } + + QJsonDocument document = QJsonDocument::fromJson(m_reply->readAll()); + + if (document.isObject()) { + QJsonObject object = document.object(); + + switch (operationType()) { + case Geocode: + { + QJsonArray candidates = object.value(QStringLiteral("candidates")).toArray(); + + QList<QGeoLocation> locations; + + for (int i = 0; i < candidates.count(); i++) { + if (!candidates.at(i).isObject()) + continue; + + QJsonObject candidate = candidates.at(i).toObject(); + + QGeoLocation location = parseCandidate(candidate); + locations.append(location); + } + + setLocations(locations); + setFinished(true); + } + break; + + case ReverseGeocode: + { + QGeoLocation location = parseAddress(object); + + QList<QGeoLocation> locations; + locations.append(location); + + setLocations(locations); + setFinished(true); + } + break; + } + + } else { + setError(QGeoCodeReply::CommunicationError, QStringLiteral("Unknown document")); + } + + m_reply->deleteLater(); + m_reply = Q_NULLPTR; +} + +QGeoLocation GeoCodeReplyEsri::parseAddress(const QJsonObject& object) +{ + QJsonObject addressObject = object.value(QStringLiteral("address")).toObject(); + + QGeoAddress address; + + address.setCountryCode(addressObject.value(QStringLiteral("CountryCode")).toString()); + address.setState(addressObject.value(QStringLiteral("Region")).toString()); + address.setCity(addressObject.value(QStringLiteral("City")).toString()); + address.setDistrict(addressObject.value(QStringLiteral("Subregion")).toString()); + address.setPostalCode(addressObject.value(QStringLiteral("Postal")).toString()); + address.setStreet(addressObject.value(QStringLiteral("Address")).toString()); + + QGeoCoordinate coordinate; + + QJsonObject locationObject = object.value(QStringLiteral("location")).toObject(); + + coordinate.setLongitude(locationObject.value(QStringLiteral("x")).toDouble()); + coordinate.setLatitude(locationObject.value(QStringLiteral("y")).toDouble()); + + QGeoLocation location; + + location.setCoordinate(coordinate); + location.setAddress(address); + + return location; +} + +QGeoLocation GeoCodeReplyEsri::parseCandidate(const QJsonObject& candidate) +{ + QGeoCoordinate coordinate; + + QJsonObject locationObject = candidate.value(QStringLiteral("location")).toObject(); + + coordinate.setLongitude(locationObject.value(QStringLiteral("x")).toDouble()); + coordinate.setLatitude(locationObject.value(QStringLiteral("y")).toDouble()); + + QGeoRectangle extent; + + if (candidate.contains(QStringLiteral("extent"))) { + QJsonObject extentObject = candidate.value(QStringLiteral("extent")).toObject(); + + extent.setTopLeft(QGeoCoordinate(extentObject.value(QStringLiteral("ymin")).toDouble(), + extentObject.value(QStringLiteral("xmin")).toDouble())); + + extent.setBottomRight(QGeoCoordinate(extentObject.value(QStringLiteral("ymax")).toDouble(), + extentObject.value(QStringLiteral("xmax")).toDouble())); + } + + QJsonObject attributesObject = candidate.value(QStringLiteral("attributes")).toObject(); + + QGeoAddress address; + + address.setText(candidate.value(QStringLiteral("address")).toString()); + + address.setCountry(attributesObject.value(QStringLiteral("Country")).toString()); + address.setCountryCode(attributesObject.value(QStringLiteral("Country")).toString()); + address.setState(attributesObject.value(QStringLiteral("Region")).toString()); + address.setCity(attributesObject.value(QStringLiteral("City")).toString()); + address.setDistrict(attributesObject.value(QStringLiteral("Subregion")).toString()); + address.setPostalCode(attributesObject.value(QStringLiteral("Postal")).toString()); + + QGeoLocation location; + + location.setCoordinate(coordinate); + location.setBoundingBox(extent); + location.setAddress(address); + + return location; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geocodereply_esri.h b/src/plugins/geoservices/esri/geocodereply_esri.h new file mode 100644 index 00000000..4434b7dc --- /dev/null +++ b/src/plugins/geoservices/esri/geocodereply_esri.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 GEOCODEREPLYESRI_H +#define GEOCODEREPLYESRI_H + +#include <QNetworkReply> +#include <QGeoCodeReply> + +QT_BEGIN_NAMESPACE + +class GeoCodeReplyEsri : public QGeoCodeReply +{ + Q_OBJECT + +public: + enum OperationType + { + Geocode, + ReverseGeocode + }; + +public: + GeoCodeReplyEsri(QNetworkReply *reply, OperationType operationType, QObject *parent = Q_NULLPTR); + virtual ~GeoCodeReplyEsri(); + + void abort() Q_DECL_OVERRIDE; + + inline OperationType operationType() const; + +private Q_SLOTS: + void networkReplyFinished(); + void networkReplyError(QNetworkReply::NetworkError error); + + QGeoLocation parseAddress(const QJsonObject &object); + QGeoLocation parseCandidate(const QJsonObject &candidate); + +private: + QNetworkReply *m_reply; + OperationType m_operationType; +}; + +inline GeoCodeReplyEsri::OperationType GeoCodeReplyEsri::operationType() const +{ + return m_operationType; +} + +QT_END_NAMESPACE + +#endif // GEOCODEREPLYESRI_H diff --git a/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp b/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp new file mode 100644 index 00000000..fcdc5962 --- /dev/null +++ b/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 "geocodingmanagerengine_esri.h" +#include "geocodereply_esri.h" + +#include <QVariantMap> +#include <QUrl> +#include <QUrlQuery> +#include <QLocale> +#include <QNetworkAccessManager> +#include <QNetworkRequest> +#include <QGeoCoordinate> +#include <QGeoAddress> +#include <QGeoShape> +#include <QGeoRectangle> + +QT_BEGIN_NAMESPACE + +// https://developers.arcgis.com/rest/geocode/api-reference/geocoding-find-address-candidates.htm +// https://developers.arcgis.com/rest/geocode/api-reference/geocoding-reverse-geocode.htm + +static const QString kPrefixEsri(QStringLiteral("esri.")); +static const QString kParamUserAgent(kPrefixEsri + QStringLiteral("useragent")); + +static const QString kUrlGeocode(QStringLiteral("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates")); +static const QString kUrlReverseGeocode(QStringLiteral("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode")); + +static QString addressToQuery(const QGeoAddress &address) +{ + return address.street() + QStringLiteral(", ") + + address.district() + QStringLiteral(", ") + + address.city() + QStringLiteral(", ") + + address.state() + QStringLiteral(", ") + + address.country(); +} + +static QString boundingBoxToLtrb(const QGeoRectangle &rect) +{ + return QString::number(rect.topLeft().longitude()) + QLatin1Char(',') + + QString::number(rect.topLeft().latitude()) + QLatin1Char(',') + + QString::number(rect.bottomRight().longitude()) + QLatin1Char(',') + + QString::number(rect.bottomRight().latitude()); +} + +GeoCodingManagerEngineEsri::GeoCodingManagerEngineEsri(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) +: QGeoCodingManagerEngine(parameters), m_networkManager(new QNetworkAccessManager(this)) +{ + if (parameters.contains(kParamUserAgent)) + m_userAgent = parameters.value(kParamUserAgent).toString().toLatin1(); + else + m_userAgent = QByteArrayLiteral("Qt Location based application"); + + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +GeoCodingManagerEngineEsri::~GeoCodingManagerEngineEsri() +{ +} + +QGeoCodeReply *GeoCodingManagerEngineEsri::geocode(const QGeoAddress &address, + const QGeoShape &bounds) +{ + return geocode(addressToQuery(address), 1, -1, bounds); +} + +QGeoCodeReply *GeoCodingManagerEngineEsri::geocode(const QString &address, int limit, int offset, + const QGeoShape &bounds) +{ + Q_UNUSED(offset) + + QNetworkRequest request; + request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); + + QUrl url(kUrlGeocode); + + QUrlQuery query; + query.addQueryItem(QStringLiteral("singleLine"), address); + query.addQueryItem(QStringLiteral("f"), QStringLiteral("json")); + query.addQueryItem(QStringLiteral("outFields"), "*"); + + if (bounds.type() == QGeoShape::RectangleType) + query.addQueryItem(QStringLiteral("searchExtent"), boundingBoxToLtrb(bounds)); + + if (limit != -1) + query.addQueryItem(QStringLiteral("maxLocations"), QString::number(limit)); + + url.setQuery(query); + request.setUrl(url); + + QNetworkReply *reply = m_networkManager->get(request); + GeoCodeReplyEsri *geocodeReply = new GeoCodeReplyEsri(reply, GeoCodeReplyEsri::Geocode, this); + + connect(geocodeReply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(geocodeReply, SIGNAL(error(QGeoCodeReply::Error,QString)), + this, SLOT(replyError(QGeoCodeReply::Error,QString))); + + return geocodeReply; +} + +QGeoCodeReply *GeoCodingManagerEngineEsri::reverseGeocode(const QGeoCoordinate &coordinate, + const QGeoShape &bounds) +{ + Q_UNUSED(bounds) + + QNetworkRequest request; + request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); + + QUrl url(kUrlReverseGeocode); + + QUrlQuery query; + + query.addQueryItem(QStringLiteral("f"), QStringLiteral("json")); + query.addQueryItem(QStringLiteral("langCode"), locale().name().left(2)); + query.addQueryItem(QStringLiteral("location"), QString::number(coordinate.longitude()) + QLatin1Char(',') + + QString::number(coordinate.latitude())); + + url.setQuery(query); + request.setUrl(url); + + QNetworkReply *reply = m_networkManager->get(request); + GeoCodeReplyEsri *geocodeReply = new GeoCodeReplyEsri(reply, GeoCodeReplyEsri::ReverseGeocode, + this); + + connect(geocodeReply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(geocodeReply, SIGNAL(error(QGeoCodeReply::Error,QString)), + this, SLOT(replyError(QGeoCodeReply::Error,QString))); + + return geocodeReply; +} + +void GeoCodingManagerEngineEsri::replyFinished() +{ + QGeoCodeReply *reply = qobject_cast<QGeoCodeReply *>(sender()); + if (reply) + emit finished(reply); +} + +void GeoCodingManagerEngineEsri::replyError(QGeoCodeReply::Error errorCode, + const QString &errorString) +{ + QGeoCodeReply *reply = qobject_cast<QGeoCodeReply *>(sender()); + if (reply) + emit error(reply, errorCode, errorString); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geocodingmanagerengine_esri.h b/src/plugins/geoservices/esri/geocodingmanagerengine_esri.h new file mode 100644 index 00000000..9ff246e0 --- /dev/null +++ b/src/plugins/geoservices/esri/geocodingmanagerengine_esri.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 GEOCODINGMANAGERENGINEESRI_H +#define GEOCODINGMANAGERENGINEESRI_H + +#include <QGeoServiceProvider> +#include <QGeoCodingManagerEngine> +#include <QGeoCodeReply> + +QT_BEGIN_NAMESPACE + +class QNetworkAccessManager; + +class GeoCodingManagerEngineEsri : public QGeoCodingManagerEngine +{ + Q_OBJECT + +public: + GeoCodingManagerEngineEsri(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, + QString *errorString); + virtual ~GeoCodingManagerEngineEsri(); + + QGeoCodeReply *geocode(const QGeoAddress &address, const QGeoShape &bounds) Q_DECL_OVERRIDE; + QGeoCodeReply *geocode(const QString &address, int limit, int offset, + const QGeoShape &bounds) Q_DECL_OVERRIDE; + QGeoCodeReply *reverseGeocode(const QGeoCoordinate &coordinate, + const QGeoShape &bounds) Q_DECL_OVERRIDE; + +private Q_SLOTS: + void replyFinished(); + void replyError(QGeoCodeReply::Error errorCode, const QString &errorString); + +private: + QNetworkAccessManager *m_networkManager; + QByteArray m_userAgent; +}; + +QT_END_NAMESPACE + +#endif // GEOCODINGMANAGERENGINEESRI_H diff --git a/src/plugins/geoservices/esri/geomapsource.cpp b/src/plugins/geoservices/esri/geomapsource.cpp new file mode 100644 index 00000000..32fe1899 --- /dev/null +++ b/src/plugins/geoservices/esri/geomapsource.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 "geomapsource.h" + +#include <QUrl> + +QT_BEGIN_NAMESPACE + +static const QString kArcGISTileScheme(QStringLiteral("/tile/${z}/${y}/${x}")); + +struct MapStyleData +{ + QString name; + QGeoMapType::MapStyle style; +}; + +static const MapStyleData mapStyles[] = +{ + { QStringLiteral("StreetMap"), QGeoMapType::StreetMap }, + { QStringLiteral("SatelliteMapDay"), QGeoMapType::SatelliteMapDay }, + { QStringLiteral("SatelliteMapNight"), QGeoMapType::SatelliteMapNight }, + { QStringLiteral("TerrainMap"), QGeoMapType::TerrainMap }, + { QStringLiteral("HybridMap"), QGeoMapType::HybridMap }, + { QStringLiteral("TransitMap"), QGeoMapType::TransitMap }, + { QStringLiteral("GrayStreetMap"), QGeoMapType::GrayStreetMap }, + { QStringLiteral("PedestrianMap"), QGeoMapType::PedestrianMap }, + { QStringLiteral("CarNavigationMap"), QGeoMapType::CarNavigationMap }, + { QStringLiteral("CustomMap"), QGeoMapType::CustomMap } +}; + +GeoMapSource::GeoMapSource(QGeoMapType::MapStyle style, const QString &name, + const QString &description, bool mobile, bool night, int mapId, + const QString &url, const QString ©right) : + QGeoMapType(style, name, description, mobile, night, mapId), + m_url(url), m_copyright(copyright) +{ +} + +QString GeoMapSource::toFormat(const QString &url) +{ + QString format = url; + + if (!format.contains(QLatin1String("${"))) + format += kArcGISTileScheme; + + format.replace(QLatin1String("${z}"), QLatin1String("%1")); + format.replace(QLatin1String("${x}"), QLatin1String("%2")); + format.replace(QLatin1String("${y}"), QLatin1String("%3")); + format.replace(QLatin1String("${token}"), QLatin1String("%4")); + + return format; +} + +QGeoMapType::MapStyle GeoMapSource::mapStyle(const QString &styleString) +{ + for (unsigned int i = 0; i < sizeof(mapStyles)/sizeof(MapStyle); i++) { + const MapStyleData &mapStyle = mapStyles[i]; + + if (styleString.compare(mapStyle.name, Qt::CaseInsensitive) == 0) + return mapStyle.style; + } + + QGeoMapType::MapStyle style = static_cast<QGeoMapType::MapStyle>(styleString.toInt()); + if (style <= QGeoMapType::NoMap) + style = QGeoMapType::CustomMap; + + return style; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geomapsource.h b/src/plugins/geoservices/esri/geomapsource.h new file mode 100644 index 00000000..8660e999 --- /dev/null +++ b/src/plugins/geoservices/esri/geomapsource.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 GEOMAPSOURCE_H +#define GEOMAPSOURCE_H + +#include <QtLocation/private/qgeomaptype_p.h> + +QT_BEGIN_NAMESPACE + +class GeoMapSource : public QGeoMapType +{ +public: + GeoMapSource(QGeoMapType::MapStyle style, const QString &name, + const QString &description, bool mobile, bool night, int mapId, + const QString &url, const QString ©right); + + inline const QString &url() const; + inline const QString ©right() const; + + static QString toFormat(const QString &url); + static QGeoMapType::MapStyle mapStyle(const QString &styleString); + +private: + QString m_url; + QString m_copyright; +}; + +inline const QString &GeoMapSource::url() const +{ + return m_url; +} + +inline const QString &GeoMapSource::copyright() const +{ + return m_copyright; +} + +QT_END_NAMESPACE + +#endif // GEOMAPSOURCE_H diff --git a/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp b/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp new file mode 100644 index 00000000..30db48f0 --- /dev/null +++ b/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 "georoutejsonparser_esri.h" + +#include <QJsonArray> +#include <QGeoRectangle> +#include <QGeoManeuver> +#include <QGeoRouteSegment> + +QT_BEGIN_NAMESPACE + +// JSON reference: http://resources.arcgis.com/en/help/arcgis-rest-api/#/Route_service_with_synchronous_execution/02r300000036000000/ + +static const QString kErrorMessage(QStringLiteral("Error %1: %2.")); +static const QString kErrorJson(QStringLiteral("Error: invalide JSON document.")); + +static const QString kErrorKey(QStringLiteral("error")); +static const QString kErrorCodeKey(QStringLiteral("code")); +static const QString kErrorMessageKey(QStringLiteral("message")); +static const QString kErrorDetailsKey(QStringLiteral("details")); +static const QString kDirectionsKey(QStringLiteral("directions")); +static const QString kRoutesKey(QStringLiteral("routes")); +static const QString kBarriersKey(QStringLiteral("barriers")); +static const QString kMessagesKey(QStringLiteral("messages")); +static const QString kDirectionsRouteIdKey(QStringLiteral("routeId")); +static const QString kDirectionsRouteNameKey(QStringLiteral("routeName")); +static const QString kDirectionsSummaryKey(QStringLiteral("summary")); +static const QString kDirectionsTotalLengthKey(QStringLiteral("totalLength")); +static const QString kDirectionsTotalTimeKey(QStringLiteral("totalTime")); +static const QString kDirectionsTotalDriveTimeKey(QStringLiteral("totalDriveTime")); +static const QString kDirectionsEnvelopeKey(QStringLiteral("envelope")); +static const QString kDirectionsEnvelopeXminKey(QStringLiteral("xmin")); +static const QString kDirectionsEnvelopeYminKey(QStringLiteral("ymin")); +static const QString kDirectionsEnvelopeXmaxKey(QStringLiteral("xmax")); +static const QString kDirectionsEnvelopeYmaxKey(QStringLiteral("ymax")); +static const QString kDirectionsFeaturesKey(QStringLiteral("features")); +static const QString kDirectionsFeaturesAttributesKey(QStringLiteral("attributes")); +static const QString kDirectionsFeaturesCompressedGeometryKey(QStringLiteral("compressedGeometry")); +static const QString kDirectionsFeaturesAttributesLengthKey(QStringLiteral("length")); +static const QString kDirectionsFeaturesAttributesTimeKey(QStringLiteral("time")); +static const QString kDirectionsFeaturesAttributesTextKey(QStringLiteral("text")); +static const QString kDirectionsFeaturesAttributesEtaKey(QStringLiteral("ETA")); +static const QString kDirectionsFeaturesAttributesManeuverTypeKey(QStringLiteral("maneuverType")); +static const QString kRoutesFeaturesKey(QStringLiteral("features")); +static const QString kRoutesFeaturesAttributesKey(QStringLiteral("attributes")); +static const QString kRoutesFeaturesObjectIdKey(QStringLiteral("ObjectID")); +static const QString kRoutesFeaturesGeometryKey(QStringLiteral("geometry")); +static const QString kRoutesFeaturesGeometryPathsKey(QStringLiteral("paths")); + +GeoRouteJsonParserEsri::GeoRouteJsonParserEsri(const QJsonDocument &document) +{ + if (!document.isObject()) + { + m_error = kErrorJson; + return; + } + + m_json = document.object(); + if (m_json.contains(kErrorKey)) + { + QJsonObject error = m_json.value(kErrorKey).toObject(); + int code = error.value(kErrorCodeKey).toInt(); + QString message = error.value(kErrorMessageKey).toString(); + + m_error = kErrorMessage.arg(code).arg(message); + return; + } + + parseDirections(); + parseRoutes(); +} + +QList<QGeoRoute> GeoRouteJsonParserEsri::routes() const +{ + return m_routes.values(); +} + +bool GeoRouteJsonParserEsri::isValid() const +{ + return (m_error.isEmpty()); +} + +QString GeoRouteJsonParserEsri::errorString() const +{ + return m_error; +} + +void GeoRouteJsonParserEsri::parseDirections() +{ + QJsonArray directions = m_json.value(kDirectionsKey).toArray(); + foreach (const QJsonValue &direction, directions) + parseDirection(direction.toObject()); +} + +void GeoRouteJsonParserEsri::parseDirection(const QJsonObject &direction) +{ + QGeoRoute &geoRoute = m_routes[direction.value(kDirectionsRouteIdKey).toInt()]; + + // parse summary + geoRoute.setRouteId(direction.value(kDirectionsRouteNameKey).toString()); + + QJsonObject summary = direction.value(kDirectionsSummaryKey).toObject(); + geoRoute.setDistance(summary.value(kDirectionsTotalLengthKey).toDouble()); + + geoRoute.setTravelTime(summary.value(kDirectionsTotalTimeKey).toDouble() * 60); + // default units is minutes, see directionsTimeAttributeName param + + geoRoute.setTravelMode(QGeoRouteRequest::CarTravel); + // default request is time for car, see directionsTimeAttributeName param + + QJsonObject enveloppe = summary.value(kDirectionsEnvelopeKey).toObject(); + + QGeoCoordinate topLeft(enveloppe.value(kDirectionsEnvelopeXminKey).toDouble(), + enveloppe.value(kDirectionsEnvelopeYmaxKey).toDouble()); + QGeoCoordinate bottomRight(enveloppe.value(kDirectionsEnvelopeXmaxKey).toDouble(), + enveloppe.value(kDirectionsEnvelopeYminKey).toDouble()); + geoRoute.setBounds(QGeoRectangle(topLeft, bottomRight)); + + // parse features + QJsonArray features = direction.value(kDirectionsFeaturesKey).toArray(); + + static const QMap<QString, QGeoManeuver::InstructionDirection> esriDirectionsManeuverTypes + { + { QStringLiteral("esriDMTUnknown"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTStop"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTStraight"), QGeoManeuver::DirectionForward }, + { QStringLiteral("esriDMTBearLeft"), QGeoManeuver::DirectionBearLeft }, + { QStringLiteral("esriDMTBearRight"), QGeoManeuver::DirectionBearRight }, + { QStringLiteral("esriDMTTurnLeft"), QGeoManeuver::DirectionLeft }, + { QStringLiteral("esriDMTTurnRight"), QGeoManeuver::DirectionRight }, + { QStringLiteral("esriDMTSharpLeft"), QGeoManeuver::DirectionLightLeft }, + { QStringLiteral("esriDMTSharpRight"), QGeoManeuver::DirectionLightRight }, + { QStringLiteral("esriDMTUTurn"), QGeoManeuver::DirectionUTurnRight }, + { QStringLiteral("esriDMTFerry"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTRoundabout"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTHighwayMerge"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTHighwayExit"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTHighwayChange"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTForkCenter"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTForkLeft"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTForkRight"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTDepart"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTTripItem"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTEndOfFerry"), QGeoManeuver::NoDirection } + }; + + QGeoRouteSegment firstSegment; + for (int i = features.size() - 1; i >= 0; --i) + { + QJsonObject feature = features.at(i).toObject(); + QJsonObject attributes = feature.value(kDirectionsFeaturesAttributesKey).toObject(); + + QGeoRouteSegment segment; + double length = attributes.value(kDirectionsFeaturesAttributesLengthKey).toDouble(); + segment.setDistance(length); + + double time = attributes.value(kDirectionsFeaturesAttributesTimeKey).toDouble() * 60; + // default units is minutes, see directionsTimeAttributeName param + segment.setTravelTime(time); + + QGeoManeuver maneuver; + QString type = attributes.value(kDirectionsFeaturesAttributesManeuverTypeKey).toString(); + maneuver.setDirection(esriDirectionsManeuverTypes.value(type)); + + maneuver.setInstructionText(attributes.value(kDirectionsFeaturesAttributesTextKey).toString() + "."); + maneuver.setDistanceToNextInstruction(length); + maneuver.setTimeToNextInstruction(time); + + segment.setManeuver(maneuver); + + segment.setNextRouteSegment(firstSegment); + firstSegment = segment; + } + geoRoute.setFirstRouteSegment(firstSegment); +} + +void GeoRouteJsonParserEsri::parseRoutes() +{ + QJsonObject routes = m_json.value(kRoutesKey).toObject(); + QJsonArray features = routes.value(kRoutesFeaturesKey).toArray(); + foreach (const QJsonValue &feature, features) + parseRoute(feature.toObject()); +} + +void GeoRouteJsonParserEsri::parseRoute(const QJsonObject &route) +{ + QJsonObject attributes = route.value(kRoutesFeaturesAttributesKey).toObject(); + QGeoRoute &geoRoute = m_routes[attributes.value(kRoutesFeaturesObjectIdKey).toInt()]; + + QJsonObject geometry = route.value(kRoutesFeaturesGeometryKey).toObject(); + QJsonArray paths = geometry.value(kRoutesFeaturesGeometryPathsKey).toArray(); + + if (!paths.isEmpty()) + { + QList<QGeoCoordinate> geoCoordinates; + foreach (const QJsonValue &value, paths.first().toArray()) // only first polyline? + { + QJsonArray geoCoordinate = value.toArray(); + if (geoCoordinate.size() == 2) // ignore 3rd coordinate + { + geoCoordinates.append(QGeoCoordinate(geoCoordinate[1].toDouble(), + geoCoordinate[0].toDouble())); + } + } + geoRoute.setPath(geoCoordinates); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/georoutejsonparser_esri.h b/src/plugins/geoservices/esri/georoutejsonparser_esri.h new file mode 100644 index 00000000..0511cf4d --- /dev/null +++ b/src/plugins/geoservices/esri/georoutejsonparser_esri.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 GEOROUTEJSONPARSERESRI_H +#define GEOROUTEJSONPARSERESRI_H + +#include <QJsonDocument> +#include <QJsonObject> +#include <QGeoRoute> +#include <QMap> + +QT_BEGIN_NAMESPACE + +class GeoRouteJsonParserEsri +{ +public: + GeoRouteJsonParserEsri(const QJsonDocument &document); + + QList<QGeoRoute> routes() const; + bool isValid() const; + QString errorString() const; + +private: + void parseDirections(); + void parseDirection(const QJsonObject &direction); + void parseRoutes(); + void parseRoute(const QJsonObject &route); + + QString m_error; + QMap<int, QGeoRoute> m_routes; + QJsonObject m_json; +}; + +QT_END_NAMESPACE + +#endif // GEOROUTEJSONPARSERESRI_H diff --git a/src/plugins/geoservices/esri/georoutereply_esri.cpp b/src/plugins/geoservices/esri/georoutereply_esri.cpp new file mode 100644 index 00000000..4a7d5c67 --- /dev/null +++ b/src/plugins/geoservices/esri/georoutereply_esri.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 "georoutereply_esri.h" +#include "georoutejsonparser_esri.h" + +#include <QJsonDocument> + +QT_BEGIN_NAMESPACE + +// JSON reference: http://resources.arcgis.com/en/help/arcgis-rest-api/#/Route_service_with_synchronous_execution/02r300000036000000/ + +GeoRouteReplyEsri::GeoRouteReplyEsri(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))); +} + +GeoRouteReplyEsri::~GeoRouteReplyEsri() +{ + if (m_reply) + m_reply->deleteLater(); +} + +void GeoRouteReplyEsri::abort() +{ + if (!m_reply) + return; + + m_reply->abort(); + m_reply->deleteLater(); + m_reply = Q_NULLPTR; +} + +void GeoRouteReplyEsri::networkReplyFinished() +{ + if (!m_reply) + return; + + if (m_reply->error() != QNetworkReply::NoError) + { + setError(QGeoRouteReply::CommunicationError, m_reply->errorString()); + m_reply->deleteLater(); + m_reply = Q_NULLPTR; + return; + } + + QJsonDocument document = QJsonDocument::fromJson(m_reply->readAll()); + GeoRouteJsonParserEsri parser(document); + + if (parser.isValid()) + { + setRoutes(parser.routes()); + setFinished(true); + } else { + setError(QGeoRouteReply::ParseError, parser.errorString()); + } + + m_reply->deleteLater(); + m_reply = Q_NULLPTR; +} + +void GeoRouteReplyEsri::networkReplyError(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + + if (!m_reply) + return; + + setError(QGeoRouteReply::CommunicationError, m_reply->errorString()); + m_reply->deleteLater(); + m_reply = Q_NULLPTR; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/georoutereply_esri.h b/src/plugins/geoservices/esri/georoutereply_esri.h new file mode 100644 index 00000000..6e97ee9f --- /dev/null +++ b/src/plugins/geoservices/esri/georoutereply_esri.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 GEOROUTEREPLYESRI_H +#define GEOROUTEREPLYESRI_H + +#include <QNetworkReply> +#include <QGeoRouteReply> + +QT_BEGIN_NAMESPACE + +class GeoRouteReplyEsri : public QGeoRouteReply +{ + Q_OBJECT + +public: + GeoRouteReplyEsri(QNetworkReply *reply, const QGeoRouteRequest &request, QObject *parent = Q_NULLPTR); + virtual ~GeoRouteReplyEsri(); + + void abort() Q_DECL_OVERRIDE; + +private Q_SLOTS: + void networkReplyFinished(); + void networkReplyError(QNetworkReply::NetworkError error); + +private: + QNetworkReply *m_reply; +}; + +QT_END_NAMESPACE + +#endif // GEOROUTEREPLYESRI_H diff --git a/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp b/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp new file mode 100644 index 00000000..ae722e59 --- /dev/null +++ b/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 "georoutingmanagerengine_esri.h" +#include "georoutereply_esri.h" + +#include <QUrlQuery> + +QT_BEGIN_NAMESPACE + +static const QString kPrefixEsri(QStringLiteral("esri.")); +static const QString kParamUserAgent(kPrefixEsri + QStringLiteral("useragent")); +static const QString kParamToken(kPrefixEsri + QStringLiteral("token")); + +static const QString kUrlRouting(QStringLiteral("http://route.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World/solve")); + +GeoRoutingManagerEngineEsri::GeoRoutingManagerEngineEsri(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) : + QGeoRoutingManagerEngine(parameters), m_networkManager(new QNetworkAccessManager(this)) +{ + if (parameters.contains(kParamUserAgent)) + m_userAgent = parameters.value(kParamUserAgent).toString().toLatin1(); + else + m_userAgent = QByteArrayLiteral("Qt Location based application"); + + m_token = parameters.value(kParamToken).toString(); + + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +GeoRoutingManagerEngineEsri::~GeoRoutingManagerEngineEsri() +{ +} + +// REST reference: http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r300000036000000 + +QGeoRouteReply *GeoRoutingManagerEngineEsri::calculateRoute(const QGeoRouteRequest &request) +{ + QNetworkRequest networkRequest; + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); + + QUrl url(kUrlRouting); + QUrlQuery query; + QString stops; + + foreach (const QGeoCoordinate &coordinate, request.waypoints()) + { + if (!stops.isEmpty()) + stops += "; "; + + stops += QString::number(coordinate.longitude()) + QLatin1Char(',') + + QString::number(coordinate.latitude()); + } + + query.addQueryItem(QStringLiteral("stops"), stops); + query.addQueryItem(QStringLiteral("f"), QStringLiteral("json")); + query.addQueryItem(QStringLiteral("directionsLanguage"), preferedDirectionLangage()); + query.addQueryItem(QStringLiteral("directionsLengthUnits"), preferedDirectionsLengthUnits()); + query.addQueryItem(QStringLiteral("token"), m_token); + + url.setQuery(query); + networkRequest.setUrl(url); + + QNetworkReply *reply = m_networkManager->get(networkRequest); + GeoRouteReplyEsri *routeReply = new GeoRouteReplyEsri(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 GeoRoutingManagerEngineEsri::replyFinished() +{ + QGeoRouteReply *reply = qobject_cast<QGeoRouteReply *>(sender()); + if (reply) + emit finished(reply); +} + +void GeoRoutingManagerEngineEsri::replyError(QGeoRouteReply::Error errorCode, const QString &errorString) +{ + QGeoRouteReply *reply = qobject_cast<QGeoRouteReply *>(sender()); + if (reply) + emit error(reply, errorCode, errorString); +} + +QString GeoRoutingManagerEngineEsri::preferedDirectionLangage() +{ + // list of supported langages is defined in: + // http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r300000036000000 + const QStringList supportedLanguages = { + "ar", // Generate directions in Arabic + "cs", // Generate directions in Czech + "de", // Generate directions in German + "el", // Generate directions in Greek + "en", // Generate directions in English (default) + "es", // Generate directions in Spanish + "et", // Generate directions in Estonian + "fr", // Generate directions in French + "he", // Generate directions in Hebrew + "it", // Generate directions in Italian + "ja", // Generate directions in Japanese + "ko", // Generate directions in Korean + "lt", // Generate directions in Lithuanian + "lv", // Generate directions in Latvian + "nl", // Generate directions in Dutch + "pl", // Generate directions in Polish + "pt-BR", // Generate directions in Brazilian Portuguese + "pt-PT", // Generate directions in Portuguese (Portugal) + "ru", // Generate directions in Russian + "sv", // Generate directions in Swedish + "tr", // Generate directions in Turkish + "zh-CN" // Simplified Chinese + }; + + for (const QString &language: locale().uiLanguages()) + { + if (language.startsWith("pt_BR")) // Portuguese (Brazilian) + return QStringLiteral("pt-BR"); + if (language.startsWith("pt")) // Portuguese (Portugal) + return QStringLiteral("pt-PT"); + if (language.startsWith("zh")) // Portuguese (Portugal) + return QStringLiteral("zh-CN"); + + const QString country = language.left(2); + if (supportedLanguages.contains(country)) + return country; + } + return QStringLiteral("en"); // default value +} + +QString GeoRoutingManagerEngineEsri::preferedDirectionsLengthUnits() +{ + switch (measurementSystem()) + { + case QLocale::MetricSystem: + return QStringLiteral("esriNAUMeters"); + break; + case QLocale::ImperialUSSystem: + return QStringLiteral( "esriNAUFeet"); + break; + case QLocale::ImperialUKSystem: + return QStringLiteral("esriNAUFeet"); + break; + default: + return QStringLiteral("esriNAUMeters"); + break; + } + return QStringLiteral("esriNAUMeters"); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/georoutingmanagerengine_esri.h b/src/plugins/geoservices/esri/georoutingmanagerengine_esri.h new file mode 100644 index 00000000..7387e25c --- /dev/null +++ b/src/plugins/geoservices/esri/georoutingmanagerengine_esri.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 GEOROUTINGMANAGERENGINEESRI_H +#define GEOROUTINGMANAGERENGINEESRI_H + +#include <QGeoServiceProvider> +#include <QGeoRoutingManagerEngine> + +QT_BEGIN_NAMESPACE + +class QNetworkAccessManager; + +class GeoRoutingManagerEngineEsri : public QGeoRoutingManagerEngine +{ + Q_OBJECT + +public: + GeoRoutingManagerEngineEsri(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, + QString *errorString); + virtual ~GeoRoutingManagerEngineEsri(); + + QGeoRouteReply *calculateRoute(const QGeoRouteRequest &request) Q_DECL_OVERRIDE; + +private Q_SLOTS: + void replyFinished(); + void replyError(QGeoRouteReply::Error errorCode, const QString &errorString); + +private: + QString preferedDirectionLangage(); + QString preferedDirectionsLengthUnits(); + + QNetworkAccessManager *m_networkManager; + QByteArray m_userAgent; + QString m_token; +}; + +QT_END_NAMESPACE + +#endif // GEOROUTINGMANAGERENGINEESRI_H diff --git a/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.cpp b/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.cpp new file mode 100644 index 00000000..5277cde3 --- /dev/null +++ b/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 "geoserviceproviderfactory_esri.h" +#include "geotiledmappingmanagerengine_esri.h" +#include "geocodingmanagerengine_esri.h" +#include "georoutingmanagerengine_esri.h" + +#include <QtLocation/private/qgeotiledmappingmanagerengine_p.h> + +QT_BEGIN_NAMESPACE + +QGeoCodingManagerEngine *GeoServiceProviderFactoryEsri::createGeocodingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + return new GeoCodingManagerEngineEsri(parameters, error, errorString); +} + +QGeoMappingManagerEngine *GeoServiceProviderFactoryEsri::createMappingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + return new GeoTiledMappingManagerEngineEsri(parameters, error, errorString); +} + +QGeoRoutingManagerEngine *GeoServiceProviderFactoryEsri::createRoutingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + const QString token = parameters.value(QStringLiteral("esri.token")).toString(); + + if (!token.isEmpty()) { + return new GeoRoutingManagerEngineEsri(parameters, error, errorString); + } else { + *error = QGeoServiceProvider::MissingRequiredParameterError; + *errorString = tr("Esri plugin requires a 'esri.token' parameter.\n" + "Please visit https://developers.arcgis.com/authentication/accessing-arcgis-online-services/"); + return 0; + } +} + +QPlaceManagerEngine *GeoServiceProviderFactoryEsri::createPlaceManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + Q_UNUSED(parameters) + Q_UNUSED(error) + Q_UNUSED(errorString) + + return Q_NULLPTR; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.h b/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.h new file mode 100644 index 00000000..d198c75a --- /dev/null +++ b/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 GEOSERVICEPROVIDERFACTORYESRI_H +#define GEOSERVICEPROVIDERFACTORYESRI_H + +#include <QObject> +#include <QGeoServiceProviderFactory> + +QT_BEGIN_NAMESPACE + +class GeoServiceProviderFactoryEsri: public QObject, public QGeoServiceProviderFactory +{ + Q_OBJECT + Q_INTERFACES(QGeoServiceProviderFactory) + Q_PLUGIN_METADATA(IID "org.qt-project.qt.geoservice.serviceproviderfactory/5.0" + FILE "esri_plugin.json") + +public: + QGeoCodingManagerEngine *createGeocodingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const Q_DECL_OVERRIDE; + QGeoMappingManagerEngine *createMappingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const Q_DECL_OVERRIDE; + QGeoRoutingManagerEngine *createRoutingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const Q_DECL_OVERRIDE; + QPlaceManagerEngine *createPlaceManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const Q_DECL_OVERRIDE; +}; + +QT_END_NAMESPACE + +#endif // GEOSERVICEPROVIDERFACTORYESRI_H diff --git a/src/plugins/geoservices/esri/geotiledmap_esri.cpp b/src/plugins/geoservices/esri/geotiledmap_esri.cpp new file mode 100644 index 00000000..9171fc2b --- /dev/null +++ b/src/plugins/geoservices/esri/geotiledmap_esri.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 "geotiledmap_esri.h" +#include "geotiledmappingmanagerengine_esri.h" + +#include <QtLocation/private/qgeotilespec_p.h> + +QT_BEGIN_NAMESPACE + +GeoTiledMapEsri::GeoTiledMapEsri(GeoTiledMappingManagerEngineEsri *engine, QObject *parent) : + QGeoTiledMap(engine, parent), m_engine(engine), m_mapId(-1) +{ +} + +GeoTiledMapEsri::~GeoTiledMapEsri() +{ +} + +void GeoTiledMapEsri::evaluateCopyrights(const QSet<QGeoTileSpec> &visibleTiles) +{ + if (visibleTiles.isEmpty()) + return; + + QGeoTileSpec tile = *(visibleTiles.constBegin()); + if (tile.mapId() == m_mapId) + return; + + m_mapId = tile.mapId(); + + GeoMapSource *mapSource = engine()->mapSource(m_mapId); + + if (mapSource) + emit copyrightsChanged(mapSource->copyright()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geotiledmap_esri.h b/src/plugins/geoservices/esri/geotiledmap_esri.h new file mode 100644 index 00000000..d9d9d0b7 --- /dev/null +++ b/src/plugins/geoservices/esri/geotiledmap_esri.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 GEOTILEDMAPESRI_H +#define GEOTILEDMAPESRI_H + +#include <QtLocation/private/qgeotiledmap_p.h> + +QT_BEGIN_NAMESPACE + +class GeoTiledMappingManagerEngineEsri; + +class GeoTiledMapEsri: public QGeoTiledMap +{ + Q_OBJECT + +public: + explicit GeoTiledMapEsri(GeoTiledMappingManagerEngineEsri *engine, QObject *parent = Q_NULLPTR); + virtual ~GeoTiledMapEsri(); + +protected: + void evaluateCopyrights(const QSet<QGeoTileSpec> &visibleTiles) Q_DECL_OVERRIDE; + + inline GeoTiledMappingManagerEngineEsri *engine() const; + +private: + GeoTiledMappingManagerEngineEsri *m_engine; + int m_mapId; +}; + +inline GeoTiledMappingManagerEngineEsri *GeoTiledMapEsri::engine() const +{ + return m_engine; +} + +QT_END_NAMESPACE + +#endif // GEOTILEDMAPESRI_H diff --git a/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp b/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp new file mode 100644 index 00000000..abcb3779 --- /dev/null +++ b/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 "geotiledmappingmanagerengine_esri.h" +#include "geotiledmap_esri.h" +#include "geotilefetcher_esri.h" + +#include <QtLocation/private/qgeocameracapabilities_p.h> +#include <QtLocation/private/qgeomaptype_p.h> +#include <QtLocation/private/qgeotiledmap_p.h> +#include <QtLocation/private/qgeofiletilecache_p.h> + +#include <QFileInfo> +#include <QDir> +#include <QUrl> +#include <QFile> +#include <QJsonDocument> +#include <QJsonObject> + +static void initResources() +{ + Q_INIT_RESOURCE(esri); +} + +QT_BEGIN_NAMESPACE + +static const QString kPrefixEsri(QStringLiteral("esri.")); +static const QString kParamUserAgent(kPrefixEsri + QStringLiteral("useragent")); +static const QString kParamToken(kPrefixEsri + QStringLiteral("token")); +static const QString kPrefixMapping(kPrefixEsri + QStringLiteral("mapping.")); +static const QString kParamMinimumZoomLevel(kPrefixMapping + QStringLiteral("minimumZoomLevel")); +static const QString kParamMaximumZoomLevel(kPrefixMapping + QStringLiteral("maximumZoomLevel")); + +static const QString kPropMapSources(QStringLiteral("mapSources")); +static const QString kPropStyle(QStringLiteral("style")); +static const QString kPropName(QStringLiteral("name")); +static const QString kPropDescription(QStringLiteral("description")); +static const QString kPropMobile(QStringLiteral("mobile")); +static const QString kPropNight(QStringLiteral("night")); +static const QString kPropUrl(QStringLiteral("url")); +static const QString kPropMapId(QStringLiteral("mapId")); +static const QString kPropCopyright(QStringLiteral("copyrightText")); + +GeoTiledMappingManagerEngineEsri::GeoTiledMappingManagerEngineEsri(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) : + QGeoTiledMappingManagerEngine() +{ + QGeoCameraCapabilities cameraCaps; + + double minimumZoomLevel = 0; + double maximumZoomLevel = 19; + + if (parameters.contains(kParamMinimumZoomLevel)) + minimumZoomLevel = parameters[kParamMinimumZoomLevel].toDouble(); + + if (parameters.contains(kParamMaximumZoomLevel)) + maximumZoomLevel = parameters[kParamMaximumZoomLevel].toDouble(); + + cameraCaps.setMinimumZoomLevel(minimumZoomLevel); + cameraCaps.setMaximumZoomLevel(maximumZoomLevel); + + setCameraCapabilities(cameraCaps); + + setTileSize(QSize(256, 256)); + + if (!initializeMapSources(error, errorString)) + return; + + QList<QGeoMapType> mapTypes; + + foreach (GeoMapSource *mapSource, m_mapSources) { + mapTypes << QGeoMapType( + mapSource->style(), + mapSource->name(), + mapSource->description(), + mapSource->mobile(), + mapSource->night(), + mapSource->mapId()); + } + + setSupportedMapTypes(mapTypes); + + GeoTileFetcherEsri *tileFetcher = new GeoTileFetcherEsri(this); + + if (parameters.contains(kParamUserAgent)) + tileFetcher->setUserAgent(parameters.value(kParamUserAgent).toString().toLatin1()); + + if (parameters.contains(kParamToken)) + tileFetcher->setToken(parameters.value(kParamToken).toString()); + + setTileFetcher(tileFetcher); + + /* TILE CACHE */ + QString cacheDirectory; + if (parameters.contains(QStringLiteral("esri.mapping.cache.directory"))) { + cacheDirectory = parameters.value(QStringLiteral("esri.mapping.cache.directory")).toString(); + } else { + // managerName() is not yet set, we have to hardcode the plugin name below + cacheDirectory = QAbstractGeoTileCache::baseLocationCacheDirectory() + QLatin1String("esri"); + } + QGeoFileTileCache *tileCache = new QGeoFileTileCache(cacheDirectory); + + /* + * Disk cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("esri.mapping.cache.disk.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("esri.mapping.cache.disk.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyDisk(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("esri.mapping.cache.disk.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("esri.mapping.cache.disk.size")).toString().toInt(&ok); + if (ok) + tileCache->setMaxDiskUsage(cacheSize); + } + + /* + * Memory cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("esri.mapping.cache.memory.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("esri.mapping.cache.memory.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyMemory(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("esri.mapping.cache.memory.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("esri.mapping.cache.memory.size")).toString().toInt(&ok); + if (ok) + tileCache->setMaxMemoryUsage(cacheSize); + } + + /* + * Texture cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("esri.mapping.cache.texture.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("esri.mapping.cache.texture.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyTexture(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("esri.mapping.cache.texture.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("esri.mapping.cache.texture.size")).toString().toInt(&ok); + if (ok) + tileCache->setExtraTextureUsage(cacheSize); + } + + + setTileCache(tileCache); + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +GeoTiledMappingManagerEngineEsri::~GeoTiledMappingManagerEngineEsri() +{ + qDeleteAll(m_mapSources); +} + +QGeoMap *GeoTiledMappingManagerEngineEsri::createMap() +{ + return new GeoTiledMapEsri(this); +} + +// ${z} = Zoom +// ${x} = X +// ${y} = Y +// ${token} = Token + +// template = 'http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{{z}}/{{y}}/{{x}}.png' + +bool GeoTiledMappingManagerEngineEsri::initializeMapSources(QGeoServiceProvider::Error *error, + QString *errorString) +{ + initResources(); + QFile mapsFile(":/maps.json"); + + if (!mapsFile.open(QIODevice::ReadOnly)) { + *error = QGeoServiceProvider::NotSupportedError; + *errorString = Q_FUNC_INFO + QStringLiteral("Unable to open: ") + mapsFile.fileName(); + + return false; + } + + QByteArray mapsData = mapsFile.readAll(); + mapsFile.close(); + + QJsonParseError parseError; + + QJsonDocument mapsDocument = QJsonDocument::fromJson(mapsData, &parseError); + + if (!mapsDocument.isObject()) { + *error = QGeoServiceProvider::NotSupportedError; + *errorString = Q_FUNC_INFO + QStringLiteral("JSON error: ") + (int)parseError.error + + ", offset: " + parseError.offset + + ", details: " + parseError.errorString(); + return false; + } + + QVariantMap maps = mapsDocument.object().toVariantMap(); + + QVariantList mapSources = maps["mapSources"].toList(); + + foreach (QVariant mapSourceElement, mapSources) { + QVariantMap mapSource = mapSourceElement.toMap(); + + int mapId = mapSource[kPropMapId].toInt(); + if (mapId <= 0) + mapId = m_mapSources.count() + 1; + + m_mapSources << new GeoMapSource( + GeoMapSource::mapStyle(mapSource[kPropStyle].toString()), + mapSource[kPropName].toString(), + mapSource[kPropDescription].toString(), + mapSource[kPropMobile].toBool(), + mapSource[kPropMapId].toBool(), + mapId, + GeoMapSource::toFormat(mapSource[kPropUrl].toString()), + mapSource[kPropCopyright].toString() + ); + } + + return true; +} + +GeoMapSource *GeoTiledMappingManagerEngineEsri::mapSource(int mapId) const +{ + foreach (GeoMapSource *mapSource, mapSources()) { + if (mapSource->mapId() == mapId) + return mapSource; + } + + return Q_NULLPTR; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.h b/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.h new file mode 100644 index 00000000..a13ba05c --- /dev/null +++ b/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 GEOTILEDMAPPINGMANAGERENGINEESRI_H +#define GEOTILEDMAPPINGMANAGERENGINEESRI_H + +#include <QGeoServiceProvider> + +#include <QtLocation/private/qgeotiledmappingmanagerengine_p.h> + +#include "geomapsource.h" + +QT_BEGIN_NAMESPACE + +class GeoTiledMappingManagerEngineEsri : public QGeoTiledMappingManagerEngine +{ + Q_OBJECT + +public: + GeoTiledMappingManagerEngineEsri(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString); + virtual ~GeoTiledMappingManagerEngineEsri(); + + QGeoMap *createMap() Q_DECL_OVERRIDE; + + inline const QList<GeoMapSource *>& mapSources() const; + GeoMapSource *mapSource(int mapId) const; + +private: + bool initializeMapSources(QGeoServiceProvider::Error *error, QString *errorString); + + QList<GeoMapSource *> m_mapSources; +}; + +inline const QList<GeoMapSource *>& GeoTiledMappingManagerEngineEsri::mapSources() const +{ + return m_mapSources; +} + +QT_END_NAMESPACE + +#endif // GEOTILEDMAPPINGMANAGERENGINEESRI_H diff --git a/src/plugins/geoservices/esri/geotiledmapreply_esri.cpp b/src/plugins/geoservices/esri/geotiledmapreply_esri.cpp new file mode 100644 index 00000000..e0816c15 --- /dev/null +++ b/src/plugins/geoservices/esri/geotiledmapreply_esri.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 "geotiledmapreply_esri.h" + +#include <QtLocation/private/qgeotilespec_p.h> + +QT_BEGIN_NAMESPACE + +static const unsigned char pngSignature[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00}; +static const unsigned char jpegSignature[] = {0xFF, 0xD8, 0xFF, 0x00}; +static const unsigned char gifSignature[] = {0x47, 0x49, 0x46, 0x38, 0x00}; + +GeoTiledMapReplyEsri::GeoTiledMapReplyEsri(QNetworkReply *reply, const QGeoTileSpec &spec, + QObject *parent) : + QGeoTiledMapReply(spec, parent), m_reply(reply) +{ + connect(m_reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); + connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(networkReplyError(QNetworkReply::NetworkError))); + connect(m_reply, SIGNAL(destroyed()), this, SLOT(replyDestroyed())); +} + +GeoTiledMapReplyEsri::~GeoTiledMapReplyEsri() +{ + if (m_reply) { + m_reply->deleteLater(); + m_reply = Q_NULLPTR; + } +} + +void GeoTiledMapReplyEsri::abort() +{ + if (!m_reply) + return; + + m_reply->abort(); + QGeoTiledMapReply::abort(); +} + +void GeoTiledMapReplyEsri::replyDestroyed() +{ + m_reply = Q_NULLPTR; +} + +void GeoTiledMapReplyEsri::networkReplyFinished() +{ + if (!m_reply) + return; + + if (m_reply->error() != QNetworkReply::NoError) + { + setError(QGeoTiledMapReply::CommunicationError, m_reply->errorString()); + m_reply->deleteLater(); + m_reply = Q_NULLPTR; + return; + } + + QByteArray const& imageData = m_reply->readAll(); + + bool validFormat = true; + if (imageData.startsWith(reinterpret_cast<const char*>(pngSignature))) + setMapImageFormat(QStringLiteral("png")); + else if (imageData.startsWith(reinterpret_cast<const char*>(jpegSignature))) + setMapImageFormat(QStringLiteral("jpg")); + else if (imageData.startsWith(reinterpret_cast<const char*>(gifSignature))) + setMapImageFormat(QStringLiteral("gif")); + else + validFormat = false; + + if (validFormat) + setMapImageData(imageData); + + setFinished(true); + + m_reply->deleteLater(); + m_reply = Q_NULLPTR; +} + +void GeoTiledMapReplyEsri::networkReplyError(QNetworkReply::NetworkError error) +{ + if (!m_reply) + return; + + if (error != QNetworkReply::OperationCanceledError) + setError(QGeoTiledMapReply::CommunicationError, m_reply->errorString()); + + setFinished(true); + m_reply->deleteLater(); + m_reply = Q_NULLPTR; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geotiledmapreply_esri.h b/src/plugins/geoservices/esri/geotiledmapreply_esri.h new file mode 100644 index 00000000..32a35698 --- /dev/null +++ b/src/plugins/geoservices/esri/geotiledmapreply_esri.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 GEOTILEDMAPREPLYESRI_H +#define GEOTILEDMAPREPLYESRI_H + +#include <QNetworkReply> + +#include <QtLocation/private/qgeotiledmapreply_p.h> + +QT_BEGIN_NAMESPACE + +class GeoTiledMapReplyEsri : public QGeoTiledMapReply +{ + Q_OBJECT + +public: + GeoTiledMapReplyEsri(QNetworkReply *reply, const QGeoTileSpec &spec, QObject *parent = Q_NULLPTR); + virtual ~GeoTiledMapReplyEsri(); + + void abort() Q_DECL_OVERRIDE; + +private Q_SLOTS: + void replyDestroyed(); + void networkReplyFinished(); + void networkReplyError(QNetworkReply::NetworkError error); + +private: + QNetworkReply *m_reply; +}; + +QT_END_NAMESPACE + +#endif // GEOTILEDMAPREPLYESRI_H diff --git a/src/plugins/geoservices/esri/geotilefetcher_esri.cpp b/src/plugins/geoservices/esri/geotilefetcher_esri.cpp new file mode 100644 index 00000000..62484bbb --- /dev/null +++ b/src/plugins/geoservices/esri/geotilefetcher_esri.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 "geotilefetcher_esri.h" +#include "geotiledmappingmanagerengine_esri.h" +#include "geotiledmapreply_esri.h" + +#include <QNetworkAccessManager> +#include <QNetworkRequest> + +#include <QtLocation/private/qgeotilespec_p.h> + +QT_BEGIN_NAMESPACE + +GeoTileFetcherEsri::GeoTileFetcherEsri(QObject *parent) : + QGeoTileFetcher(parent), m_networkManager(new QNetworkAccessManager(this)), + m_userAgent(QByteArrayLiteral("Qt Location based application")) +{ +} + +QGeoTiledMapReply *GeoTileFetcherEsri::getTileImage(const QGeoTileSpec &spec) +{ + QNetworkRequest request; + request.setHeader(QNetworkRequest::UserAgentHeader, userAgent()); + + GeoTiledMappingManagerEngineEsri *engine = qobject_cast<GeoTiledMappingManagerEngineEsri *>( + parent()); + + GeoMapSource *mapSource = engine->mapSource(spec.mapId()); + + if (!mapSource) + qWarning("Unknown mapId %d\n", spec.mapId()); + else + request.setUrl(mapSource->url().arg(spec.zoom()).arg(spec.x()).arg(spec.y())); + + QNetworkReply *reply = m_networkManager->get(request); + + return new GeoTiledMapReplyEsri(reply, spec); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geotilefetcher_esri.h b/src/plugins/geoservices/esri/geotilefetcher_esri.h new file mode 100644 index 00000000..43dcfdfa --- /dev/null +++ b/src/plugins/geoservices/esri/geotilefetcher_esri.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri <contracts@esri.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 GEOTILEFETCHERESRI_H +#define GEOTILEFETCHERESRI_H + +#include <QtLocation/private/qgeotilefetcher_p.h> + +QT_BEGIN_NAMESPACE + +class QGeoTiledMappingManagerEngine; +class QNetworkAccessManager; + +class GeoTileFetcherEsri : public QGeoTileFetcher +{ + Q_OBJECT + +public: + explicit GeoTileFetcherEsri(QObject *parent = Q_NULLPTR); + + inline const QByteArray &userAgent() const; + inline void setUserAgent(const QByteArray &userAgent); + + inline const QString &token() const; + inline void setToken(const QString &token); + +private: + QGeoTiledMapReply *getTileImage(const QGeoTileSpec &spec) Q_DECL_OVERRIDE; + + QNetworkAccessManager *m_networkManager; + QByteArray m_userAgent; + QString m_token; +}; + +inline const QByteArray &GeoTileFetcherEsri::userAgent() const +{ + return m_userAgent; +} + +inline void GeoTileFetcherEsri::setUserAgent(const QByteArray &userAgent) +{ + m_userAgent = userAgent; +} + +inline const QString &GeoTileFetcherEsri::token() const +{ + return m_token; +} + +inline void GeoTileFetcherEsri::setToken(const QString &token) +{ + m_token = token; +} + +QT_END_NAMESPACE + +#endif // GEOTILEFETCHERESRI_H diff --git a/src/plugins/geoservices/esri/maps.json b/src/plugins/geoservices/esri/maps.json new file mode 100644 index 00000000..8167ae7d --- /dev/null +++ b/src/plugins/geoservices/esri/maps.json @@ -0,0 +1,123 @@ +{ + "mapSources": [ + { + "style": "StreetMap", + "name": "World Street Map", + "description": "ArcGIS Online World Street Map", + "mobile": true, + "night": false, + "url": "http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer", + "copyrightText": "© <a href='http://www.arcgis.com/home/item.html?id=3b93337983e9436f8db950e38a8629af'>Esri</a> contributors" + }, + + { + "style": "SatelliteMapDay", + "name": "World Imagery", + "": "ArcGIS Online World Imagery", + "mobile": true, + "night": false, + "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer", + "copyrightText": "© <a href='http://www.arcgis.com/home/item.html?id=10df2279f9684e4a9f6a7f08febac2a9'>Esri</a> contributors" + }, + + { + "style": "TerrainMap", + "name": "World Terrain Base", + "description": "ArcGIS Online World Terrain Base", + "mobile": false, + "night": false, + "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer", + "copyrightText": "© <a href='http://www.arcgis.com/home/item.html?id=c61ad8ab017d49e1a82f580ee1298931'>Esri</a> contributors" + }, + + { + "style": "CustomMap", + "name": "World Topography", + "description": "ArcGIS Online World Topography", + "mobile": true, + "night": false, + "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer", + "copyrightText": "© <a href='http://www.arcgis.com/home/item.html?id=30e5fe3149c34df1ba922e6f5bbf808f'>Esri</a> contributors" + }, + + { + "style": "CustomMap", + "name": "USA Topo Maps", + "description": "This map presents land cover and detailed topographic maps for the United States.", + "mobile": true, + "night": false, + "url": "http://services.arcgisonline.com/ArcGIS/rest/services/USA_Topo_Maps/MapServer", + "copyrightText": "© <a href='http://www.arcgis.com/home/item.html?id=99cd5fbd98934028802b4f797c4b1732'>Esri</a> contributors" + }, + + { + "style": "CustomMap", + "name": "National Geographic World Map", + "description": "National Geographic World Map", + "mobile": false, + "night": false, + "url": "http://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer", + "copyrightText": "© <a href='http://www.arcgis.com/home/item.html?id=b9b1b422198944fbbd5250b3241691b6'>Esri</a> contributors" + }, + + { + "style": "GrayStreetMap", + "name": "Light Gray Canvas", + "description": "Thematic content providing a neutral background with minimal colors", + "mobile": true, + "night": false, + "url": "http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer", + "copyrightText": "© <a href='http://www.arcgis.com/home/item.html?id=ed712cb1db3e4bae9e85329040fb9a49'>Esri</a> contributors" + }, + + { + "style": "CustomMap", + "name": "World Physical Map", + "description": "Natural Earth physical map for the world", + "mobile": false, + "night": false, + "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer", + "copyrightText": "© <a href='http://www.arcgis.com/home/item.html?id=c4ec722a1cd34cf0a23904aadf8923a0'>Esri</a> contributors" + }, + + { + "style": "CustomMap", + "name": "World Shaded Relief", + "description": "Portrays surface elevation as shaded relief", + "mobile": false, + "night": false, + "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer", + "copyrightText": "© <a href='http://www.arcgis.com/home/item.html?id=9c5370d0b54f4de1b48a3792d7377ff2'>Esri</a> contributors" + }, + + { + "style": "CustomMap", + "name": "World Ocean Base", + "description": "This map is designed to be used as a basemap by marine GIS professionals and as a reference map by anyone interested in ocean data", + "mobile": false, + "night": false, + "url": "http://server.arcgisonline.com/arcgis/rest/services/Ocean/World_Ocean_Base/MapServer", + "copyrightText": "© <a href='http://www.arcgis.com/home/item.html?id=1e126e7520f9466c9ca28b8f28b5e500'>Esri</a> contributors" + }, + + { + "style": "GrayStreetMap", + "name": "Dark Gray Canvas", + "description": "Thematic content providing a neutral background with minimal colors", + "mobile": false, + "night": true, + "url": "http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer", + "copyrightText": "© <a href='http://www.arcgis.com/home/item.html?id=a284a9b99b3446a3910d4144a50990f6'>Esri</a> contributors" + }, + + { + "style": "CustomMap", + "name": "DeLorme World Basemap", + "description": "DeLorme’s topographic basemap is a seamless global data set that portrays transportation, hydrography, jurisdiction boundaries, and major geographic features", + "mobile": false, + "night": false, + "url": "http://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer", + "copyrightText": "© <a href='http://www.arcgis.com/home/item.html?id=b165c3df453e4be6b5ac4fdb241effbe'>Esri</a> contributors" + } + ] +} diff --git a/src/plugins/geoservices/geoservices.pro b/src/plugins/geoservices/geoservices.pro index 3d0971f7..7a392b57 100644 --- a/src/plugins/geoservices/geoservices.pro +++ b/src/plugins/geoservices/geoservices.pro @@ -1,3 +1,3 @@ TEMPLATE = subdirs -SUBDIRS = nokia osm mapbox +SUBDIRS = nokia osm mapbox esri diff --git a/src/plugins/geoservices/mapbox/mapbox.pro b/src/plugins/geoservices/mapbox/mapbox.pro index d4797e37..ea011131 100644 --- a/src/plugins/geoservices/mapbox/mapbox.pro +++ b/src/plugins/geoservices/mapbox/mapbox.pro @@ -6,13 +6,19 @@ HEADERS += \ qgeoserviceproviderpluginmapbox.h \ qgeotiledmappingmanagerenginemapbox.h \ qgeotilefetchermapbox.h \ - qgeomapreplymapbox.h + qgeomapreplymapbox.h \ + qgeofiletilecachemapbox.h \ + qgeoroutingmanagerenginemapbox.h \ + qgeoroutereplymapbox.h SOURCES += \ qgeoserviceproviderpluginmapbox.cpp \ qgeotiledmappingmanagerenginemapbox.cpp \ qgeotilefetchermapbox.cpp \ - qgeomapreplymapbox.cpp + qgeomapreplymapbox.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/qgeofiletilecachemapbox.cpp b/src/plugins/geoservices/mapbox/qgeofiletilecachemapbox.cpp new file mode 100644 index 00000000..8cc3622b --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeofiletilecachemapbox.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeofiletilecachemapbox.h" +#include <QtLocation/private/qgeotilespec_p.h> +#include <QDir> + +QT_BEGIN_NAMESPACE + +QGeoFileTileCacheMapbox::QGeoFileTileCacheMapbox(const QList<QGeoMapType> &mapTypes, int scaleFactor, const QString &directory, QObject *parent) + :QGeoFileTileCache(directory, parent), m_mapTypes(mapTypes) +{ + m_scaleFactor = qBound(1, scaleFactor, 2); + for (int i=0; i < mapTypes.size(); i++) + m_mapNameToId.insert(mapTypes[i].name(), i); +} + +QGeoFileTileCacheMapbox::~QGeoFileTileCacheMapbox() +{ + +} + +QString QGeoFileTileCacheMapbox::tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const +{ + QString filename = spec.plugin(); + filename += QLatin1String("-"); + filename += m_mapTypes[spec.mapId()].name(); + filename += QLatin1String("-"); + filename += QString::number(spec.zoom()); + filename += QLatin1String("-"); + filename += QString::number(spec.x()); + filename += QLatin1String("-"); + filename += QString::number(spec.y()); + + //Append version if real version number to ensure backwards compatibility and eviction of old tiles + if (spec.version() != -1) { + filename += QLatin1String("-"); + filename += QString::number(spec.version()); + } + + filename += QLatin1String("-@"); + filename += QString::number(m_scaleFactor); + filename += QLatin1Char('x'); + + filename += QLatin1String("."); + filename += format; + + QDir dir = QDir(directory); + + return dir.filePath(filename); +} + +QGeoTileSpec QGeoFileTileCacheMapbox::filenameToTileSpec(const QString &filename) const +{ + QStringList parts = filename.split('.'); + + if (parts.length() != 3) + return QGeoTileSpec(); + + QString name = parts.at(0) + parts.at(1); + QStringList fields = name.split('-'); + + int length = fields.length(); + if (length != 6 && length != 7) { + return QGeoTileSpec(); + } else { + int scaleIdx = fields.last().indexOf("@"); + if (scaleIdx < 0 || fields.last().size() <= (scaleIdx + 2)) + return QGeoTileSpec(); + int scaleFactor = fields.last()[scaleIdx + 1].digitValue(); + if (scaleFactor != m_scaleFactor) + return QGeoTileSpec(); + } + + QList<int> numbers; + + bool ok = false; + for (int i = 2; i < length-1; ++i) { // skipping -@_X + ok = false; + int value = fields.at(i).toInt(&ok); + if (!ok) + return QGeoTileSpec(); + numbers.append(value); + } + + //File name without version, append default + if (numbers.length() < 4) + numbers.append(-1); + + return QGeoTileSpec(fields.at(0), + m_mapNameToId[fields.at(1)], + numbers.at(0), + numbers.at(1), + numbers.at(2), + numbers.at(3)); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapbox/qgeofiletilecachemapbox.h b/src/plugins/geoservices/mapbox/qgeofiletilecachemapbox.h new file mode 100644 index 00000000..faf9b2bf --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeofiletilecachemapbox.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOFILETILECACHEMAPBOX_H +#define QGEOFILETILECACHEMAPBOX_H + +#include <QtLocation/private/qgeofiletilecache_p.h> +#include <QMap> + +QT_BEGIN_NAMESPACE + +class QGeoFileTileCacheMapbox : public QGeoFileTileCache +{ + Q_OBJECT +public: + QGeoFileTileCacheMapbox(const QList<QGeoMapType> &mapTypes, int scaleFactor, const QString &directory = QString(), QObject *parent = 0); + ~QGeoFileTileCacheMapbox(); + +protected: + QString tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const Q_DECL_OVERRIDE; + QGeoTileSpec filenameToTileSpec(const QString &filename) const Q_DECL_OVERRIDE; + + QList<QGeoMapType> m_mapTypes; + QMap<QString, int> m_mapNameToId; + int m_scaleFactor; +}; + +QT_END_NAMESPACE + +#endif // QGEOFILETILECACHEMAPBOX_H 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 ec40716b..835f9d04 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> @@ -51,18 +52,22 @@ QGeoCodingManagerEngine *QGeoServiceProviderFactoryMapbox::createGeocodingManage return 0; } +static inline QString msgAccessTokenParameter() +{ + return QGeoServiceProviderFactoryMapbox::tr("Mapbox plugin requires a 'mapbox.access_token' parameter.\n" + "Please visit https://www.mapbox.com"); +} + QGeoMappingManagerEngine *QGeoServiceProviderFactoryMapbox::createMappingManagerEngine( const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const { - const QString mapId = parameters.value(QStringLiteral("mapbox.map_id")).toString(); const QString accessToken = parameters.value(QStringLiteral("mapbox.access_token")).toString(); - if (!mapId.isEmpty() && !accessToken.isEmpty()) { + if (!accessToken.isEmpty()) { return new QGeoTiledMappingManagerEngineMapbox(parameters, error, errorString); } else { *error = QGeoServiceProvider::MissingRequiredParameterError; - *errorString = tr("Mapbox plugin requires 'mapbox.map_id' and 'mapbox.access_token' parameters.\n" - "Please visit https://www.mapbox.com"); + *errorString = msgAccessTokenParameter(); return 0; } } @@ -70,11 +75,15 @@ 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 = msgAccessTokenParameter(); + return 0; + } } QPlaceManagerEngine *QGeoServiceProviderFactoryMapbox::createPlaceManagerEngine( diff --git a/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.cpp b/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.cpp index 4be5ac24..5404eb30 100644 --- a/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.cpp +++ b/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.cpp @@ -40,6 +40,7 @@ #include <QtLocation/private/qgeocameracapabilities_p.h> #include <QtLocation/private/qgeomaptype_p.h> #include <QtLocation/private/qgeotiledmap_p.h> +#include "qgeofiletilecachemapbox.h" QT_BEGIN_NAMESPACE @@ -54,19 +55,80 @@ QGeoTiledMappingManagerEngineMapbox::QGeoTiledMappingManagerEngineMapbox(const Q setTileSize(QSize(256, 256)); QList<QGeoMapType> mapTypes; - mapTypes << QGeoMapType(QGeoMapType::CustomMap, tr("Custom"), tr("Mapbox custom map"), false, false, 0); + // as index 0 to retain compatibility with the current API, that expects the passed map_id to be on by default. + if (parameters.contains(QStringLiteral("mapbox.mapping.map_id"))) { + const QString name = parameters.value(QStringLiteral("mapbox.mapping.map_id")).toString(); + mapTypes << QGeoMapType(QGeoMapType::CustomMap, name, name, false, false, mapTypes.size()); + } else if (parameters.contains(QStringLiteral("mapbox.map_id"))) { //deprecated + const QString name = parameters.value(QStringLiteral("mapbox.map_id")).toString(); + mapTypes << QGeoMapType(QGeoMapType::CustomMap, name, name, false, false, mapTypes.size()); + } + + // As of 2016.06.15, valid mapbox map_ids are documented at https://www.mapbox.com/api-documentation/#maps + //: Noun describing map type 'Street map' + mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox.streets"), tr("Street"), false, false, mapTypes.size()); + //: Noun describing type of a map using light colors (weak contrast) + mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox.light"), tr("Light"), false, false, mapTypes.size()); + //: Noun describing type of a map using dark colors + mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox.dark"), tr("Dark"), false, true, mapTypes.size()); + //: Noun describing type of a map created by satellite + mapTypes << QGeoMapType(QGeoMapType::SatelliteMapDay, QStringLiteral("mapbox.satellite"), tr("Satellite"), false, false, mapTypes.size()); + //: Noun describing type of a street map created by satellite + mapTypes << QGeoMapType(QGeoMapType::HybridMap, QStringLiteral("mapbox.streets-satellite"), tr("Streets Satellite"), false, false, mapTypes.size()); + //: Noun describing type of a map using wheat paste colors + mapTypes << QGeoMapType(QGeoMapType::CustomMap, QStringLiteral("mapbox.wheatpaste"), tr("Wheatpaste"), false, false, mapTypes.size()); + //: Noun describing type of a basic street map + mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox.streets-basic"), tr("Streets Basic"), false, false, mapTypes.size()); + //: Noun describing type of a map using cartoon-style fonts + mapTypes << QGeoMapType(QGeoMapType::CustomMap, QStringLiteral("mapbox.comic"), tr("Comic"), false, false, mapTypes.size()); + //: Noun describing type of a map for outdoor activities + mapTypes << QGeoMapType(QGeoMapType::PedestrianMap, QStringLiteral("mapbox.outdoors"), tr("Outdoors"), false, false, mapTypes.size()); + //: Noun describing type of a map for sports + mapTypes << QGeoMapType(QGeoMapType::CycleMap, QStringLiteral("mapbox.run-bike-hike"), tr("Run Bike Hike"), false, false, mapTypes.size()); + //: Noun describing type of a map drawn by pencil + mapTypes << QGeoMapType(QGeoMapType::CustomMap, QStringLiteral("mapbox.pencil"), tr("Pencil"), false, false, mapTypes.size()); + //: Noun describing type of a treasure map with pirate boat watermark + mapTypes << QGeoMapType(QGeoMapType::CustomMap, QStringLiteral("mapbox.pirates"), tr("Pirates"), false, false, mapTypes.size()); + //: Noun describing type of a map using emerald colors + mapTypes << QGeoMapType(QGeoMapType::CustomMap, QStringLiteral("mapbox.emerald"), tr("Emerald"), false, false, mapTypes.size()); + //: Noun describing type of a map with high contrast + mapTypes << QGeoMapType(QGeoMapType::CustomMap, QStringLiteral("mapbox.high-contrast"), tr("High Contrast"), false, false, mapTypes.size()); + + // New way to specify multiple customized map_ids via additional_map_ids + if (parameters.contains(QStringLiteral("mapbox.mapping.additional_map_ids"))) { + const QString ids = parameters.value(QStringLiteral("mapbox.mapping.additional_map_ids")).toString(); + const QStringList idList = ids.split(',', QString::SkipEmptyParts); + + for (const QString &name: idList) { + if (!name.isEmpty()) + mapTypes << QGeoMapType(QGeoMapType::CustomMap, name, name, false, false, mapTypes.size()); + } + } + + QVector<QString> mapIds; + for (int i=0; i < mapTypes.size(); ++i) + mapIds.push_back(mapTypes[i].name()); + setSupportedMapTypes(mapTypes); - QGeoTileFetcherMapbox *tileFetcher = new QGeoTileFetcherMapbox(this); + int scaleFactor = 1; + if (parameters.contains(QStringLiteral("mapbox.mapping.highdpi_tiles"))) { + const QString param = parameters.value(QStringLiteral("mapbox.mapping.highdpi_tiles")).toString().toLower(); + if (param == "true") + scaleFactor = 2; + } + + QGeoTileFetcherMapbox *tileFetcher = new QGeoTileFetcherMapbox(scaleFactor, this); + tileFetcher->setMapIds(mapIds); + if (parameters.contains(QStringLiteral("useragent"))) { const QByteArray ua = parameters.value(QStringLiteral("useragent")).toString().toLatin1(); tileFetcher->setUserAgent(ua); } - if (parameters.contains(QStringLiteral("mapbox.map_id"))) { - const QString id = parameters.value(QStringLiteral("mapbox.map_id")).toString(); - tileFetcher->setMapId(id); - } - if (parameters.contains(QStringLiteral("mapbox.format"))) { + if (parameters.contains(QStringLiteral("mapbox.mapping.format"))) { + const QString format = parameters.value(QStringLiteral("mapbox.mapping.format")).toString(); + tileFetcher->setFormat(format); + } else if (parameters.contains(QStringLiteral("mapbox.format"))) { //deprecated const QString format = parameters.value(QStringLiteral("mapbox.format")).toString(); tileFetcher->setFormat(format); } @@ -77,6 +139,85 @@ QGeoTiledMappingManagerEngineMapbox::QGeoTiledMappingManagerEngineMapbox(const Q setTileFetcher(tileFetcher); + // TODO: do this in a plugin-neutral way so that other tiled map plugins + // don't need this boilerplate or hardcode plugin name + + if (parameters.contains(QStringLiteral("mapbox.mapping.cache.directory"))) { + m_cacheDirectory = parameters.value(QStringLiteral("mapbox.mapping.cache.directory")).toString(); + } else { + // managerName() is not yet set, we have to hardcode the plugin name below + m_cacheDirectory = QAbstractGeoTileCache::baseLocationCacheDirectory() + QLatin1String("mapbox"); + } + + QGeoFileTileCache *tileCache = new QGeoFileTileCacheMapbox(mapTypes, scaleFactor, m_cacheDirectory); + + /* + * Disk cache setup -- defaults to Unitary since: + * + * The Mapbox free plan allows for 6000 tiles to be stored for offline uses, + * As of 2016.06.15, according to https://www.mapbox.com/help/mobile-offline/ . + * Thus defaulting to Unitary strategy, and setting 6000 tiles as default cache disk size + */ + if (parameters.contains(QStringLiteral("mapbox.mapping.cache.disk.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("mapbox.mapping.cache.disk.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyDisk(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyDisk(QGeoFileTileCache::Unitary); + } + if (parameters.contains(QStringLiteral("mapbox.mapping.cache.disk.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("mapbox.mapping.cache.disk.size")).toString().toInt(&ok); + if (ok) + tileCache->setMaxDiskUsage(cacheSize); + } else { + if (tileCache->costStrategyDisk() == QGeoFileTileCache::Unitary) + tileCache->setMaxDiskUsage(6000); // The maximum allowed with the free tier + } + + /* + * Memory cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("mapbox.mapping.cache.memory.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("mapbox.mapping.cache.memory.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyMemory(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("mapbox.mapping.cache.memory.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("mapbox.mapping.cache.memory.size")).toString().toInt(&ok); + if (ok) + tileCache->setMaxMemoryUsage(cacheSize); + } + + /* + * Texture cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("mapbox.mapping.cache.texture.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("mapbox.mapping.cache.texture.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyTexture(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("mapbox.mapping.cache.texture.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("mapbox.mapping.cache.texture.size")).toString().toInt(&ok); + if (ok) + tileCache->setExtraTextureUsage(cacheSize); + } + + + setTileCache(tileCache); + *error = QGeoServiceProvider::NoError; errorString->clear(); } diff --git a/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.h b/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.h index 379ca6b8..292e4211 100644 --- a/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.h +++ b/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.h @@ -53,6 +53,9 @@ public: ~QGeoTiledMappingManagerEngineMapbox(); QGeoMap *createMap(); + +private: + QString m_cacheDirectory; }; QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapbox/qgeotilefetchermapbox.cpp b/src/plugins/geoservices/mapbox/qgeotilefetchermapbox.cpp index 69a832df..062b4f89 100644 --- a/src/plugins/geoservices/mapbox/qgeotilefetchermapbox.cpp +++ b/src/plugins/geoservices/mapbox/qgeotilefetchermapbox.cpp @@ -44,14 +44,14 @@ QT_BEGIN_NAMESPACE -QGeoTileFetcherMapbox::QGeoTileFetcherMapbox(QObject *parent) +QGeoTileFetcherMapbox::QGeoTileFetcherMapbox(int scaleFactor, QObject *parent) : QGeoTileFetcher(parent), m_networkManager(new QNetworkAccessManager(this)), m_userAgent("Qt Location based application"), - m_mapId(""), m_format("png"), m_replyFormat("png"), m_accessToken("") { + m_scaleFactor = qBound(1, scaleFactor, 2); } void QGeoTileFetcherMapbox::setUserAgent(const QByteArray &userAgent) @@ -59,9 +59,9 @@ void QGeoTileFetcherMapbox::setUserAgent(const QByteArray &userAgent) m_userAgent = userAgent; } -void QGeoTileFetcherMapbox::setMapId(const QString &mapId) +void QGeoTileFetcherMapbox::setMapIds(const QVector<QString> &mapIds) { - m_mapId = mapId; + m_mapIds = mapIds; } void QGeoTileFetcherMapbox::setFormat(const QString &format) @@ -87,10 +87,11 @@ QGeoTiledMapReply *QGeoTileFetcherMapbox::getTileImage(const QGeoTileSpec &spec) request.setRawHeader("User-Agent", m_userAgent); request.setUrl(QUrl(QStringLiteral("http://api.tiles.mapbox.com/v4/") + - m_mapId + QLatin1Char('/') + + ((spec.mapId() >= m_mapIds.size()) ? QStringLiteral("mapbox.streets") : m_mapIds[spec.mapId()]) + QLatin1Char('/') + QString::number(spec.zoom()) + QLatin1Char('/') + QString::number(spec.x()) + QLatin1Char('/') + - QString::number(spec.y()) + QLatin1Char('.') + + QString::number(spec.y()) + + ((m_scaleFactor > 1) ? (QLatin1Char('@') + QString::number(m_scaleFactor) + QLatin1String("x.")) : QLatin1String(".")) + m_format + QLatin1Char('?') + QStringLiteral("access_token=") + m_accessToken)); diff --git a/src/plugins/geoservices/mapbox/qgeotilefetchermapbox.h b/src/plugins/geoservices/mapbox/qgeotilefetchermapbox.h index ddec2896..e52651e1 100644 --- a/src/plugins/geoservices/mapbox/qgeotilefetchermapbox.h +++ b/src/plugins/geoservices/mapbox/qgeotilefetchermapbox.h @@ -37,6 +37,7 @@ #ifndef QGEOTILEFETCHERMAPBOX_H #define QGEOTILEFETCHERMAPBOX_H +#include <qvector.h> #include <QtLocation/private/qgeotilefetcher_p.h> QT_BEGIN_NAMESPACE @@ -49,10 +50,10 @@ class QGeoTileFetcherMapbox : public QGeoTileFetcher Q_OBJECT public: - QGeoTileFetcherMapbox(QObject *parent = 0); + QGeoTileFetcherMapbox(int scaleFactor = 2, QObject *parent = 0); void setUserAgent(const QByteArray &userAgent); - void setMapId(const QString &mapId); + void setMapIds(const QVector<QString> &mapIds); void setFormat(const QString &format); void setAccessToken(const QString &accessToken); @@ -61,10 +62,11 @@ private: QNetworkAccessManager *m_networkManager; QByteArray m_userAgent; - QString m_mapId; QString m_format; QString m_replyFormat; QString m_accessToken; + QVector<QString> m_mapIds; + int m_scaleFactor; }; QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/nokia.pro b/src/plugins/geoservices/nokia/nokia.pro index 1aa31123..c60bc7af 100644 --- a/src/plugins/geoservices/nokia/nokia.pro +++ b/src/plugins/geoservices/nokia/nokia.pro @@ -2,12 +2,6 @@ TARGET = qtgeoservices_nokia QT += location-private positioning-private network -contains(QT_CONFIG, location-china-support) { - # China support - DEFINES += USE_CHINA_NETWORK_REGISTRATION - QT += systeminfo -} - HEADERS += \ qgeocodereply_nokia.h \ qgeocodejsonparser.h \ @@ -26,7 +20,8 @@ HEADERS += \ uri_constants.h \ qgeoerror_messages.h \ qgeomapversion.h \ - qgeotiledmap_nokia.h + qgeotiledmap_nokia.h \ + qgeofiletilecachenokia.h SOURCES += \ @@ -45,7 +40,8 @@ SOURCES += \ uri_constants.cpp \ qgeoerror_messages.cpp \ qgeomapversion.cpp \ - qgeotiledmap_nokia.cpp + qgeotiledmap_nokia.cpp \ + qgeofiletilecachenokia.cpp include(placesv2/placesv2.pri) diff --git a/src/plugins/geoservices/nokia/qgeofiletilecachenokia.cpp b/src/plugins/geoservices/nokia/qgeofiletilecachenokia.cpp new file mode 100644 index 00000000..8b795325 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeofiletilecachenokia.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeofiletilecachenokia.h" +#include <QtLocation/private/qgeotilespec_p.h> +#include <QDir> + +QT_BEGIN_NAMESPACE + +QGeoFileTileCacheNokia::QGeoFileTileCacheNokia(int ppi, const QString &directory, QObject *parent) + :QGeoFileTileCache(directory, parent) +{ + m_ppi = QString::number(ppi) + QLatin1String("p"); +} + +QGeoFileTileCacheNokia::~QGeoFileTileCacheNokia() +{ + +} + +QString QGeoFileTileCacheNokia::tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const +{ + QString filename = spec.plugin(); + filename += QLatin1String("-"); + filename += QString::number(spec.mapId()); + filename += QLatin1String("-"); + filename += QString::number(spec.zoom()); + filename += QLatin1String("-"); + filename += QString::number(spec.x()); + filename += QLatin1String("-"); + filename += QString::number(spec.y()); + + //Append version if real version number to ensure backwards compatibility and eviction of old tiles + if (spec.version() != -1) { + filename += QLatin1String("-"); + filename += QString::number(spec.version()); + } + + filename += QLatin1String("-"); + filename += m_ppi; + + filename += QLatin1String("."); + filename += format; + + QDir dir = QDir(directory); + + return dir.filePath(filename); +} + +QGeoTileSpec QGeoFileTileCacheNokia::filenameToTileSpec(const QString &filename) const +{ + QGeoTileSpec emptySpec; + + QStringList parts = filename.split('.'); + + if (parts.length() != 2) + return emptySpec; + + QString name = parts.at(0); + QStringList fields = name.split('-'); + + int length = fields.length(); + if (length != 6 && length != 7) + return emptySpec; + else if (fields.last() != m_ppi) + return QGeoTileSpec(); + + QList<int> numbers; + + bool ok = false; + for (int i = 1; i < length-1; ++i) { // skipping -<ppi> + ok = false; + int value = fields.at(i).toInt(&ok); + if (!ok) + return emptySpec; + numbers.append(value); + } + + //File name without version, append default + if (numbers.length() < 5) + numbers.append(-1); + + return QGeoTileSpec(fields.at(0), + numbers.at(0), + numbers.at(1), + numbers.at(2), + numbers.at(3), + numbers.at(4)); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeofiletilecachenokia.h b/src/plugins/geoservices/nokia/qgeofiletilecachenokia.h new file mode 100644 index 00000000..aba232f5 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeofiletilecachenokia.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOFILETILECACHENOKIA_H +#define QGEOFILETILECACHENOKIA_H + +#include <QtLocation/private/qgeofiletilecache_p.h> + +QT_BEGIN_NAMESPACE + +class QGeoFileTileCacheNokia : public QGeoFileTileCache +{ + Q_OBJECT +public: + QGeoFileTileCacheNokia(int ppi, const QString &directory = QString(), QObject *parent = 0); + ~QGeoFileTileCacheNokia(); + +protected: + virtual QString tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const Q_DECL_OVERRIDE; + virtual QGeoTileSpec filenameToTileSpec(const QString &filename) const Q_DECL_OVERRIDE; + + QString m_ppi; +}; + +QT_END_NAMESPACE + +#endif // QGEOFILETILECACHENOKIA_H diff --git a/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp b/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp index f6d05c36..4deac4ee 100644 --- a/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp +++ b/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp @@ -81,7 +81,11 @@ namespace token = parameters.value(QStringLiteral("here.token")).toString(); if (isValidParameter(appId) && isValidParameter(token)) - return; + return; + else if (!isValidParameter(appId)) + qWarning() << "Invalid here.app_id"; + else + qWarning() << "Invalid here.token"; if (parameters.contains(QStringLiteral("app_id")) || parameters.contains(QStringLiteral("token"))) qWarning() << QStringLiteral("Please prefix 'app_id' and 'token' with prefix 'here' (e.g.: 'here.app_id')"); diff --git a/src/plugins/geoservices/nokia/qgeotiledmap_nokia.cpp b/src/plugins/geoservices/nokia/qgeotiledmap_nokia.cpp index d83ad0f9..5179fff4 100644 --- a/src/plugins/geoservices/nokia/qgeotiledmap_nokia.cpp +++ b/src/plugins/geoservices/nokia/qgeotiledmap_nokia.cpp @@ -72,13 +72,13 @@ void QGeoTiledMapNokia::evaluateCopyrights(const QSet<QGeoTileSpec> &visibleTile const QString copyrightsString = m_engine->evaluateCopyrightsText(activeMapType(), cameraData().zoomLevel(), visibleTiles); - if (width() > 0 && height() > 0 && ((copyrightsString.isNull() && m_copyrightsSlab.isNull()) || copyrightsString != m_lastCopyrightsString)) { + if (viewportWidth() > 0 && viewportHeight() > 0 && ((copyrightsString.isNull() && m_copyrightsSlab.isNull()) || copyrightsString != m_lastCopyrightsString)) { QFont font("Sans Serif"); font.setPixelSize(fontSize); font.setStyleHint(QFont::SansSerif); font.setWeight(QFont::Bold); - QRect textBounds = QFontMetrics(font).boundingRect(0, 0, width(), height(), Qt::AlignBottom | Qt::AlignLeft | Qt::TextWordWrap, copyrightsString); + QRect textBounds = QFontMetrics(font).boundingRect(0, 0, viewportWidth(), viewportHeight(), Qt::AlignBottom | Qt::AlignLeft | Qt::TextWordWrap, copyrightsString); m_copyrightsSlab = QImage(m_logo.width() + textBounds.width() + spaceToLogo + blurRate * 2, qMax(m_logo.height(), textBounds.height() + blurRate * 2), diff --git a/src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.cpp b/src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.cpp index 4f44e5fd..6548aa2b 100644 --- a/src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.cpp +++ b/src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.cpp @@ -39,7 +39,7 @@ #include "qgeotiledmap_nokia.h" #include "qgeotilefetcher_nokia.h" #include "qgeotilespec_p.h" -#include "qgeofiletilecache_p.h" +#include "qgeofiletilecachenokia.h" #include <QDebug> #include <QDir> @@ -95,22 +95,40 @@ QGeoTiledMappingManagerEngineNokia::QGeoTiledMappingManagerEngineNokia( types << QGeoMapType(QGeoMapType::CarNavigationMap, tr("Car Navigation Map"), tr("Normal map view in daylight mode for car navigation"), false, false, 21); setSupportedMapTypes(types); - QGeoTileFetcherNokia *fetcher = new QGeoTileFetcherNokia(parameters, networkManager, this, tileSize()); + int ppi = 72; + if (parameters.contains(QStringLiteral("here.mapping.highdpi_tiles"))) { + const QString param = parameters.value(QStringLiteral("here.mapping.highdpi_tiles")).toString().toLower(); + if (param == "true") + ppi = 250; + } + + QGeoTileFetcherNokia *fetcher = new QGeoTileFetcherNokia(parameters, networkManager, this, tileSize(), ppi); setTileFetcher(fetcher); + /* TILE CACHE */ // TODO: do this in a plugin-neutral way so that other tiled map plugins // don't need this boilerplate or hardcode plugin name - if (parameters.contains(QStringLiteral("here.mapping.cache.directory"))) { m_cacheDirectory = parameters.value(QStringLiteral("here.mapping.cache.directory")).toString(); } else { // managerName() is not yet set, we have to hardcode the plugin name below - m_cacheDirectory = QAbstractGeoTileCache::baseCacheDirectory() + QLatin1String("here"); + m_cacheDirectory = QAbstractGeoTileCache::baseLocationCacheDirectory() + QLatin1String("here"); } - QAbstractGeoTileCache *tileCache = new QGeoFileTileCache(m_cacheDirectory); - setTileCache(tileCache); - + QGeoFileTileCache *tileCache = new QGeoFileTileCacheNokia(ppi, m_cacheDirectory); + + /* + * Disk cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("here.mapping.cache.disk.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("here.mapping.cache.disk.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyDisk(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); + } if (parameters.contains(QStringLiteral("here.mapping.cache.disk.size"))) { bool ok = false; int cacheSize = parameters.value(QStringLiteral("here.mapping.cache.disk.size")).toString().toInt(&ok); @@ -118,6 +136,18 @@ QGeoTiledMappingManagerEngineNokia::QGeoTiledMappingManagerEngineNokia( tileCache->setMaxDiskUsage(cacheSize); } + /* + * Memory cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("here.mapping.cache.memory.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("here.mapping.cache.memory.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyMemory(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + } if (parameters.contains(QStringLiteral("here.mapping.cache.memory.size"))) { bool ok = false; int cacheSize = parameters.value(QStringLiteral("here.mapping.cache.memory.size")).toString().toInt(&ok); @@ -125,6 +155,18 @@ QGeoTiledMappingManagerEngineNokia::QGeoTiledMappingManagerEngineNokia( tileCache->setMaxMemoryUsage(cacheSize); } + /* + * Texture cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("here.mapping.cache.texture.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("here.mapping.cache.texture.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyTexture(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + } if (parameters.contains(QStringLiteral("here.mapping.cache.texture.size"))) { bool ok = false; int cacheSize = parameters.value(QStringLiteral("here.mapping.cache.texture.size")).toString().toInt(&ok); @@ -132,6 +174,7 @@ QGeoTiledMappingManagerEngineNokia::QGeoTiledMappingManagerEngineNokia( tileCache->setExtraTextureUsage(cacheSize); } + setTileCache(tileCache); populateMapSchemes(); loadMapVersion(); QMetaObject::invokeMethod(fetcher, "fetchCopyrightsData", Qt::QueuedConnection); diff --git a/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp b/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp index 50acc2a0..a55f71c0 100644 --- a/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp +++ b/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp @@ -56,11 +56,11 @@ QT_BEGIN_NAMESPACE namespace { - QString sizeToStr(const QSize &size) + QString sizeToStr(int size) { - if (size.height() >= 512 || size.width() >= 512) + if (size > 256) return QStringLiteral("512"); - else if (size.height() >= 256 || size.width() >= 256) + else if (size > 128) return QStringLiteral("256"); else return QStringLiteral("128"); // 128 pixel tiles are deprecated. @@ -74,13 +74,14 @@ namespace QGeoTileFetcherNokia::QGeoTileFetcherNokia(const QVariantMap ¶meters, QGeoNetworkAccessManager *networkManager, QGeoTiledMappingManagerEngineNokia *engine, - const QSize &tileSize) -: QGeoTileFetcher(engine), m_engineNokia(engine), m_networkManager(networkManager), - m_tileSize(tileSize), m_copyrightsReply(0), + const QSize &tileSize, + int ppi) +: QGeoTileFetcher(engine), m_engineNokia(engine), m_networkManager(networkManager), m_ppi(ppi), m_copyrightsReply(0), m_baseUriProvider(new QGeoUriProvider(this, parameters, QStringLiteral("here.mapping.host"), MAP_TILES_HOST)), m_aerialUriProvider(new QGeoUriProvider(this, parameters, QStringLiteral("here.mapping.host.aerial"), MAP_TILES_HOST_AERIAL)) { Q_ASSERT(networkManager); + m_tileSize = qMax(tileSize.width(), tileSize.height()); m_networkManager->setParent(this); m_applicationId = parameters.value(QStringLiteral("here.app_id")).toString(); @@ -94,7 +95,11 @@ QGeoTileFetcherNokia::~QGeoTileFetcherNokia() QGeoTiledMapReply *QGeoTileFetcherNokia::getTileImage(const QGeoTileSpec &spec) { // TODO add error detection for if request.connectivityMode() != QGraphicsGeoMap::OnlineMode - QString rawRequest = getRequestString(spec); + int ppi = m_ppi; + if ((spec.mapId() == 2) || (spec.mapId() == 12) || (spec.mapId() == 21)) + ppi = 72; // HiDpi apparently not supported for these maps + + QString rawRequest = getRequestString(spec, ppi); if (rawRequest.isEmpty()) { return new QGeoTiledMapReply(QGeoTiledMapReply::UnknownError, tr("Mapping manager no longer exists"), this); @@ -110,7 +115,7 @@ QGeoTiledMapReply *QGeoTileFetcherNokia::getTileImage(const QGeoTileSpec &spec) return mapReply; } -QString QGeoTileFetcherNokia::getRequestString(const QGeoTileSpec &spec) +QString QGeoTileFetcherNokia::getRequestString(const QGeoTileSpec &spec, int ppi) { if (!m_engineNokia) return QString(); @@ -136,11 +141,11 @@ QString QGeoTileFetcherNokia::getRequestString(const QGeoTileSpec &spec) requestString += slash; requestString += QString::number(spec.y()); requestString += slash; - requestString += sizeToStr(m_tileSize); + requestString += ((ppi > 72)) ? sizeToStr(m_tileSize * 2) : sizeToStr(m_tileSize); static const QString slashpng("/png8"); requestString += slashpng; - if (!m_token.isEmpty() && !m_applicationId.isEmpty()) { + if (!m_token.isEmpty() && !m_applicationId.isEmpty()) { // TODO: remove the if requestString += "?token="; requestString += m_token; @@ -148,9 +153,10 @@ QString QGeoTileFetcherNokia::getRequestString(const QGeoTileSpec &spec) requestString += m_applicationId; } + requestString += "&ppi=" + QString::number(ppi); + requestString += "&lg="; requestString += getLanguageString(); - return requestString; } diff --git a/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h b/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h index 44f2ad07..06d1bba9 100644 --- a/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h +++ b/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h @@ -57,7 +57,7 @@ class QGeoTileFetcherNokia : public QGeoTileFetcher public: QGeoTileFetcherNokia(const QVariantMap ¶meters, QGeoNetworkAccessManager *networkManager, - QGeoTiledMappingManagerEngineNokia *engine, const QSize &tileSize); + QGeoTiledMappingManagerEngineNokia *engine, const QSize &tileSize, int ppi); ~QGeoTileFetcherNokia(); QGeoTiledMapReply *getTileImage(const QGeoTileSpec &spec); @@ -74,13 +74,14 @@ public Q_SLOTS: private: Q_DISABLE_COPY(QGeoTileFetcherNokia) - QString getRequestString(const QGeoTileSpec &spec); + QString getRequestString(const QGeoTileSpec &spec, int ppi=72); QString getLanguageString() const; QPointer<QGeoTiledMappingManagerEngineNokia> m_engineNokia; QGeoNetworkAccessManager *m_networkManager; - QSize m_tileSize; + int m_tileSize; + int m_ppi; QString m_token; QNetworkReply *m_copyrightsReply; QNetworkReply *m_versionReply; diff --git a/src/plugins/geoservices/nokia/qgeouriprovider.cpp b/src/plugins/geoservices/nokia/qgeouriprovider.cpp index 05ace120..80b47f31 100644 --- a/src/plugins/geoservices/nokia/qgeouriprovider.cpp +++ b/src/plugins/geoservices/nokia/qgeouriprovider.cpp @@ -35,10 +35,6 @@ ****************************************************************************/ #include "qgeouriprovider.h" -#ifdef USE_CHINA_NETWORK_REGISTRATION -#include <QNetworkInfo> -#endif - #include <QMap> #include <QVariant> #include <QSet> @@ -46,14 +42,6 @@ QT_BEGIN_NAMESPACE -namespace -{ - const QString CHINA_MCC = QLatin1String("460"); // China mobile country code - const QString CHINA2_MCC = QLatin1String("461"); // China mobile country code - const QString HONG_KONG_MCC = QLatin1String("454"); // Hong Kong mobile country code - const QString MACAU_MCC = QLatin1String("455"); // Macau mobile country code -} - QGeoUriProvider::QGeoUriProvider( QObject *parent, const QVariantMap ¶meters, @@ -61,17 +49,11 @@ QGeoUriProvider::QGeoUriProvider( const QString &internationalHost, const QString &localizedHost) : QObject(parent) -#ifdef USE_CHINA_NETWORK_REGISTRATION - , m_networkInfo(new QNetworkInfo(this)) -#endif , m_internationalHost(parameters.value(hostParameterName, internationalHost).toString()) , m_localizedHost(localizedHost) , m_firstSubdomain(QChar::Null) , m_maxSubdomains(0) { -#ifdef USE_CHINA_NETWORK_REGISTRATION - QObject::connect(m_networkInfo, SIGNAL(currentMobileCountryCodeChanged(int,QString)), this, SLOT(mobileCountryCodeChanged(int,QString))); -#endif setCurrentHost(isInternationalNetwork() || m_localizedHost.isEmpty() ? m_internationalHost : m_localizedHost); } @@ -109,26 +91,7 @@ void QGeoUriProvider::mobileCountryCodeChanged(int interface, const QString& mcc bool QGeoUriProvider::isInternationalNetwork() const { -#ifndef USE_CHINA_NETWORK_REGISTRATION - return true; -#else - static QSet<QString> codes; - if (codes.empty()) { - codes.insert(CHINA_MCC); - codes.insert(CHINA2_MCC); - } - - QNetworkInfo::NetworkMode mode = m_networkInfo->currentNetworkMode(); - - int interfaces = m_networkInfo->networkInterfaceCount(mode); - for (int i = 0; i < interfaces; ++i) { - QString mcc = m_networkInfo->currentMobileCountryCode(interfaces); - if (codes.contains(mcc)) - return false; - } - return true; -#endif // USE_CHINA_NETWORK_REGISTRATION } QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeouriprovider.h b/src/plugins/geoservices/nokia/qgeouriprovider.h index 1bd30b52..e8d93a0f 100644 --- a/src/plugins/geoservices/nokia/qgeouriprovider.h +++ b/src/plugins/geoservices/nokia/qgeouriprovider.h @@ -64,9 +64,6 @@ private: bool isInternationalNetwork() const; void setCurrentHost(const QString &host); -#ifdef USE_CHINA_NETWORK_REGISTRATION - QNetworkInfo *m_networkInfo; -#endif const QString m_internationalHost; const QString m_localizedHost; QString m_currentHost; diff --git a/src/plugins/geoservices/osm/osm.pro b/src/plugins/geoservices/osm/osm.pro index 56f4cb33..86a62744 100644 --- a/src/plugins/geoservices/osm/osm.pro +++ b/src/plugins/geoservices/osm/osm.pro @@ -1,6 +1,6 @@ TARGET = qtgeoservices_osm -QT += location-private positioning-private network +QT += location-private positioning-private network concurrent HEADERS += \ qgeoserviceproviderpluginosm.h \ @@ -15,6 +15,7 @@ HEADERS += \ qplacesearchreplyosm.h \ qplacecategoriesreplyosm.h \ qgeotiledmaposm.h \ + qgeofiletilecacheosm.h \ qgeotileproviderosm.h SOURCES += \ @@ -30,6 +31,7 @@ SOURCES += \ qplacesearchreplyosm.cpp \ qplacecategoriesreplyosm.cpp \ qgeotiledmaposm.cpp \ + qgeofiletilecacheosm.cpp \ qgeotileproviderosm.cpp diff --git a/src/plugins/geoservices/osm/providers/5.8/cycle b/src/plugins/geoservices/osm/providers/5.8/cycle new file mode 100644 index 00000000..5e37aab2 --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/cycle @@ -0,0 +1,8 @@ +{ + "UrlTemplate" : "http://a.tile.thunderforest.com/cycle/%z/%x/%y.png", + "ImageFormat" : "png", + "QImageFormat" : "Indexed8", + "ID" : "thf-cycle", + "MapCopyRight" : "<a href='http://www.thunderforest.com/'>Thunderforest</a>", + "DataCopyRight" : "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors" +} diff --git a/src/plugins/geoservices/osm/providers/5.8/hiking b/src/plugins/geoservices/osm/providers/5.8/hiking new file mode 100644 index 00000000..1bb182e4 --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/hiking @@ -0,0 +1,9 @@ +{ + "UrlTemplate" : "http://b.tiles.wmflabs.org/hikebike/%z/%x/%y.png", + "ImageFormat" : "png", + "QImageFormat" : "Indexed8", + "ID" : "wmf-hike", + "MaximumZoomLevel" : 18, + "MapCopyRight" : "<a href='https://wikimediafoundation.org/wiki/Terms_of_Use'>WikiMedia Foundation</a>", + "DataCopyRight" : "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors" +} diff --git a/src/plugins/geoservices/osm/providers/5.8/night-transit b/src/plugins/geoservices/osm/providers/5.8/night-transit new file mode 100644 index 00000000..988a096a --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/night-transit @@ -0,0 +1,9 @@ +{ + "UrlTemplate" : "http://a.tile.thunderforest.com/transport-dark/%z/%x/%y.png", + "ImageFormat" : "png", + "QImageFormat" : "Indexed8", + "ID" : "thf-nighttransit", + "MaximumZoomLevel" : 19, + "MapCopyRight" : "<a href='http://www.thunderforest.com/'>Thunderforest</a>", + "DataCopyRight" : "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors" +} diff --git a/src/plugins/geoservices/osm/providers/5.8/satellite b/src/plugins/geoservices/osm/providers/5.8/satellite new file mode 100644 index 00000000..5c48a077 --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/satellite @@ -0,0 +1,10 @@ +{ + "Enabled" : false, + "UrlTemplate" : "http://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/%z/%y/%x", + "ImageFormat" : "jpg", + "QImageFormat" : "RGB888", + "ID" : "usgs-l7", + "MaximumZoomLevel" : 8, + "MapCopyRight" : "<a href='http://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer'>USGS The National Map: Orthoimagery</a>", + "DataCopyRight" : "<a href='http://landsat.gsfc.nasa.gov/?page_id=2339'>USGS/NASA Landsat</a>" +} diff --git a/src/plugins/geoservices/osm/providers/5.8/street b/src/plugins/geoservices/osm/providers/5.8/street new file mode 100644 index 00000000..b3bccf1d --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/street @@ -0,0 +1,10 @@ +{ + "UrlTemplate" : "http://korona.geog.uni-heidelberg.de/tiles/roads/x=%x&y=%y&z=%z", + "ImageFormat" : "jpg", + "QImageFormat" : "Indexed8", + "ID" : "oms-street", + "MaximumZoomLevel" : 20, + "MapCopyRight" : "<a href='http://giscience.uni-hd.de/'>GIScience Research Group @ University of Heidelberg</a>", + "StyleCopyRight" : "<a href='http://www.geog.uni-heidelberg.de/personen/gis_rylov.html'>Maxim Rylov</a>", + "DataCopyRight" : "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors" +} diff --git a/src/plugins/geoservices/osm/providers/5.8/street-hires b/src/plugins/geoservices/osm/providers/5.8/street-hires new file mode 100644 index 00000000..9819f619 --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/street-hires @@ -0,0 +1,9 @@ +{ + "UrlTemplate" : "https://maps.wikimedia.org/osm-intl/%z/%x/%y@2x.png", + "ImageFormat" : "png", + "QImageFormat" : "Indexed8", + "MaximumZoomLevel" : 18, + "ID" : "wmf-intl-2x", + "MapCopyRight" : "<a href='https://wikimediafoundation.org/wiki/Terms_of_Use'>WikiMedia Foundation</a>", + "DataCopyRight" : "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors" +} diff --git a/src/plugins/geoservices/osm/providers/5.8/terrain b/src/plugins/geoservices/osm/providers/5.8/terrain new file mode 100644 index 00000000..7fc6636c --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/terrain @@ -0,0 +1,9 @@ +{ + "UrlTemplate" : "http://a.tile.thunderforest.com/landscape/%z/%x/%y.png", + "ImageFormat" : "png", + "QImageFormat" : "Indexed8", + "ID" : "thf-landsc", + "MaximumZoomLevel" : 19, + "MapCopyRight" : "<a href='http://www.thunderforest.com/'>Thunderforest</a>", + "DataCopyRight" : "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors" +} diff --git a/src/plugins/geoservices/osm/providers/5.8/transit b/src/plugins/geoservices/osm/providers/5.8/transit new file mode 100644 index 00000000..ebf87b05 --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/transit @@ -0,0 +1,9 @@ +{ + "UrlTemplate" : "http://a.tile.thunderforest.com/transport/%z/%x/%y.png", + "ImageFormat" : "png", + "QImageFormat" : "Indexed8", + "ID" : "thf-transit", + "MaximumZoomLevel" : 19, + "MapCopyRight" : "<a href='http://www.thunderforest.com/'>Thunderforest</a>", + "DataCopyRight" : "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors" +} diff --git a/src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp b/src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp new file mode 100644 index 00000000..a563cced --- /dev/null +++ b/src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp @@ -0,0 +1,360 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeofiletilecacheosm.h" +#include <QtLocation/private/qgeotilespec_p.h> +#include <QDir> +#include <QDirIterator> +#include <QPair> +#include <QDateTime> +#include <QtConcurrent> +#include <QThread> + +QT_BEGIN_NAMESPACE + +QGeoFileTileCacheOsm::QGeoFileTileCacheOsm(const QVector<QGeoTileProviderOsm *> &providers, + const QString &offlineDirectory, + const QString &directory, + QObject *parent) +: QGeoFileTileCache(directory, parent), m_offlineDirectory(offlineDirectory), m_requestCancel(0), m_providers(providers) +{ + m_highDpi.resize(providers.size()); + for (int i = 0; i < providers.size(); i++) { + m_highDpi[i] = providers[i]->isHighDpi(); + m_mapIdFutures[providers[i]->mapType().mapId()].isFinished(); // To construct a default future for this mapId + connect(providers[i], &QGeoTileProviderOsm::resolutionFinished, this, &QGeoFileTileCacheOsm::onProviderResolutionFinished); + connect(providers[i], &QGeoTileProviderOsm::resolutionError, this, &QGeoFileTileCacheOsm::onProviderResolutionFinished); + } +} + +QGeoFileTileCacheOsm::~QGeoFileTileCacheOsm() +{ + m_requestCancel = 1; + m_future.waitForFinished(); + for (const QGeoTileProviderOsm *p : m_providers) + m_mapIdFutures[p->mapType().mapId()].waitForFinished(); +} + +QSharedPointer<QGeoTileTexture> QGeoFileTileCacheOsm::get(const QGeoTileSpec &spec) +{ + QSharedPointer<QGeoTileTexture> tt = getFromMemory(spec); + if (tt) + return tt; + if ((tt = getFromOfflineStorage(spec))) + return tt; + return getFromDisk(spec); +} + +void QGeoFileTileCacheOsm::onProviderResolutionFinished(const QGeoTileProviderOsm *provider) +{ + clearObsoleteTiles(provider); + Q_UNUSED(provider) + for (int i = 0; i < m_providers.size(); i++) { + if (m_providers[i]->isHighDpi() != m_highDpi[i]) { // e.g., HiDpi was requested but only LoDpi is available + int mapId = m_providers[i]->mapType().mapId(); + m_highDpi[i] = m_providers[i]->isHighDpi(); + + // reload cache for mapId i + dropTiles(mapId); + loadTiles(mapId); + + // reload offline registry for mapId i + m_mapIdFutures[mapId] = QtConcurrent::run(this, &QGeoFileTileCacheOsm::initOfflineRegistry, mapId); + + // send signal to clear scene in all maps created through this provider that use the reloaded tiles + emit mapDataUpdated(mapId); + } + } +} + +// On resolution error the provider is removed ONLY if there is no enabled hardcoded fallback. +// Hardcoded fallbacks also have a timestamp, that can get updated with Qt releases. +void QGeoFileTileCacheOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + clearObsoleteTiles(provider); // this still removes tiles who happen to be older than qgeotileproviderosm.cpp defaultTs +} + +void QGeoFileTileCacheOsm::init() +{ + if (directory_.isEmpty()) + directory_ = baseLocationCacheDirectory(); + QDir::root().mkpath(directory_); + + // find max mapId + int max = 0; + for (auto p: m_providers) + if (p->mapType().mapId() > max) + max = p->mapType().mapId(); + // Create a mapId to maxTimestamp LUT.. + m_maxMapIdTimestamps.resize(max+1); // initializes to invalid QDateTime + + // .. by finding the newest file in each tileset (tileset = mapId). + QDir dir(directory_); + QStringList formats; + formats << QLatin1String("*.*"); + QStringList files = dir.entryList(formats, QDir::Files); + + for (const QString &tileFileName : files) { + QGeoTileSpec spec = filenameToTileSpec(tileFileName); + if (spec.zoom() == -1) + continue; + QFileInfo fi(dir.filePath(tileFileName)); + if (fi.lastModified() > m_maxMapIdTimestamps[spec.mapId()]) + m_maxMapIdTimestamps[spec.mapId()] = fi.lastModified(); + } + + // Base class ::init() + QGeoFileTileCache::init(); + + for (QGeoTileProviderOsm * p: m_providers) + clearObsoleteTiles(p); + + if (!m_offlineDirectory.isEmpty()) + m_future = QtConcurrent::run(this, &QGeoFileTileCacheOsm::initOfflineRegistry, -1); +} + +QSharedPointer<QGeoTileTexture> QGeoFileTileCacheOsm::getFromOfflineStorage(const QGeoTileSpec &spec) +{ + QMutexLocker locker(&storageLock); + if (m_tilespecToOfflineFilepath.contains(spec)) { + QFile file(m_tilespecToOfflineFilepath[spec]); + file.open(QIODevice::ReadOnly); + QByteArray bytes = file.readAll(); + file.close(); + locker.unlock(); + + QImage image; + if (!image.loadFromData(bytes)) { + handleError(spec, QLatin1String("Problem with tile image")); + return QSharedPointer<QGeoTileTexture>(0); + } + + addToMemoryCache(spec, bytes, QString()); + QSharedPointer<QGeoTileTexture> tt = addToTextureCache(spec, image); + if (tt) + return tt; + } + + return QSharedPointer<QGeoTileTexture>(); +} + +void QGeoFileTileCacheOsm::dropTiles(int mapId) +{ + QList<QGeoTileSpec> keys; + keys = textureCache_.keys(); + for (const QGeoTileSpec &k : keys) + if (k.mapId() == mapId) + textureCache_.remove(k); + + keys = memoryCache_.keys(); + for (const QGeoTileSpec &k : keys) + if (k.mapId() == mapId) + memoryCache_.remove(k); + + keys = diskCache_.keys(); + for (const QGeoTileSpec &k : keys) + if (k.mapId() == mapId) + diskCache_.remove(k); +} + +void QGeoFileTileCacheOsm::loadTiles(int mapId) +{ + QStringList formats; + formats << QLatin1String("*.*"); + + QDir dir(directory_); + QStringList files = dir.entryList(formats, QDir::Files); + + for (int i = 0; i < files.size(); ++i) { + QGeoTileSpec spec = filenameToTileSpec(files.at(i)); + if (spec.zoom() == -1 || spec.mapId() != mapId) + continue; + QString filename = dir.filePath(files.at(i)); + addToDiskCache(spec, filename); + } +} + +void QGeoFileTileCacheOsm::initOfflineRegistry(int mapId) +{ + // Dealing with duplicates: picking the newest + QMap<QString, QPair<QString, QDateTime> > fileDates; // key is filename, value is <filepath, lastmodified> + QDirIterator it(m_offlineDirectory, QStringList() << "*.*", QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks ); + while (it.hasNext()) { + const QString &path = it.next(); + QFileInfo f(path); + if (!fileDates.contains(f.fileName()) || fileDates[f.fileName()].second < f.lastModified()) + fileDates[f.fileName()] = QPair<QString, QDateTime>(f.filePath(), f.lastModified()); + if (m_requestCancel) + return; + } + + // Clear the content of the index. Entirely (at startup), or selectively (when a provider resolution changes the highDpi status). + if (mapId < 0) { + storageLock.lock(); + m_tilespecToOfflineFilepath.clear(); + storageLock.unlock(); + } else { + QList<QGeoTileSpec> toRemove; + for (auto i = m_tilespecToOfflineFilepath.constBegin(); i != m_tilespecToOfflineFilepath.constEnd(); ++i) { + if (i.key().mapId() == mapId) + toRemove.append(i.key()); + } + storageLock.lock(); + for (const auto &i : toRemove) + m_tilespecToOfflineFilepath.remove(i); + storageLock.unlock(); + } + if (m_requestCancel) + return; + + // Fill the index entirely or selectively + int count = 0; + for (auto i= fileDates.constBegin(); i != fileDates.constEnd(); ++i) { + QGeoTileSpec spec = filenameToTileSpec(i.key()); + if (spec.zoom() == -1) + continue; + if (mapId >= 0 && spec.mapId() != mapId) // if mapId != -1, pick up only those files with that mapId. + continue; + count++; + storageLock.lock(); + m_tilespecToOfflineFilepath[spec] = i.value().first; + storageLock.unlock(); + if (m_requestCancel) + return; + } + //qInfo() << "OSM plugin has found and is using "<< count <<" offline tiles"; +} + +QString QGeoFileTileCacheOsm::tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const +{ + int providerId = spec.mapId() - 1; + if (providerId < 0 || providerId >= m_providers.size()) + return QString(); + QString filename = spec.plugin(); + filename += QLatin1String("-"); + filename += (m_providers[providerId]->isHighDpi()) ? QLatin1Char('h') : QLatin1Char('l'); + filename += QLatin1String("-"); + filename += QString::number(spec.mapId()); + filename += QLatin1String("-"); + filename += QString::number(spec.zoom()); + filename += QLatin1String("-"); + filename += QString::number(spec.x()); + filename += QLatin1String("-"); + filename += QString::number(spec.y()); + + //Append version if real version number to ensure backwards compatibility and eviction of old tiles + if (spec.version() != -1) { + filename += QLatin1String("-"); + filename += QString::number(spec.version()); + } + + filename += QLatin1String("."); + filename += format; + + QDir dir = QDir(directory); + + return dir.filePath(filename); +} + +QGeoTileSpec QGeoFileTileCacheOsm::filenameToTileSpec(const QString &filename) const +{ + QGeoTileSpec emptySpec; + + QStringList parts = filename.split('.'); + + if (parts.length() != 2) + return emptySpec; + + QString name = parts.at(0); + QStringList fields = name.split('-'); + + int length = fields.length(); + if (length != 6 && length != 7) + return emptySpec; + + QList<int> numbers; + + bool ok = false; + for (int i = 2; i < length; ++i) { + ok = false; + int value = fields.at(i).toInt(&ok); + if (!ok) + return emptySpec; + numbers.append(value); + } + + bool highDpi = m_providers[numbers.at(0) - 1]->isHighDpi(); + if (highDpi && fields.at(1) != QLatin1Char('h')) + return emptySpec; + else if (!highDpi && fields.at(1) != QLatin1Char('l')) + return emptySpec; + + //File name without version, append default + if (numbers.length() < 5) + numbers.append(-1); + + return QGeoTileSpec(fields.at(0), + numbers.at(0), + numbers.at(1), + numbers.at(2), + numbers.at(3), + numbers.at(4)); +} + +void QGeoFileTileCacheOsm::clearObsoleteTiles(const QGeoTileProviderOsm *p) +{ + // process initialized providers, and connect the others + + if (p->isResolved()) { + if (m_maxMapIdTimestamps[p->mapType().mapId()].isValid() && // there are tiles in the cache + p->timestamp() > m_maxMapIdTimestamps[p->mapType().mapId()]) { // and they are older than the provider + qInfo() << "provider for " << p->mapType().name() << " timestamp: " << p->timestamp() + << " -- data last modified: " << m_maxMapIdTimestamps[p->mapType().mapId()] << ". Clearing."; + clearMapId(p->mapType().mapId()); + m_maxMapIdTimestamps[p->mapType().mapId()] = p->timestamp(); // don't do it again. + } + } else { + connect(p, &QGeoTileProviderOsm::resolutionFinished, + this, &QGeoFileTileCacheOsm::onProviderResolutionFinished); +#if 0 // If resolution fails, better not try to remove anything. Beside, on error, resolutionFinished is also emitted. + connect(p, &QGeoTileProviderOsm::resolutionError, + this, &QGeoFileTileCacheOsm::onProviderResolutionError); +#endif + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/osm/qgeofiletilecacheosm.h b/src/plugins/geoservices/osm/qgeofiletilecacheosm.h new file mode 100644 index 00000000..d26cad4a --- /dev/null +++ b/src/plugins/geoservices/osm/qgeofiletilecacheosm.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOFILETILECACHEOSM_H +#define QGEOFILETILECACHEOSM_H + +#include "qgeotileproviderosm.h" +#include <QtLocation/private/qgeofiletilecache_p.h> +#include <QHash> +#include <QtConcurrent> +#include <qatomic.h> + +QT_BEGIN_NAMESPACE + +class QGeoFileTileCacheOsm : public QGeoFileTileCache +{ + Q_OBJECT +public: + QGeoFileTileCacheOsm(const QVector<QGeoTileProviderOsm *> &providers, + const QString &offlineDirectory = QString(), + const QString &directory = QString(), + QObject *parent = 0); + ~QGeoFileTileCacheOsm(); + + QSharedPointer<QGeoTileTexture> get(const QGeoTileSpec &spec) Q_DECL_OVERRIDE; + +Q_SIGNALS: + void mapDataUpdated(int mapId); + +protected Q_SLOTS: + void onProviderResolutionFinished(const QGeoTileProviderOsm *provider); + void onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error); + +protected: + void init() Q_DECL_OVERRIDE; + QString tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const Q_DECL_OVERRIDE; + QGeoTileSpec filenameToTileSpec(const QString &filename) const Q_DECL_OVERRIDE; + QSharedPointer<QGeoTileTexture> getFromOfflineStorage(const QGeoTileSpec &spec); + void dropTiles(int mapId); + void loadTiles(int mapId); + + void initOfflineRegistry(int mapId = -1); + void clearObsoleteTiles(const QGeoTileProviderOsm *p); + + QString m_offlineDirectory; + QHash<QGeoTileSpec, QString> m_tilespecToOfflineFilepath; + QAtomicInt m_requestCancel; + QFuture<void> m_future; + QMap<int, QFuture<void>> m_mapIdFutures; + QMutex storageLock; + QVector<QGeoTileProviderOsm *> m_providers; + QVector<bool> m_highDpi; + QVector<QDateTime> m_maxMapIdTimestamps; +}; + +QT_END_NAMESPACE + +#endif // QGEOFILETILECACHEOSM_H diff --git a/src/plugins/geoservices/osm/qgeotiledmaposm.cpp b/src/plugins/geoservices/osm/qgeotiledmaposm.cpp index f16e602a..d94a40a6 100644 --- a/src/plugins/geoservices/osm/qgeotiledmaposm.cpp +++ b/src/plugins/geoservices/osm/qgeotiledmaposm.cpp @@ -71,10 +71,13 @@ void QGeoTiledMapOsm::evaluateCopyrights(const QSet<QGeoTileSpec> &visibleTiles) return; int providerId = tile.mapId() - 1; - if (providerId < 0 || providerId >= m_engine->providers().size() || !m_engine->providers().at(providerId)->isValid()) + if (providerId < 0 || providerId >= m_engine->providers().size()) return; m_mapId = tile.mapId(); + if (!m_engine->providers().at(providerId)->isValid()) + return; + onProviderDataUpdated(m_engine->providers().at(providerId)); } diff --git a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp index fba177f4..ff79c261 100644 --- a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp +++ b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp @@ -40,7 +40,7 @@ #include "qgeotiledmappingmanagerengineosm.h" #include "qgeotilefetcherosm.h" #include "qgeotiledmaposm.h" -#include "qgeotileproviderosm.h" +#include "qgeofiletilecacheosm.h" #include <QtLocation/private/qgeocameracapabilities_p.h> #include <QtLocation/private/qgeomaptype_p.h> @@ -62,77 +62,120 @@ QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVarian setTileSize(QSize(256, 256)); QNetworkAccessManager *nm = new QNetworkAccessManager(); - QString domain = QStringLiteral("http://maps-redirect.qt.io/osm/5.6/"); + QString domain = QStringLiteral("http://maps-redirect.qt.io/osm/5.8/"); if (parameters.contains(QStringLiteral("osm.mapping.providersrepository.address"))) { QString customAddress = parameters.value(QStringLiteral("osm.mapping.providersrepository.address")).toString(); - // Allowing some malformed addresses ( containing the suffix "/osm/5.6/" + // Allowing some malformed addresses if (customAddress.indexOf(QStringLiteral(":")) < 0) // defaulting to http:// if no prefix is found customAddress = QStringLiteral("http://") + customAddress; if (customAddress[customAddress.length()-1] != QLatin1Char('/')) customAddress += QLatin1Char('/'); - domain = customAddress; + if (QUrl(customAddress).isValid()) + domain = customAddress; + else + qWarning() << "Invalid custom providers repository address: " << customAddress; } - m_providers.push_back( - new QGeoTileProviderOsm(domain + "street", - nm, + bool highdpi = false; + if (parameters.contains(QStringLiteral("osm.mapping.highdpi_tiles"))) { + const QString param = parameters.value(QStringLiteral("osm.mapping.highdpi_tiles")).toString().toLower(); + if (param == "true") + highdpi = true; + } + + /* TileProviders setup */ + QVector<TileProvider *> providers_street; + QVector<TileProvider *> providers_satellite; + QVector<TileProvider *> providers_cycle; + QVector<TileProvider *> providers_transit; + QVector<TileProvider *> providers_nighttransit; + QVector<TileProvider *> providers_terrain; + QVector<TileProvider *> providers_hiking; + if (highdpi) { + providers_street.push_back(new TileProvider(domain + "street-hires", true)); + providers_satellite.push_back(new TileProvider(domain + "satellite-hires", true)); + providers_cycle.push_back(new TileProvider(domain + "cycle-hires", true)); + providers_transit.push_back(new TileProvider(domain + "transit-hires", true)); + providers_nighttransit.push_back(new TileProvider(domain + "night-transit-hires", true)); + providers_terrain.push_back(new TileProvider(domain + "terrain-hires", true)); + providers_hiking.push_back(new TileProvider(domain + "hiking-hires", true)); + } + providers_street.push_back(new TileProvider(domain + "street")); + providers_satellite.push_back(new TileProvider(domain + "satellite")); + providers_cycle.push_back(new TileProvider(domain + "cycle")); + providers_transit.push_back(new TileProvider(domain + "transit")); + providers_nighttransit.push_back(new TileProvider(domain + "night-transit")); + providers_terrain.push_back(new TileProvider(domain + "terrain")); + providers_hiking.push_back(new TileProvider(domain + "hiking")); + // Backups + const QDateTime defaultTs = QDateTime::fromString(QStringLiteral("2016-06-01T00:00:00"), Qt::ISODate); + providers_street.push_back( + new TileProvider(QStringLiteral("http://c.tile.openstreetmap.org/%z/%x/%y.png"), + QStringLiteral("png"), + QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap.org</a>"), + QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"))); + providers_street.back()->setTimestamp(defaultTs); + + // No available open access satellite backup with satisfactory level of details at the present. + + providers_cycle.push_back( + new TileProvider(QStringLiteral("http://c.tile.opencyclemap.org/cycle/%z/%x/%y.png"), + QStringLiteral("png"), + QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"), + QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"))); + providers_cycle.back()->setTimestamp(defaultTs); + + providers_transit.push_back( + new TileProvider(QStringLiteral("http://c.tile2.opencyclemap.org/transport/%z/%x/%y.png"), + QStringLiteral("png"), + QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"), + QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"))); + providers_transit.back()->setTimestamp(defaultTs); + + providers_nighttransit.push_back( + new TileProvider(QStringLiteral("http://a.tile.thunderforest.com/transport-dark/%z/%x/%y.png"), + QStringLiteral("png"), + QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"), + QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors")) ); + providers_nighttransit.back()->setTimestamp(defaultTs); + + providers_terrain.push_back( + new TileProvider(QStringLiteral("http://a.tile.thunderforest.com/landscape/%z/%x/%y.png"), + QStringLiteral("png"), + QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"), + QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"))); + providers_terrain.back()->setTimestamp(defaultTs); + + providers_hiking.push_back( + new TileProvider(QStringLiteral("http://a.tile.thunderforest.com/outdoors/%z/%x/%y.png"), + QStringLiteral("png"), + QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"), + QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"))); + providers_hiking.back()->setTimestamp(defaultTs); + + + /* QGeoTileProviderOsms setup */ + m_providers.push_back( new QGeoTileProviderOsm( nm, QGeoMapType(QGeoMapType::StreetMap, tr("Street Map"), tr("Street map view in daylight mode"), false, false, 1), - QGeoTileProviderOsm::TileProvider(QStringLiteral("http://c.tile.openstreetmap.org/%z/%x/%y.png"), - QStringLiteral("png"), - QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap.org</a>"), - QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors") - ))); - m_providers.push_back( - new QGeoTileProviderOsm(domain + "satellite", - nm, + providers_street )); + m_providers.push_back( new QGeoTileProviderOsm( nm, QGeoMapType(QGeoMapType::SatelliteMapDay, tr("Satellite Map"), tr("Satellite map view in daylight mode"), false, false, 2), - QGeoTileProviderOsm::TileProvider() - )); - m_providers.push_back( - new QGeoTileProviderOsm(domain + "cycle", - nm, + providers_satellite )); + m_providers.push_back( new QGeoTileProviderOsm( nm, QGeoMapType(QGeoMapType::CycleMap, tr("Cycle Map"), tr("Cycle map view in daylight mode"), false, false, 3), - QGeoTileProviderOsm::TileProvider(QStringLiteral("http://c.tile.opencyclemap.org/cycle/%z/%x/%y.png"), - QStringLiteral("png"), - QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"), - QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors") - ))); - m_providers.push_back( - new QGeoTileProviderOsm(domain + "transit", - nm, + providers_cycle )); + m_providers.push_back( new QGeoTileProviderOsm( nm, QGeoMapType(QGeoMapType::TransitMap, tr("Transit Map"), tr("Public transit map view in daylight mode"), false, false, 4), - QGeoTileProviderOsm::TileProvider(QStringLiteral("http://c.tile2.opencyclemap.org/transport/%z/%x/%y.png"), - QStringLiteral("png"), - QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"), - QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors") - ))); - m_providers.push_back( - new QGeoTileProviderOsm(domain + "night-transit", - nm, + providers_transit )); + m_providers.push_back( new QGeoTileProviderOsm( nm, QGeoMapType(QGeoMapType::TransitMap, tr("Night Transit Map"), tr("Public transit map view in night mode"), false, true, 5), - QGeoTileProviderOsm::TileProvider(QStringLiteral("http://a.tile.thunderforest.com/transport-dark/%z/%x/%y.png"), - QStringLiteral("png"), - QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"), - QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors") - ))); - m_providers.push_back( - new QGeoTileProviderOsm(domain + "terrain", - nm, + providers_nighttransit )); + m_providers.push_back( new QGeoTileProviderOsm( nm, QGeoMapType(QGeoMapType::TerrainMap, tr("Terrain Map"), tr("Terrain map view"), false, false, 6), - QGeoTileProviderOsm::TileProvider(QStringLiteral("http://a.tile.thunderforest.com/landscape/%z/%x/%y.png"), - QStringLiteral("png"), - QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"), - QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors") - ))); - m_providers.push_back( - new QGeoTileProviderOsm(domain + "hiking", - nm, + providers_terrain )); + m_providers.push_back( new QGeoTileProviderOsm( nm, QGeoMapType(QGeoMapType::PedestrianMap, tr("Hiking Map"), tr("Hiking map view"), false, false, 7), - QGeoTileProviderOsm::TileProvider(QStringLiteral("http://a.tile.thunderforest.com/outdoors/%z/%x/%y.png"), - QStringLiteral("png"), - QStringLiteral("<a href='http://www.thunderforest.com/'>Thunderforest</a>"), - QStringLiteral("<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors") - ))); + providers_hiking )); if (parameters.contains(QStringLiteral("osm.mapping.custom.host")) || parameters.contains(QStringLiteral("osm.mapping.host"))) { @@ -154,14 +197,13 @@ QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVarian m_customCopyright = parameters.value(QStringLiteral("osm.mapping.copyright")).toString(); m_providers.push_back( - new QGeoTileProviderOsm("", - nm, + new QGeoTileProviderOsm( nm, QGeoMapType(QGeoMapType::CustomMap, tr("Custom URL Map"), tr("Custom url map view set via urlprefix parameter"), false, false, 8), - QGeoTileProviderOsm::TileProvider(tmsServer + QStringLiteral("%z/%x/%y.png"), + { new TileProvider(tmsServer + QStringLiteral("%z/%x/%y.png"), QStringLiteral("png"), mapCopyright, - dataCopyright - ))); + dataCopyright) } + )); m_providers.last()->disableRedirection(); } @@ -182,20 +224,87 @@ QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVarian } updateMapTypes(); + + /* TILE CACHE */ + if (parameters.contains(QStringLiteral("osm.mapping.cache.directory"))) { + m_cacheDirectory = parameters.value(QStringLiteral("osm.mapping.cache.directory")).toString(); + } else { + // managerName() is not yet set, we have to hardcode the plugin name below + m_cacheDirectory = QAbstractGeoTileCache::baseLocationCacheDirectory() + QLatin1String("osm"); + } + if (parameters.contains(QStringLiteral("osm.mapping.offline.directory"))) + m_offlineDirectory = parameters.value(QStringLiteral("osm.mapping.offline.directory")).toString(); + QGeoFileTileCacheOsm *tileCache = new QGeoFileTileCacheOsm(m_providers, m_offlineDirectory, m_cacheDirectory); + + /* + * Disk cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("osm.mapping.cache.disk.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("osm.mapping.cache.disk.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyDisk(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("osm.mapping.cache.disk.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("osm.mapping.cache.disk.size")).toString().toInt(&ok); + if (ok) + tileCache->setMaxDiskUsage(cacheSize); + } + + /* + * Memory cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("osm.mapping.cache.memory.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("osm.mapping.cache.memory.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyMemory(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("osm.mapping.cache.memory.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("osm.mapping.cache.memory.size")).toString().toInt(&ok); + if (ok) + tileCache->setMaxMemoryUsage(cacheSize); + } + + /* + * Texture cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("osm.mapping.cache.texture.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("osm.mapping.cache.texture.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyTexture(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("osm.mapping.cache.texture.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("osm.mapping.cache.texture.size")).toString().toInt(&ok); + if (ok) + tileCache->setExtraTextureUsage(cacheSize); + } + + + setTileCache(tileCache); + + + /* TILE FETCHER */ QGeoTileFetcherOsm *tileFetcher = new QGeoTileFetcherOsm(m_providers, nm, this); if (parameters.contains(QStringLiteral("osm.useragent"))) { const QByteArray ua = parameters.value(QStringLiteral("osm.useragent")).toString().toLatin1(); tileFetcher->setUserAgent(ua); } - - setTileFetcher(tileFetcher); - QAbstractGeoTileCache *tileCache = new QGeoFileTileCache(QAbstractGeoTileCache::baseCacheDirectory() + QStringLiteral("osm")); - // 50mb of disk cache by default to minimize n. of accesses to public OSM servers - tileCache->setMaxDiskUsage(50 * 1024 * 1024); - setTileCache(tileCache); - *error = QGeoServiceProvider::NoError; errorString->clear(); } @@ -206,7 +315,10 @@ QGeoTiledMappingManagerEngineOsm::~QGeoTiledMappingManagerEngineOsm() QGeoMap *QGeoTiledMappingManagerEngineOsm::createMap() { - return new QGeoTiledMapOsm(this); + QGeoTiledMap *map = new QGeoTiledMapOsm(this); + connect(qobject_cast<QGeoFileTileCacheOsm *>(tileCache()), &QGeoFileTileCacheOsm::mapDataUpdated + , map, &QGeoTiledMap::clearScene); + return map; } const QVector<QGeoTileProviderOsm *> &QGeoTiledMappingManagerEngineOsm::providers() @@ -226,9 +338,8 @@ void QGeoTiledMappingManagerEngineOsm::onProviderResolutionFinished(const QGeoTi updateMapTypes(); } -void QGeoTiledMappingManagerEngineOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error) +void QGeoTiledMappingManagerEngineOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider) { - Q_UNUSED(error) if (!provider->isResolved()) return; updateMapTypes(); diff --git a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h index 247e4377..b1f0a13c 100644 --- a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h +++ b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h @@ -65,7 +65,7 @@ public: protected Q_SLOTS: void onProviderResolutionFinished(const QGeoTileProviderOsm *provider); - void onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error); + void onProviderResolutionError(const QGeoTileProviderOsm *provider); protected: void updateMapTypes(); @@ -73,6 +73,8 @@ protected: private: QVector<QGeoTileProviderOsm *> m_providers; QString m_customCopyright; + QString m_cacheDirectory; + QString m_offlineDirectory; }; QT_END_NAMESPACE diff --git a/src/plugins/geoservices/osm/qgeotilefetcherosm.cpp b/src/plugins/geoservices/osm/qgeotilefetcherosm.cpp index 98621411..f7c25d61 100644 --- a/src/plugins/geoservices/osm/qgeotilefetcherosm.cpp +++ b/src/plugins/geoservices/osm/qgeotilefetcherosm.cpp @@ -43,6 +43,7 @@ #include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkRequest> #include <QtLocation/private/qgeotilespec_p.h> +#include <QtLocation/private/qgeotilefetcher_p_p.h> QT_BEGIN_NAMESPACE @@ -55,10 +56,30 @@ static bool providersResolved(const QVector<QGeoTileProviderOsm *> &providers) return true; } +class QGeoTileFetcherOsmPrivate : public QGeoTileFetcherPrivate +{ + Q_DECLARE_PUBLIC(QGeoTileFetcherOsm) +public: + QGeoTileFetcherOsmPrivate(); + virtual ~QGeoTileFetcherOsmPrivate(); + +private: + Q_DISABLE_COPY(QGeoTileFetcherOsmPrivate) +}; + +QGeoTileFetcherOsmPrivate::QGeoTileFetcherOsmPrivate() : QGeoTileFetcherPrivate() +{ +} + +QGeoTileFetcherOsmPrivate::~QGeoTileFetcherOsmPrivate() +{ +} + + QGeoTileFetcherOsm::QGeoTileFetcherOsm(const QVector<QGeoTileProviderOsm *> &providers, QNetworkAccessManager *nm, QObject *parent) -: QGeoTileFetcher(parent), m_userAgent("Qt Location based application"), +: QGeoTileFetcher(*new QGeoTileFetcherOsmPrivate(), parent), m_userAgent("Qt Location based application"), m_providers(providers), m_nm(nm), m_ready(true) { m_nm->setParent(this); @@ -69,6 +90,8 @@ QGeoTileFetcherOsm::QGeoTileFetcherOsm(const QVector<QGeoTileProviderOsm *> &pro this, &QGeoTileFetcherOsm::onProviderResolutionFinished); connect(provider, &QGeoTileProviderOsm::resolutionError, this, &QGeoTileFetcherOsm::onProviderResolutionError); + connect(provider, &QGeoTileProviderOsm::resolutionRequired, + this, &QGeoTileFetcherOsm::restartTimer, Qt::QueuedConnection); provider->resolveProvider(); } } @@ -100,9 +123,8 @@ void QGeoTileFetcherOsm::onProviderResolutionFinished(const QGeoTileProviderOsm emit providerDataUpdated(provider); } -void QGeoTileFetcherOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error) +void QGeoTileFetcherOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider) { - Q_UNUSED(error) if ((m_ready = providersResolved(m_providers))) { qWarning("QGeoTileFetcherOsm: all providers resolved"); readyUpdated(); @@ -110,6 +132,14 @@ void QGeoTileFetcherOsm::onProviderResolutionError(const QGeoTileProviderOsm *pr emit providerDataUpdated(provider); } +void QGeoTileFetcherOsm::restartTimer() +{ + Q_D(QGeoTileFetcherOsm); + + if (!d->queue_.isEmpty()) + d->timer_.start(0, this); +} + QGeoTiledMapReply *QGeoTileFetcherOsm::getTileImage(const QGeoTileSpec &spec) { int id = spec.mapId(); @@ -122,6 +152,9 @@ QGeoTiledMapReply *QGeoTileFetcherOsm::getTileImage(const QGeoTileSpec &spec) } id -= 1; // TODO: make OSM map ids start from 0. + if (spec.zoom() > m_providers[id]->maximumZoomLevel() || spec.zoom() < m_providers[id]->minimumZoomLevel()) + return Q_NULLPTR; + const QUrl url = m_providers[id]->tileAddress(spec.x(), spec.y(), spec.zoom()); QNetworkRequest request; request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); diff --git a/src/plugins/geoservices/osm/qgeotilefetcherosm.h b/src/plugins/geoservices/osm/qgeotilefetcherosm.h index 8d69cc56..a7b89bad 100644 --- a/src/plugins/geoservices/osm/qgeotilefetcherosm.h +++ b/src/plugins/geoservices/osm/qgeotilefetcherosm.h @@ -47,10 +47,12 @@ QT_BEGIN_NAMESPACE class QNetworkAccessManager; +class QGeoTileFetcherOsmPrivate; class QGeoTileFetcherOsm : public QGeoTileFetcher { Q_OBJECT + Q_DECLARE_PRIVATE(QGeoTileFetcherOsm) friend class QGeoMapReplyOsm; friend class QGeoTiledMappingManagerEngineOsm; @@ -69,10 +71,11 @@ protected: protected Q_SLOTS: void onProviderResolutionFinished(const QGeoTileProviderOsm *provider); - void onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error); + void onProviderResolutionError(const QGeoTileProviderOsm *provider); + void restartTimer(); private: - QGeoTiledMapReply *getTileImage(const QGeoTileSpec &spec); + QGeoTiledMapReply *getTileImage(const QGeoTileSpec &spec) Q_DECL_OVERRIDE; void readyUpdated(); QByteArray m_userAgent; diff --git a/src/plugins/geoservices/osm/qgeotileproviderosm.cpp b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp index 3d46a425..1989c44f 100644 --- a/src/plugins/geoservices/osm/qgeotileproviderosm.cpp +++ b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp @@ -43,115 +43,89 @@ QT_BEGIN_NAMESPACE static const int maxValidZoom = 30; +static const QDateTime defaultTs = QDateTime::fromString(QStringLiteral("2016-06-01T00:00:00"), Qt::ISODate); -QGeoTileProviderOsm::QGeoTileProviderOsm(const QString &urlRedir, - QNetworkAccessManager *nm, - const QGeoMapType &mapType, - const QGeoTileProviderOsm::TileProvider &providerFallback) - : m_nm(nm), m_urlRedirector(urlRedir), - m_providerFallback(providerFallback), - m_mapType(mapType), m_status(Idle) +QGeoTileProviderOsm::QGeoTileProviderOsm(QNetworkAccessManager *nm, + const QGeoMapType &mapType, + const QVector<TileProvider *> &providers) +: m_nm(nm), m_provider(nullptr), m_mapType(mapType), m_status(Idle) { - if (!m_urlRedirector.isValid()) - disableRedirection(); + for (int i = 0; i < providers.size(); ++i) { + TileProvider *p = providers[i]; + if (!m_provider) + m_providerId = i; + addProvider(p); + } + + if (!m_provider || m_provider->isValid()) + m_status = Resolved; } QGeoTileProviderOsm::~QGeoTileProviderOsm() { - } -void QGeoTileProviderOsm::resolveProvider() +QUrl QGeoTileProviderOsm::tileAddress(int x, int y, int z) const { - switch (m_status) { - case Resolving: - case Invalid: - case Valid: - return; - case Idle: - m_status = Resolving; - break; - } + if (m_status != Resolved || !m_provider) + return QUrl(); + return m_provider->tileAddress(x, y, z); +} - QNetworkRequest request; - request.setHeader(QNetworkRequest::UserAgentHeader, QByteArrayLiteral("QGeoTileFetcherOsm")); - request.setUrl(m_urlRedirector); - QNetworkReply *reply = m_nm->get(request); - connect(reply, SIGNAL(finished()), this, SLOT(onNetworkReplyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), - this, SLOT(onNetworkReplyError(QNetworkReply::NetworkError))); +QString QGeoTileProviderOsm::mapCopyRight() const +{ + if (m_status != Resolved || !m_provider) + return QString(); + return m_provider->mapCopyRight(); } -void QGeoTileProviderOsm::disableRedirection() +QString QGeoTileProviderOsm::dataCopyRight() const { - m_status = Invalid; - m_provider.m_valid = false; + if (m_status != Resolved || !m_provider) + return QString(); + return m_provider->dataCopyRight(); } -void QGeoTileProviderOsm::handleError(QNetworkReply::NetworkError error) +QString QGeoTileProviderOsm::styleCopyRight() const { - switch (error) { - case QNetworkReply::ConnectionRefusedError: - case QNetworkReply::TooManyRedirectsError: - case QNetworkReply::InsecureRedirectError: - case QNetworkReply::ContentAccessDenied: - case QNetworkReply::ContentOperationNotPermittedError: - case QNetworkReply::ContentNotFoundError: - case QNetworkReply::AuthenticationRequiredError: - case QNetworkReply::ContentGoneError: - case QNetworkReply::OperationNotImplementedError: - case QNetworkReply::ServiceUnavailableError: - // Errors we don't expect to recover from in the near future, which - // prevent accessing the redirection info but not the actual providers. - m_status = Invalid; - default: - break; - } + if (m_status != Resolved || !m_provider) + return QString(); + return m_provider->styleCopyRight(); } -QUrl QGeoTileProviderOsm::tileAddress(int x, int y, int z) const +QString QGeoTileProviderOsm::format() const { - if (m_provider.isValid()) - return m_provider.tileAddress(x,y,z); - if (m_providerFallback.isValid()) - return m_providerFallback.tileAddress(x,y,z); - return QUrl(); + if (m_status != Resolved || !m_provider) + return QString(); + return m_provider->format(); } -QString QGeoTileProviderOsm::mapCopyRight() const +int QGeoTileProviderOsm::minimumZoomLevel() const { - if (m_provider.isValid()) - return m_provider.mapCopyRight(); - if (m_providerFallback.isValid()) - return m_providerFallback.mapCopyRight(); - return QString(); + if (m_status != Resolved || !m_provider) + return 0; + return m_provider->minimumZoomLevel(); } -QString QGeoTileProviderOsm::dataCopyRight() const +int QGeoTileProviderOsm::maximumZoomLevel() const { - if (m_provider.isValid()) - return m_provider.dataCopyRight(); - if (m_providerFallback.isValid()) - return m_providerFallback.dataCopyRight(); - return QString(); + if (m_status != Resolved || !m_provider) + return 20; + return m_provider->maximumZoomLevel(); } -QString QGeoTileProviderOsm::styleCopyRight() const +bool QGeoTileProviderOsm::isHighDpi() const { - if (m_provider.isValid()) - return m_provider.styleCopyRight(); - if (m_providerFallback.isValid()) - return m_providerFallback.styleCopyRight(); - return QString(); + if (!m_provider) + return false; + return m_provider->isHighDpi(); } -QString QGeoTileProviderOsm::format() const +const QDateTime QGeoTileProviderOsm::timestamp() const { - if (m_provider.isValid()) - return m_provider.format(); - if (m_providerFallback.isValid()) - return m_providerFallback.format(); - return QString(); + if (!m_provider) + return QDateTime(); + return m_provider->timestamp(); } const QGeoMapType &QGeoTileProviderOsm::mapType() const @@ -161,33 +135,218 @@ const QGeoMapType &QGeoTileProviderOsm::mapType() const bool QGeoTileProviderOsm::isValid() const { - return (m_provider.isValid() || m_providerFallback.isValid()); + if (m_status != Resolved || !m_provider) + return false; + return m_provider->isValid(); } bool QGeoTileProviderOsm::isResolved() const { - return (m_status == Valid || m_status == Invalid); + return (m_status == Resolved); +} + +void QGeoTileProviderOsm::resolveProvider() +{ + if (m_status == Resolved || m_status == Resolving) + return; + + m_status = Resolving; + // Provider can't be null while on Idle status. + connect(m_provider, &TileProvider::resolutionFinished, this, &QGeoTileProviderOsm::onResolutionFinished); + connect(m_provider, &TileProvider::resolutionError, this, &QGeoTileProviderOsm::onResolutionError); + m_provider->resolveProvider(); +} + +void QGeoTileProviderOsm::disableRedirection() +{ + if (m_provider && m_provider->isValid()) + return; + bool found = false; + for (TileProvider *p: m_providerList) { + if (p->isValid() && !found) { + m_provider = p; + found = true; + } + p->disconnect(this); + } +} + +void QGeoTileProviderOsm::onResolutionFinished(TileProvider *provider) +{ + Q_UNUSED(provider) + // provider and m_provider are the same, at this point. m_status is Resolving. + m_status = Resolved; + emit resolutionFinished(this); +} + +void QGeoTileProviderOsm::onResolutionError(TileProvider *provider) +{ + Q_UNUSED(provider) + // provider and m_provider are the same at this point. m_status is Resolving. + if (m_provider->isInvalid()) { + m_provider = nullptr; + m_status = Resolved; + if (m_providerId >= m_providerList.size() -1) { // no hope left + emit resolutionError(this); + return; + } + // Advance the pointer in the provider list, and possibly start resolution on the next in the list. + for (int i = m_providerId + 1; i < m_providerList.size(); ++i) { + m_providerId = i; + TileProvider *p = m_providerList[m_providerId]; + if (!p->isInvalid()) { + m_provider = p; + if (!p->isValid()) { + m_status = Idle; +#if 0 // leaving triggering the retry to the tile fetcher, instead of constantly spinning it in here. + m_status = Resolving; + p->resolveProvider(); +#endif + emit resolutionRequired(); + } + break; + } + } + if (!m_provider) + emit resolutionError(this); + } else if (m_provider->isValid()) { + m_status = Resolved; + emit resolutionFinished(this); + } else { // still not resolved. But network error is recoverable. + m_status = Idle; +#if 0 // leaving triggering the retry to the tile fetcher + m_provider->resolveProvider(); +#endif + } +} + +void QGeoTileProviderOsm::addProvider(TileProvider *provider) +{ + if (!provider) + return; + QScopedPointer<TileProvider> p(provider); + if (provider->status() == TileProvider::Invalid) + return; // if the provider is already resolved and invalid, no point in adding it. + + provider = p.take(); + provider->setNetworkManager(m_nm); + provider->setParent(this); + m_providerList.append(provider); + if (!m_provider) + m_provider = provider; +} + + +/* + Class TileProvder +*/ + +static void sort2(int &a, int &b) +{ + if (a > b) { + int temp=a; + a=b; + b=temp; + } +} + +TileProvider::TileProvider() : m_status(Invalid), m_nm(nullptr), m_timestamp(defaultTs), m_highDpi(false) +{ + +} + +TileProvider::TileProvider(const QUrl &urlRedirector, bool highDpi) +: m_status(Idle), m_urlRedirector(urlRedirector), m_nm(nullptr), m_timestamp(defaultTs), m_highDpi(highDpi) +{ + if (!m_urlRedirector.isValid()) + m_status = Invalid; +} + +TileProvider::TileProvider(const QString &urlTemplate, + const QString &format, + const QString ©RightMap, + const QString ©RightData, + bool highDpi, + int minimumZoomLevel, + int maximumZoomLevel) +: m_status(Invalid), m_nm(nullptr), m_urlTemplate(urlTemplate), + m_format(format), m_copyRightMap(copyRightMap), m_copyRightData(copyRightData), + m_minimumZoomLevel(minimumZoomLevel), m_maximumZoomLevel(maximumZoomLevel), m_timestamp(defaultTs), m_highDpi(highDpi) +{ + setupProvider(); +} + +TileProvider::~TileProvider() +{ +} + +void TileProvider::resolveProvider() +{ + if (!m_nm) + return; + + switch (m_status) { + case Resolving: + case Invalid: + case Valid: + return; + case Idle: + m_status = Resolving; + break; + } + + QNetworkRequest request; + request.setHeader(QNetworkRequest::UserAgentHeader, QByteArrayLiteral("QGeoTileFetcherOsm")); + request.setUrl(m_urlRedirector); + request.setAttribute(QNetworkRequest::BackgroundRequestAttribute, true); + QNetworkReply *reply = m_nm->get(request); + connect(reply, SIGNAL(finished()), this, SLOT(onNetworkReplyFinished()) ); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onNetworkReplyError(QNetworkReply::NetworkError))); +} + +void TileProvider::handleError(QNetworkReply::NetworkError error) +{ + switch (error) { + case QNetworkReply::ConnectionRefusedError: + case QNetworkReply::TooManyRedirectsError: + case QNetworkReply::InsecureRedirectError: + case QNetworkReply::ContentAccessDenied: + case QNetworkReply::ContentOperationNotPermittedError: + case QNetworkReply::ContentNotFoundError: + case QNetworkReply::AuthenticationRequiredError: + case QNetworkReply::ContentGoneError: + case QNetworkReply::OperationNotImplementedError: + case QNetworkReply::ServiceUnavailableError: + // Errors we don't expect to recover from in the near future, which + // prevent accessing the redirection info but not the actual providers. + m_status = Invalid; + default: + //qWarning() << "QGeoTileProviderOsm network error:" << error; + break; + } } -void QGeoTileProviderOsm::onNetworkReplyFinished() +void TileProvider::onNetworkReplyFinished() { QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); switch (m_status) { - case Resolving: - m_status = Idle; - case Idle: // should not happen - case Invalid: // should not happen - break; - case Valid: // should not happen - return; + case Resolving: + m_status = Idle; + case Idle: // should not happen + case Invalid: // should not happen + break; + case Valid: // should not happen + emit resolutionFinished(this); + return; } + QObject errorEmitter; + QMetaObject::Connection errorEmitterConnection = connect(&errorEmitter, &QObject::destroyed, [this](){ this->resolutionError(this); }); + if (reply->error() != QNetworkReply::NoError) { handleError(reply->error()); - if (m_status == Invalid) - emit resolutionError(this, reply->error()); return; } m_status = Invalid; @@ -205,48 +364,51 @@ void QGeoTileProviderOsm::onNetworkReplyFinished() * "StyleCopyRight" : "<copyright>", (optional) * "MinimumZoomLevel" : <minimumZoomLevel>, (optional) * "MaximumZoomLevel" : <maximumZoomLevel>, (optional) + * "Timestamp" : <timestamp>, (optional) * } * * Enabled is optional, and allows to temporarily disable a tile provider if it becomes * unavailable, without making the osm plugin fire requests to it. Default is true. * * MinimumZoomLevel and MaximumZoomLevel are also optional, and allow to prevent invalid tile - * requests to the providers, if they do not support the specific ZL. Default is 0 and 19, + * requests to the providers, if they do not support the specific ZL. Default is 0 and 20, * respectively. * - * <server address template> is required, and is the tile url template, with %x, %y and %z as + * UrlTemplate is required, and is the tile url template, with %x, %y and %z as * placeholders for the actual parameters. * Example: * http://localhost:8080/maps/%z/%x/%y.png * - * <image format> is required, and is the format of the tile. + * ImageFormat is required, and is the format of the tile. * Examples: * "png", "jpg" * - * <MapCopyRight> is required and is the string that will be displayed in the "Map (c)" part + * MapCopyRight is required and is the string that will be displayed in the "Map (c)" part * of the on-screen copyright notice. Can be an empty string. * Example: * "<a href='http://www.mapquest.com/'>MapQuest</a>" * - * <DataCopyRight> is required and is the string that will be displayed in the "Data (c)" part + * DataCopyRight is required and is the string that will be displayed in the "Data (c)" part * of the on-screen copyright notice. Can be an empty string. * Example: * "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors" * - * <StyleCopyRight> is optional and is the string that will be displayed in the optional "Style (c)" part + * StyleCopyRight is optional and is the string that will be displayed in the optional "Style (c)" part * of the on-screen copyright notice. + * + * Timestamp is optional, and if set will cause QtLocation to clear the content of the cache older + * than this timestamp. The purpose is to prevent mixing tiles from different providers in the cache + * upon provider change. The value must be a string in ISO 8601 format (see Qt::ISODate) */ QJsonParseError error; QJsonDocument d = QJsonDocument::fromJson(reply->readAll(), &error); if (error.error != QJsonParseError::NoError) { qWarning() << "QGeoTileProviderOsm: Error parsing redirection data: "<<error.errorString() << "at "<<m_urlRedirector; - emit resolutionFinished(this); return; } if (!d.isObject()) { qWarning() << "QGeoTileProviderOsm: Invalid redirection data" << "at "<<m_urlRedirector; - emit resolutionFinished(this); return; } const QJsonObject json = d.object(); @@ -263,56 +425,200 @@ void QGeoTileProviderOsm::onNetworkReplyFinished() || !copyRightMap.isString() || !copyRightData.isString()) { qWarning() << "QGeoTileProviderOsm: Incomplete redirection data" << "at "<<m_urlRedirector; - emit resolutionFinished(this); return; } + m_urlTemplate = urlTemplate.toString(); + m_format = imageFormat.toString(); + m_copyRightMap = copyRightMap.toString(); + m_copyRightData = copyRightData.toString(); + const QJsonValue enabled = json.value(QLatin1String("Enabled")); if (enabled.isBool() && ! enabled.toBool()) { qWarning() << "QGeoTileProviderOsm: Tileserver disabled" << "at "<<m_urlRedirector; - emit resolutionFinished(this); return; } - QString styleCopyRight; const QJsonValue copyRightStyle = json.value(QLatin1String("StyleCopyRight")); if (copyRightStyle != QJsonValue::Undefined && copyRightStyle.isString()) - styleCopyRight = copyRightStyle.toString(); + m_copyRightStyle = copyRightStyle.toString(); - int minZL = 0; - int maxZL = 19; + m_minimumZoomLevel = 0; + m_maximumZoomLevel = 20; const QJsonValue minZoom = json.value(QLatin1String("MinimumZoomLevel")); if (minZoom.isDouble()) - minZL = qBound(0, int(minZoom.toDouble()), maxValidZoom); + m_minimumZoomLevel = qBound(0, int(minZoom.toDouble()), maxValidZoom); const QJsonValue maxZoom = json.value(QLatin1String("MaximumZoomLevel")); if (maxZoom.isDouble()) - maxZL = qBound(0, int(maxZoom.toDouble()), maxValidZoom); + m_maximumZoomLevel = qBound(0, int(maxZoom.toDouble()), maxValidZoom); - m_provider = TileProvider(urlTemplate.toString(), - imageFormat.toString(), - copyRightMap.toString(), - copyRightData.toString(), - minZL, - maxZL); - m_provider.setStyleCopyRight(styleCopyRight); + const QJsonValue ts = json.value(QLatin1String("Timestamp")); + if (ts.isString()) + m_timestamp = QDateTime::fromString(ts.toString(), Qt::ISODate); - if (m_provider.isValid()) - m_status = Valid; - - emit resolutionFinished(this); + setupProvider(); + if (isValid()) { + QObject::disconnect(errorEmitterConnection); + emit resolutionFinished(this); + } } -void QGeoTileProviderOsm::onNetworkReplyError(QNetworkReply::NetworkError error) +void TileProvider::onNetworkReplyError(QNetworkReply::NetworkError error) { if (m_status == Resolving) m_status = Idle; - qWarning() << "QGeoTileProviderOsm::onNetworkReplyError " << error; handleError(error); - static_cast<QNetworkReply *>(sender())->deleteLater(); - if (m_status == Invalid) - emit resolutionError(this, error); + emit resolutionError(this); +} + +void TileProvider::setupProvider() +{ + if (m_urlTemplate.isEmpty()) + return; + + if (m_format.isEmpty()) + return; + + if (m_minimumZoomLevel < 0 || m_minimumZoomLevel > 30) + return; + + if (m_maximumZoomLevel < 0 || m_maximumZoomLevel > 30 || m_maximumZoomLevel < m_minimumZoomLevel) + return; + + // Currently supporting only %x, %y and &z + int offset[3]; + offset[0] = m_urlTemplate.indexOf(QLatin1String("%x")); + if (offset[0] < 0) + return; + + offset[1] = m_urlTemplate.indexOf(QLatin1String("%y")); + if (offset[1] < 0) + return; + + offset[2] = m_urlTemplate.indexOf(QLatin1String("%z")); + if (offset[2] < 0) + return; + + int sortedOffsets[3]; + std::copy(offset, offset + 3, sortedOffsets); + sort2(sortedOffsets[0] ,sortedOffsets[1]); + sort2(sortedOffsets[1] ,sortedOffsets[2]); + sort2(sortedOffsets[0] ,sortedOffsets[1]); + + int min = sortedOffsets[0]; + int max = sortedOffsets[2]; + int mid = sortedOffsets[1]; + + // Initing LUT + for (int i=0; i<3; i++) { + if (offset[0] == sortedOffsets[i]) + paramsLUT[i] = 0; + else if (offset[1] == sortedOffsets[i]) + paramsLUT[i] = 1; + else + paramsLUT[i] = 2; + } + + m_urlPrefix = m_urlTemplate.mid(0 , min); + m_urlSuffix = m_urlTemplate.mid(max + 2, m_urlTemplate.size() - max - 2); + + paramsSep[0] = m_urlTemplate.mid(min + 2, mid - min - 2); + paramsSep[1] = m_urlTemplate.mid(mid + 2, max - mid - 2); + m_status = Valid; +} + +bool TileProvider::isValid() const +{ + return m_status == Valid; +} + +bool TileProvider::isInvalid() const +{ + return m_status == Invalid; +} + +bool TileProvider::isResolved() const +{ + return (m_status == Valid || m_status == Invalid); +} + +QString TileProvider::mapCopyRight() const +{ + return m_copyRightMap; +} + +QString TileProvider::dataCopyRight() const +{ + return m_copyRightData; +} + +QString TileProvider::styleCopyRight() const +{ + return m_copyRightStyle; +} + +QString TileProvider::format() const +{ + return m_format; +} + +int TileProvider::minimumZoomLevel() const +{ + return m_minimumZoomLevel; +} + +int TileProvider::maximumZoomLevel() const +{ + return m_maximumZoomLevel; } +const QDateTime &TileProvider::timestamp() const +{ + return m_timestamp; +} + +bool TileProvider::isHighDpi() const +{ + return m_highDpi; +} + +void TileProvider::setStyleCopyRight(const QString ©right) +{ + m_copyRightStyle = copyright; +} + +void TileProvider::setTimestamp(const QDateTime ×tamp) +{ + m_timestamp = timestamp; +} + +QUrl TileProvider::tileAddress(int x, int y, int z) const +{ + if (z < m_minimumZoomLevel || z > m_maximumZoomLevel) + return QUrl(); + int params[3] = { x, y, z}; + QString url; + url += m_urlPrefix; + url += QString::number(params[paramsLUT[0]]); + url += paramsSep[0]; + url += QString::number(params[paramsLUT[1]]); + url += paramsSep[1]; + url += QString::number(params[paramsLUT[2]]); + url += m_urlSuffix; + return QUrl(url); +} + +void TileProvider::setNetworkManager(QNetworkAccessManager *nm) +{ + m_nm = nm; +} + +TileProvider::Status TileProvider::status() const +{ + return m_status; +} + + QT_END_NAMESPACE diff --git a/src/plugins/geoservices/osm/qgeotileproviderosm.h b/src/plugins/geoservices/osm/qgeotileproviderosm.h index f396b3b5..b8647244 100644 --- a/src/plugins/geoservices/osm/qgeotileproviderosm.h +++ b/src/plugins/geoservices/osm/qgeotileproviderosm.h @@ -40,215 +40,148 @@ #include <QtLocation/private/qgeomaptype_p.h> #include <QtCore/QUrl> +#include <QtCore/QVector> #include <QtNetwork/QNetworkAccessManager> #include <QtNetwork/QNetworkReply> #include <QtCore/QPointer> #include <QTimer> #include <algorithm> +#include <QtCore/QJsonDocument> +#include <QtCore/QJsonObject> +#include <QDateTime> QT_BEGIN_NAMESPACE -class QGeoTileProviderOsm: public QObject +class TileProvider: public QObject { Q_OBJECT - - friend class QGeoTileFetcherOsm; - friend class QGeoMapReplyOsm; - friend class QGeoTiledMappingManagerEngineOsm; public: - struct TileProvider { - - static inline void sort2(int &a, int &b) - { - if (a > b) { - int temp=a; - a=b; - b=temp; - } - } - - TileProvider() : m_valid(false) - { - - } - - TileProvider(const QString &urlTemplate, - const QString &format, - const QString ©RightMap, - const QString ©RightData, - int minimumZoomLevel = 0, - int maximumZoomLevel = 19) : m_valid(false) - { - if (urlTemplate.isEmpty()) - return; - m_urlTemplate = urlTemplate; - - if (format.isEmpty()) - return; - m_format = format; - - m_copyRightMap = copyRightMap; - m_copyRightData = copyRightData; - - if (minimumZoomLevel < 0 || minimumZoomLevel > 30) - return; - m_minimumZoomLevel = minimumZoomLevel; - - if (maximumZoomLevel < 0 || maximumZoomLevel > 30 || maximumZoomLevel < minimumZoomLevel) - return; - m_maximumZoomLevel = maximumZoomLevel; - - // Currently supporting only %x, %y and &z - int offset[3]; - offset[0] = m_urlTemplate.indexOf(QLatin1String("%x")); - if (offset[0] < 0) - return; - - offset[1] = m_urlTemplate.indexOf(QLatin1String("%y")); - if (offset[1] < 0) - return; - - offset[2] = m_urlTemplate.indexOf(QLatin1String("%z")); - if (offset[2] < 0) - return; - - int sortedOffsets[3]; - std::copy(offset, offset + 3, sortedOffsets); - sort2(sortedOffsets[0] ,sortedOffsets[1]); - sort2(sortedOffsets[1] ,sortedOffsets[2]); - sort2(sortedOffsets[0] ,sortedOffsets[1]); - - int min = sortedOffsets[0]; - int max = sortedOffsets[2]; - int mid = sortedOffsets[1]; - - // Initing LUT - for (int i=0; i<3; i++) { - if (offset[0] == sortedOffsets[i]) - paramsLUT[i] = 0; - else if (offset[1] == sortedOffsets[i]) - paramsLUT[i] = 1; - else - paramsLUT[i] = 2; - } - - m_urlPrefix = m_urlTemplate.mid(0 , min); - m_urlSuffix = m_urlTemplate.mid(max + 2, m_urlTemplate.size() - max - 2); - - paramsSep[0] = m_urlTemplate.mid(min + 2, mid - min - 2); - paramsSep[1] = m_urlTemplate.mid(mid + 2, max - mid - 2); - m_valid = true; - } - - ~TileProvider() - { - } - - inline bool isValid() const - { - return m_valid; - } + enum Status {Idle, + Resolving, + Valid, + Invalid }; - inline QString mapCopyRight() const - { - return m_copyRightMap; - } + TileProvider(); + // "Online" constructor. Needs resolution to fetch the parameters + TileProvider(const QUrl &urlRedirector, bool highDpi = false); + // Offline constructor. Doesn't need URLRedirector and networkmanager + TileProvider(const QString &urlTemplate, + const QString &format, + const QString ©RightMap, + const QString ©RightData, + bool highDpi = false, + int minimumZoomLevel = 0, + int maximumZoomLevel = 19); + + ~TileProvider(); + void setNetworkManager(QNetworkAccessManager *nm); - inline QString dataCopyRight() const - { - return m_copyRightData; - } + void resolveProvider(); + void handleError(QNetworkReply::NetworkError error); + void setupProvider(); + + inline bool isValid() const; + inline bool isInvalid() const; + inline bool isResolved() const; + inline Status status() const; + + inline QString mapCopyRight() const; + inline QString dataCopyRight() const; + inline QString styleCopyRight() const; + inline QString format() const; + inline int minimumZoomLevel() const; + inline int maximumZoomLevel() const; + inline const QDateTime ×tamp() const; + inline bool isHighDpi() const; + QUrl tileAddress(int x, int y, int z) const; - inline QString styleCopyRight() const - { - return m_copyRightStyle; - } + // Optional properties, not needed to construct a provider + void setStyleCopyRight(const QString ©right); + void setTimestamp(const QDateTime ×tamp); - inline QString format() const - { - return m_format; - } + Status m_status; + QUrl m_urlRedirector; // The URL from where to fetch the URL template in case of a provider to resolve. + QNetworkAccessManager *m_nm; + QString m_urlTemplate; + QString m_format; + QString m_copyRightMap; + QString m_copyRightData; + QString m_copyRightStyle; + QString m_urlPrefix; + QString m_urlSuffix; + int m_minimumZoomLevel; + int m_maximumZoomLevel; + QDateTime m_timestamp; + bool m_highDpi; + + int paramsLUT[3]; //Lookup table to handle possibly shuffled x,y,z + QString paramsSep[2]; // what goes in between %x, %y and %z - // Optional properties, not needed to construct a provider - void setStyleCopyRight(const QString ©right) - { - m_copyRightStyle = copyright; - } +Q_SIGNALS: + void resolutionFinished(TileProvider *provider); + void resolutionError(TileProvider *provider); - QUrl tileAddress(int x, int y, int z) const - { - if (z < m_minimumZoomLevel || z > m_maximumZoomLevel) - return QUrl(); - int params[3] = { x, y, z}; - QString url; - url += m_urlPrefix; - url += QString::number(params[paramsLUT[0]]); - url += paramsSep[0]; - url += QString::number(params[paramsLUT[1]]); - url += paramsSep[1]; - url += QString::number(params[paramsLUT[2]]); - url += m_urlSuffix; - return QUrl(url); - } +public Q_SLOTS: + void onNetworkReplyFinished(); + void onNetworkReplyError(QNetworkReply::NetworkError error); - bool m_valid; - QString m_urlTemplate; - QString m_format; - QString m_copyRightMap; - QString m_copyRightData; - QString m_copyRightStyle; - QString m_urlPrefix; - QString m_urlSuffix; - int m_minimumZoomLevel; - int m_maximumZoomLevel; +friend class QGeoTileProviderOsm; +}; - int paramsLUT[3]; //Lookup table to handle possibly shuffled x,y,z - QString paramsSep[2]; // what goes in between %x, %y and %z - }; +class QGeoTileProviderOsm: public QObject +{ + Q_OBJECT + friend class QGeoTileFetcherOsm; + friend class QGeoMapReplyOsm; + friend class QGeoTiledMappingManagerEngineOsm; +public: enum Status {Idle, Resolving, - Valid, - Invalid }; - - QGeoTileProviderOsm(const QString &urlRedir, - QNetworkAccessManager *nm, - const QGeoMapType &mapType, - const TileProvider &providerFallback); + Resolved }; + QGeoTileProviderOsm(QNetworkAccessManager *nm, + const QGeoMapType &mapType, + const QVector<TileProvider *> &providers); ~QGeoTileProviderOsm(); - - QUrl tileAddress(int x, int y, int z) const; QString mapCopyRight() const; QString dataCopyRight() const; QString styleCopyRight() const; QString format() const; + int minimumZoomLevel() const; + int maximumZoomLevel() const; + bool isHighDpi() const; const QGeoMapType &mapType() const; bool isValid() const; bool isResolved() const; + const QDateTime timestamp() const; Q_SIGNALS: void resolutionFinished(const QGeoTileProviderOsm *provider); - void resolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error); + void resolutionError(const QGeoTileProviderOsm *provider); + void resolutionRequired(); public Q_SLOTS: - void onNetworkReplyFinished(); - void onNetworkReplyError(QNetworkReply::NetworkError error); void resolveProvider(); + void disableRedirection(); + +protected Q_SLOTS: + void onResolutionFinished(TileProvider *provider); + void onResolutionError(TileProvider *provider); protected: - void disableRedirection(); - void handleError(QNetworkReply::NetworkError error); + void addProvider(TileProvider *provider); + +/* Data members */ QNetworkAccessManager *m_nm; - QUrl m_urlRedirector; // The URL from where to fetch the URL template - TileProvider m_provider; - TileProvider m_providerFallback; + QVector<TileProvider *> m_providerList; + TileProvider *m_provider; + int m_providerId; QGeoMapType m_mapType; Status m_status; - QTimer m_retryTimer; }; QT_END_NAMESPACE diff --git a/src/plugins/position/android/src/jnipositioning.cpp b/src/plugins/position/android/src/jnipositioning.cpp index e0124eb6..44d2fda4 100644 --- a/src/plugins/position/android/src/jnipositioning.cpp +++ b/src/plugins/position/android/src/jnipositioning.cpp @@ -41,6 +41,7 @@ #include <QDebug> #include <QMap> #include <QtGlobal> +#include <QtCore/private/qjnihelpers_p.h> #include <android/log.h> #include <jni.h> #include <QGeoPositionInfo> @@ -99,7 +100,7 @@ namespace AndroidPositioning { { static bool firstInit = true; if (firstInit) { - qsrand( QDateTime::currentDateTime().toTime_t() ); + qsrand( QDateTime::currentMSecsSinceEpoch() / 1000 ); firstInit = false; } @@ -367,6 +368,20 @@ namespace AndroidPositioning { QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey); if (source) { + // Android v23+ requires runtime permission check and requests + QString permission(QLatin1String("android.permission.ACCESS_FINE_LOCATION")); + + if (QtAndroidPrivate::checkPermission(permission) == QtAndroidPrivate::PermissionsResult::Denied) { + const QHash<QString, QtAndroidPrivate::PermissionsResult> results = + QtAndroidPrivate::requestPermissionsSync(env.jniEnv, QStringList() << permission); + if (!results.contains(permission) + || results[permission] == QtAndroidPrivate::PermissionsResult::Denied) + { + qWarning() << "Position retrieval not possible due to missing permission (ACCESS_FINE_LOCATION)"; + return QGeoPositionInfoSource::AccessError; + } + } + int errorCode = env.jniEnv->CallStaticIntMethod(positioningClass, startUpdatesMethodId, androidClassKey, positioningMethodToInt(source->preferredPositioningMethods()), @@ -404,6 +419,20 @@ namespace AndroidPositioning { QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey); if (source) { + // Android v23+ requires runtime permission check and requests + QString permission(QLatin1String("android.permission.ACCESS_FINE_LOCATION")); + + if (QtAndroidPrivate::checkPermission(permission) == QtAndroidPrivate::PermissionsResult::Denied) { + const QHash<QString, QtAndroidPrivate::PermissionsResult> results = + QtAndroidPrivate::requestPermissionsSync(env.jniEnv, QStringList() << permission); + if (!results.contains(permission) + || results[permission] == QtAndroidPrivate::PermissionsResult::Denied) + { + qWarning() << "Position update not possible due to missing permission (ACCESS_FINE_LOCATION)"; + return QGeoPositionInfoSource::AccessError; + } + } + int errorCode = env.jniEnv->CallStaticIntMethod(positioningClass, requestUpdateMethodId, androidClassKey, positioningMethodToInt(source->preferredPositioningMethods())); diff --git a/src/plugins/position/android/src/src.pro b/src/plugins/position/android/src/src.pro index 3a19c85e..36facc55 100644 --- a/src/plugins/position/android/src/src.pro +++ b/src/plugins/position/android/src/src.pro @@ -1,6 +1,6 @@ TARGET = qtposition_android -QT = core positioning +QT = core core-private positioning HEADERS = \ positionfactory_android.h \ diff --git a/src/plugins/position/geoclue/org.freedesktop.Geoclue.Position.xml b/src/plugins/position/geoclue/org.freedesktop.Geoclue.Position.xml index ce5c80de..8f7f70d4 100644 --- a/src/plugins/position/geoclue/org.freedesktop.Geoclue.Position.xml +++ b/src/plugins/position/geoclue/org.freedesktop.Geoclue.Position.xml @@ -18,7 +18,7 @@ <arg name="longitude" type="d"/> <arg name="altitude" type="d"/> <arg name="accuracy" type="(idd)"/> - <annotation name="org.qtproject.QtDBus.QtTypeName.In5" value="Accuracy"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out5" value="Accuracy"/> </signal> </interface> </node> diff --git a/src/plugins/position/geoclue/org.freedesktop.Geoclue.Satellite.xml b/src/plugins/position/geoclue/org.freedesktop.Geoclue.Satellite.xml index 2ed112c9..b892e26f 100644 --- a/src/plugins/position/geoclue/org.freedesktop.Geoclue.Satellite.xml +++ b/src/plugins/position/geoclue/org.freedesktop.Geoclue.Satellite.xml @@ -26,8 +26,8 @@ <arg name="satelliteVisible" type="i" /> <arg name="usedPrn" type="ai" /> <arg name="satInfo" type="a(iiii)" />' - <annotation name="org.qtproject.QtDBus.QtTypeName.In3" value="QList<qint32>"/> - <annotation name="org.qtproject.QtDBus.QtTypeName.In4" value="QList<QGeoSatelliteInfo>"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out3" value="QList<qint32>"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out4" value="QList<QGeoSatelliteInfo>"/> </signal> </interface> </node> diff --git a/src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.h b/src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.h index f2d817b1..b9c4774a 100644 --- a/src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.h +++ b/src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.h @@ -65,17 +65,17 @@ public: ~QGeoPositionInfoSourceGeoclueMaster(); // From QGeoPositionInfoSource - void setUpdateInterval(int msec); - QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const; - PositioningMethods supportedPositioningMethods() const; - void setPreferredPositioningMethods(PositioningMethods methods); - int minimumUpdateInterval() const; + void setUpdateInterval(int msec) override; + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const override; + PositioningMethods supportedPositioningMethods() const override; + void setPreferredPositioningMethods(PositioningMethods methods) override; + int minimumUpdateInterval() const override; - Error error() const; + Error error() const override; - virtual void startUpdates() Q_DECL_OVERRIDE; - virtual void stopUpdates() Q_DECL_OVERRIDE; - virtual void requestUpdate(int timeout = 5000) Q_DECL_OVERRIDE; + void startUpdates() override; + void stopUpdates() override; + void requestUpdate(int timeout = 5000) override; private slots: void positionProviderChanged(const QString &name, const QString &description, diff --git a/src/plugins/position/gypsy/gypsy.pro b/src/plugins/position/gypsy/gypsy.pro index be65f183..c7b74325 100644 --- a/src/plugins/position/gypsy/gypsy.pro +++ b/src/plugins/position/gypsy/gypsy.pro @@ -1,6 +1,6 @@ TARGET = qtposition_gypsy -QT = core positioning +QT = core positioning-private HEADERS += \ qgeosatelliteinfosource_gypsy_p.h \ @@ -10,8 +10,7 @@ SOURCES += \ qgeosatelliteinfosource_gypsy.cpp \ qgeopositioninfosourcefactory_gypsy.cpp -CONFIG += link_pkgconfig -PKGCONFIG += gypsy gconf-2.0 +QMAKE_USE_PRIVATE += gypsy OTHER_FILES += \ plugin.json diff --git a/src/plugins/position/position.pro b/src/plugins/position/position.pro index df1930b3..1687a9d2 100644 --- a/src/plugins/position/position.pro +++ b/src/plugins/position/position.pro @@ -1,11 +1,13 @@ TEMPLATE = subdirs +QT_FOR_CONFIG += positioning-private + qtHaveModule(dbus):SUBDIRS += geoclue -config_gypsy:SUBDIRS += gypsy +qtConfig(gypsy):SUBDIRS += gypsy +qtConfig(winrt_geolocation):SUBDIRS += winrt qtHaveModule(simulator):SUBDIRS += simulator osx|ios|tvos:SUBDIRS += corelocation android:SUBDIRS += android -winrt:SUBDIRS += winrt win32:qtHaveModule(serialport):SUBDIRS += serialnmea SUBDIRS += \ diff --git a/src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp b/src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp index 33523493..a8e5e201 100644 --- a/src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp +++ b/src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp @@ -62,10 +62,10 @@ typedef IAsyncOperationCompletedHandler<Geoposition*> PositionHandler; typedef IAsyncOperationCompletedHandler<GeolocationAccessStatus> AccessHandler; #endif -QT_BEGIN_NAMESPACE - Q_DECLARE_METATYPE(QGeoPositionInfo) +QT_BEGIN_NAMESPACE + #ifndef Q_OS_WINRT namespace QEventDispatcherWinRT { HRESULT runOnXamlThread(const std::function<HRESULT ()> &delegate, bool waitForRun = true) @@ -486,6 +486,18 @@ HRESULT QGeoPositionInfoSourceWinRT::onPositionChanged(IGeolocator *locator, IPo currentInfo.setAttribute(QGeoPositionInfo::GroundSpeed, value); } + IReference<double> *heading; + hr = coord->get_Heading(&heading); + if (SUCCEEDED(hr) && heading) { + double value; + hr = heading->get_Value(&value); + double mod = 0; + value = modf(value, &mod); + value += static_cast<int>(mod) % 360; + if (value >=0 && value <= 359) // get_Value might return nan/-nan + currentInfo.setAttribute(QGeoPositionInfo::Direction, value); + } + DateTime dateTime; hr = coord->get_Timestamp(&dateTime); |