diff options
Diffstat (limited to 'src/plugins')
67 files changed, 2601 insertions, 354 deletions
diff --git a/src/plugins/geoservices/esri/esri.pro b/src/plugins/geoservices/esri/esri.pro index 3a4da208..fce3947f 100644 --- a/src/plugins/geoservices/esri/esri.pro +++ b/src/plugins/geoservices/esri/esri.pro @@ -16,7 +16,10 @@ HEADERS += \ geotiledmap_esri.h \ geotiledmappingmanagerengine_esri.h \ geotiledmapreply_esri.h \ - geotilefetcher_esri.h + geotilefetcher_esri.h \ + placemanagerengine_esri.h \ + placesearchreply_esri.h \ + placecategoriesreply_esri.h SOURCES += \ geocodereply_esri.cpp \ @@ -29,7 +32,10 @@ SOURCES += \ geotiledmap_esri.cpp \ geotiledmappingmanagerengine_esri.cpp \ geotiledmapreply_esri.cpp \ - geotilefetcher_esri.cpp + geotilefetcher_esri.cpp \ + placemanagerengine_esri.cpp \ + placesearchreply_esri.cpp \ + placecategoriesreply_esri.cpp RESOURCES += \ esri.qrc diff --git a/src/plugins/geoservices/esri/esri_plugin.json b/src/plugins/geoservices/esri/esri_plugin.json index 3398648e..c1e37614 100644 --- a/src/plugins/geoservices/esri/esri_plugin.json +++ b/src/plugins/geoservices/esri/esri_plugin.json @@ -7,7 +7,8 @@ "OnlineMappingFeature", "OnlineGeocodingFeature", "ReverseGeocodingFeature", - "OnlineRoutingFeature" + "OnlineRoutingFeature", + "OnlinePlacesFeature" ], "Priority": 1000 } diff --git a/src/plugins/geoservices/esri/geocodereply_esri.cpp b/src/plugins/geoservices/esri/geocodereply_esri.cpp index f1dac184..fd1071c9 100644 --- a/src/plugins/geoservices/esri/geocodereply_esri.cpp +++ b/src/plugins/geoservices/esri/geocodereply_esri.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/geocodereply_esri.h b/src/plugins/geoservices/esri/geocodereply_esri.h index 76b416ce..4f216193 100644 --- a/src/plugins/geoservices/esri/geocodereply_esri.h +++ b/src/plugins/geoservices/esri/geocodereply_esri.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp b/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp index 976c51cf..17ed3cd2 100644 --- a/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp +++ b/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/geocodingmanagerengine_esri.h b/src/plugins/geoservices/esri/geocodingmanagerengine_esri.h index ff7bf882..a18f6bdd 100644 --- a/src/plugins/geoservices/esri/geocodingmanagerengine_esri.h +++ b/src/plugins/geoservices/esri/geocodingmanagerengine_esri.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/geomapsource.cpp b/src/plugins/geoservices/esri/geomapsource.cpp index 7ec63e2f..7a7d264f 100644 --- a/src/plugins/geoservices/esri/geomapsource.cpp +++ b/src/plugins/geoservices/esri/geomapsource.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/geomapsource.h b/src/plugins/geoservices/esri/geomapsource.h index 86258d2e..24bef6f7 100644 --- a/src/plugins/geoservices/esri/geomapsource.h +++ b/src/plugins/geoservices/esri/geomapsource.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp b/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp index 30db48f0..17492d94 100644 --- a/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp +++ b/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/georoutejsonparser_esri.h b/src/plugins/geoservices/esri/georoutejsonparser_esri.h index 0511cf4d..d6451d70 100644 --- a/src/plugins/geoservices/esri/georoutejsonparser_esri.h +++ b/src/plugins/geoservices/esri/georoutejsonparser_esri.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/georoutereply_esri.cpp b/src/plugins/geoservices/esri/georoutereply_esri.cpp index 811ffd0d..95de6b55 100644 --- a/src/plugins/geoservices/esri/georoutereply_esri.cpp +++ b/src/plugins/geoservices/esri/georoutereply_esri.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/georoutereply_esri.h b/src/plugins/geoservices/esri/georoutereply_esri.h index 960c90de..19cb85bd 100644 --- a/src/plugins/geoservices/esri/georoutereply_esri.h +++ b/src/plugins/geoservices/esri/georoutereply_esri.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp b/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp index ae722e59..0e6bc2c7 100644 --- a/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp +++ b/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/georoutingmanagerengine_esri.h b/src/plugins/geoservices/esri/georoutingmanagerengine_esri.h index 17aaa3ab..eac32222 100644 --- a/src/plugins/geoservices/esri/georoutingmanagerengine_esri.h +++ b/src/plugins/geoservices/esri/georoutingmanagerengine_esri.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.cpp b/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.cpp index 0a54e008..197b16cd 100644 --- a/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.cpp +++ b/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. @@ -41,6 +41,7 @@ #include "geotiledmappingmanagerengine_esri.h" #include "geocodingmanagerengine_esri.h" #include "georoutingmanagerengine_esri.h" +#include "placemanagerengine_esri.h" #include <QtLocation/private/qgeotiledmappingmanagerengine_p.h> @@ -76,11 +77,7 @@ QGeoRoutingManagerEngine *GeoServiceProviderFactoryEsri::createRoutingManagerEng QPlaceManagerEngine *GeoServiceProviderFactoryEsri::createPlaceManagerEngine( const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const { - Q_UNUSED(parameters) - Q_UNUSED(error) - Q_UNUSED(errorString) - - return nullptr; + return new PlaceManagerEngineEsri(parameters, error, errorString); } QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.h b/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.h index abd0d59b..6208348a 100644 --- a/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.h +++ b/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/geotiledmap_esri.cpp b/src/plugins/geoservices/esri/geotiledmap_esri.cpp index 8feb9615..020001c4 100644 --- a/src/plugins/geoservices/esri/geotiledmap_esri.cpp +++ b/src/plugins/geoservices/esri/geotiledmap_esri.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/geotiledmap_esri.h b/src/plugins/geoservices/esri/geotiledmap_esri.h index 7a21af9a..0f62d961 100644 --- a/src/plugins/geoservices/esri/geotiledmap_esri.h +++ b/src/plugins/geoservices/esri/geotiledmap_esri.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp b/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp index 3fa9a177..5d15835d 100644 --- a/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp +++ b/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. @@ -53,11 +53,6 @@ #include <QJsonDocument> #include <QJsonObject> -static void initResources() -{ - Q_INIT_RESOURCE(esri); -} - QT_BEGIN_NAMESPACE static const QString kPrefixEsri(QStringLiteral("esri.")); @@ -241,7 +236,6 @@ bool GeoTiledMappingManagerEngineEsri::initializeMapSources(QGeoServiceProvider: QString *errorString, const QGeoCameraCapabilities &cameraCaps) { - initResources(); QFile mapsFile(":/esri/maps.json"); if (!mapsFile.open(QIODevice::ReadOnly)) { diff --git a/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.h b/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.h index 63172389..222b1779 100644 --- a/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.h +++ b/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/geotiledmapreply_esri.cpp b/src/plugins/geoservices/esri/geotiledmapreply_esri.cpp index f4431bf0..78f6c69a 100644 --- a/src/plugins/geoservices/esri/geotiledmapreply_esri.cpp +++ b/src/plugins/geoservices/esri/geotiledmapreply_esri.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/geotiledmapreply_esri.h b/src/plugins/geoservices/esri/geotiledmapreply_esri.h index 572431dd..9e649d4f 100644 --- a/src/plugins/geoservices/esri/geotiledmapreply_esri.h +++ b/src/plugins/geoservices/esri/geotiledmapreply_esri.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/geotilefetcher_esri.cpp b/src/plugins/geoservices/esri/geotilefetcher_esri.cpp index 8ceba374..00344cf7 100644 --- a/src/plugins/geoservices/esri/geotilefetcher_esri.cpp +++ b/src/plugins/geoservices/esri/geotilefetcher_esri.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/geotilefetcher_esri.h b/src/plugins/geoservices/esri/geotilefetcher_esri.h index 5702d1c4..8c109f73 100644 --- a/src/plugins/geoservices/esri/geotilefetcher_esri.h +++ b/src/plugins/geoservices/esri/geotilefetcher_esri.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013-2016 Esri <contracts@esri.com> +** Copyright (C) 2013-2018 Esri <contracts@esri.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtLocation module of the Qt Toolkit. diff --git a/src/plugins/geoservices/esri/placecategoriesreply_esri.cpp b/src/plugins/geoservices/esri/placecategoriesreply_esri.cpp new file mode 100644 index 00000000..44c27b19 --- /dev/null +++ b/src/plugins/geoservices/esri/placecategoriesreply_esri.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2018 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 "placecategoriesreply_esri.h" + +QT_BEGIN_NAMESPACE + +PlaceCategoriesReplyEsri::PlaceCategoriesReplyEsri(QObject *parent) : + QPlaceReply(parent) +{ +} + +PlaceCategoriesReplyEsri::~PlaceCategoriesReplyEsri() +{ +} + +void PlaceCategoriesReplyEsri::emitFinished() +{ + setFinished(true); + emit finished(); +} + +void PlaceCategoriesReplyEsri::setError(QPlaceReply::Error errorCode, const QString &errorString) +{ + QPlaceReply::setError(errorCode, errorString); + emit error(errorCode, errorString); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/placecategoriesreply_esri.h b/src/plugins/geoservices/esri/placecategoriesreply_esri.h new file mode 100644 index 00000000..14efcfea --- /dev/null +++ b/src/plugins/geoservices/esri/placecategoriesreply_esri.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2018 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 PLACECATEGORIESREPLYESRI_H +#define PLACECATEGORIESREPLYESRI_H + +#include <QtLocation/QPlaceReply> + +QT_BEGIN_NAMESPACE + +class PlaceCategoriesReplyEsri : public QPlaceReply +{ + Q_OBJECT + +public: + explicit PlaceCategoriesReplyEsri(QObject *parent = 0); + ~PlaceCategoriesReplyEsri(); + + void emitFinished(); + void setError(QPlaceReply::Error errorCode, const QString &errorString); +}; + +QT_END_NAMESPACE + +#endif // PLACECATEGORIESREPLYESRI_H diff --git a/src/plugins/geoservices/esri/placemanagerengine_esri.cpp b/src/plugins/geoservices/esri/placemanagerengine_esri.cpp new file mode 100644 index 00000000..8f973ff3 --- /dev/null +++ b/src/plugins/geoservices/esri/placemanagerengine_esri.cpp @@ -0,0 +1,369 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2018 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 "placemanagerengine_esri.h" +#include "placesearchreply_esri.h" +#include "placecategoriesreply_esri.h" + +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> + +#include <QtCore/QUrlQuery> + +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-category-filtering.htm +// https://developers.arcgis.com/rest/geocode/api-reference/geocoding-service-output.htm + +static const QString kCategoriesKey(QStringLiteral("categories")); +static const QString kSingleLineKey(QStringLiteral("singleLine")); +static const QString kLocationKey(QStringLiteral("location")); +static const QString kNameKey(QStringLiteral("name")); +static const QString kOutFieldsKey(QStringLiteral("outFields")); +static const QString kCandidateFieldsKey(QStringLiteral("candidateFields")); +static const QString kCountriesKey(QStringLiteral("detailedCountries")); +static const QString kLocalizedNamesKey(QStringLiteral("localizedNames")); +static const QString kMaxLocationsKey(QStringLiteral("maxLocations")); + +static const QUrl kUrlGeocodeServer("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer?f=pjson"); +static const QUrl kUrlFindAddressCandidates("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates"); + +PlaceManagerEngineEsri::PlaceManagerEngineEsri(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, + QString *errorString) : + QPlaceManagerEngine(parameters), + m_networkManager(new QNetworkAccessManager(this)) +{ + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +PlaceManagerEngineEsri::~PlaceManagerEngineEsri() +{ +} + +QList<QLocale> PlaceManagerEngineEsri::locales() const +{ + return m_locales; +} + +void PlaceManagerEngineEsri::setLocales(const QList<QLocale> &locales) +{ + m_locales = locales; +} + +/***** Search *****/ + +QPlaceSearchReply *PlaceManagerEngineEsri::search(const QPlaceSearchRequest &request) +{ + bool unsupported = false; + + // Only public visibility supported + unsupported |= request.visibilityScope() != QLocation::UnspecifiedVisibility && + request.visibilityScope() != QLocation::PublicVisibility; + unsupported |= request.searchTerm().isEmpty() && request.categories().isEmpty(); + + if (unsupported) + return QPlaceManagerEngine::search(request); + + QUrlQuery queryItems; + queryItems.addQueryItem(QStringLiteral("f"), QStringLiteral("json")); + + const QGeoCoordinate center = request.searchArea().center(); + if (center.isValid()) + { + const QString location = QString("%1,%2").arg(center.longitude()).arg(center.latitude()); + queryItems.addQueryItem(kLocationKey, location); + } + + const QGeoRectangle boundingBox = request.searchArea().boundingGeoRectangle(); + if (!boundingBox.isEmpty()) + { + const QString searchExtent = QString("%1,%2,%3,%4") + .arg(boundingBox.topLeft().longitude()) + .arg(boundingBox.topLeft().latitude()) + .arg(boundingBox.bottomRight().longitude()) + .arg(boundingBox.bottomRight().latitude()); + queryItems.addQueryItem(QStringLiteral("searchExtent"), searchExtent); + } + + if (!request.searchTerm().isEmpty()) + queryItems.addQueryItem(kSingleLineKey, request.searchTerm()); + + QStringList categories; + if (!request.categories().isEmpty()) + { + foreach (const QPlaceCategory &placeCategory, request.categories()) + categories.append(placeCategory.categoryId()); + queryItems.addQueryItem("category", categories.join(",")); + } + + if (request.limit() > 0) + queryItems.addQueryItem(kMaxLocationsKey, QString::number(request.limit())); + + queryItems.addQueryItem(kOutFieldsKey, QStringLiteral("*")); + + QUrl requestUrl(kUrlFindAddressCandidates); + requestUrl.setQuery(queryItems); + + QNetworkRequest networkRequest(requestUrl); + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + QNetworkReply *networkReply = m_networkManager->get(networkRequest); + + PlaceSearchReplyEsri *reply = new PlaceSearchReplyEsri(request, networkReply, m_candidateFieldsLocale, m_countriesLocale, this); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), this, SLOT(replyError(QPlaceReply::Error,QString))); + + return reply; +} + +void PlaceManagerEngineEsri::replyFinished() +{ + QPlaceReply *reply = qobject_cast<QPlaceReply *>(sender()); + if (reply) + emit finished(reply); +} + +void PlaceManagerEngineEsri::replyError(QPlaceReply::Error errorCode, const QString &errorString) +{ + QPlaceReply *reply = qobject_cast<QPlaceReply *>(sender()); + if (reply) + emit error(reply, errorCode, errorString); +} + +/***** Categories *****/ + +QPlaceReply *PlaceManagerEngineEsri::initializeCategories() +{ + initializeGeocodeServer(); + + PlaceCategoriesReplyEsri *reply = new PlaceCategoriesReplyEsri(this); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), this, SLOT(replyError(QPlaceReply::Error,QString))); + + // TODO delayed finished() emission + if (!m_categories.isEmpty()) + reply->emitFinished(); + + m_pendingCategoriesReply.append(reply); + return reply; +} + +void PlaceManagerEngineEsri::parseCategories(const QJsonArray &jsonArray, const QString &parentCategoryId) +{ + foreach (const QJsonValue &jsonValue, jsonArray) + { + if (!jsonValue.isObject()) + continue; + + const QJsonObject jsonCategory = jsonValue.toObject(); + const QString key = jsonCategory.value(kNameKey).toString(); + const QString localeName = localizedName(jsonCategory); + + if (key.isEmpty()) + continue; + + QPlaceCategory category; + category.setCategoryId(key); + category.setName(localeName.isEmpty() ? key : localeName); // localizedNames + m_categories.insert(key, category); + m_subcategories[parentCategoryId].append(key); + m_parentCategory.insert(key, parentCategoryId); + emit categoryAdded(category, parentCategoryId); + + if (jsonCategory.contains(kCategoriesKey)) + { + const QJsonArray jsonArray = jsonCategory.value(kCategoriesKey).toArray(); + parseCategories(jsonArray, key); + } + } +} + +QString PlaceManagerEngineEsri::parentCategoryId(const QString &categoryId) const +{ + return m_parentCategory.value(categoryId); +} + +QStringList PlaceManagerEngineEsri::childCategoryIds(const QString &categoryId) const +{ + return m_subcategories.value(categoryId); +} + +QPlaceCategory PlaceManagerEngineEsri::category(const QString &categoryId) const +{ + return m_categories.value(categoryId); +} + +QList<QPlaceCategory> PlaceManagerEngineEsri::childCategories(const QString &parentId) const +{ + QList<QPlaceCategory> categories; + foreach (const QString &id, m_subcategories.value(parentId)) + categories.append(m_categories.value(id)); + return categories; +} + +void PlaceManagerEngineEsri::finishCategories() +{ + foreach (PlaceCategoriesReplyEsri *reply, m_pendingCategoriesReply) + reply->emitFinished(); + m_pendingCategoriesReply.clear(); +} + +void PlaceManagerEngineEsri::errorCaterogies(const QString &error) +{ + foreach (PlaceCategoriesReplyEsri *reply, m_pendingCategoriesReply) + reply->setError(QPlaceReply::CommunicationError, error); +} + +/***** GeocodeServer *****/ + +void PlaceManagerEngineEsri::initializeGeocodeServer() +{ + // Only fetch categories once + if (m_categories.isEmpty() && !m_geocodeServerReply) + { + m_geocodeServerReply = m_networkManager->get(QNetworkRequest(kUrlGeocodeServer)); + connect(m_geocodeServerReply, SIGNAL(finished()), this, SLOT(geocodeServerReplyFinished())); + connect(m_geocodeServerReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(geocodeServerReplyError())); + } +} + +QString PlaceManagerEngineEsri::localizedName(const QJsonObject &jsonObject) +{ + const QJsonObject localizedNames = jsonObject.value(kLocalizedNamesKey).toObject(); + + foreach (const QLocale &locale, m_locales) + { + const QString localeStr = locale.name(); + if (localizedNames.contains(localeStr)) + { + return localizedNames.value(localeStr).toString(); + } + + const QString shortLocale = localeStr.left(2); + if (localizedNames.contains(shortLocale)) + { + return localizedNames.value(shortLocale).toString(); + } + } + return QString(); +} + +void PlaceManagerEngineEsri::parseCandidateFields(const QJsonArray &jsonArray) +{ + foreach (const QJsonValue &jsonValue, jsonArray) + { + if (!jsonValue.isObject()) + continue; + + const QJsonObject jsonCandidateField = jsonValue.toObject(); + if (!jsonCandidateField.contains(kLocalizedNamesKey)) + continue; + + const QString key = jsonCandidateField.value(kNameKey).toString(); + m_candidateFieldsLocale.insert(key, localizedName(jsonCandidateField)); + } +} + +void PlaceManagerEngineEsri::parseCountries(const QJsonArray &jsonArray) +{ + foreach (const QJsonValue &jsonValue, jsonArray) + { + if (!jsonValue.isObject()) + continue; + + const QJsonObject jsonCountry = jsonValue.toObject(); + if (!jsonCountry.contains(kLocalizedNamesKey)) + continue; + + const QString key = jsonCountry.value(kNameKey).toString(); + m_countriesLocale.insert(key, localizedName(jsonCountry)); + } +} + +void PlaceManagerEngineEsri::geocodeServerReplyFinished() +{ + if (!m_geocodeServerReply) + return; + + QJsonDocument document = QJsonDocument::fromJson(m_geocodeServerReply->readAll()); + if (!document.isObject()) + { + errorCaterogies(m_geocodeServerReply->errorString()); + return; + } + + QJsonObject jsonObject = document.object(); + + // parse categories + if (jsonObject.contains(kCategoriesKey)) + { + const QJsonArray jsonArray = jsonObject.value(kCategoriesKey).toArray(); + parseCategories(jsonArray, QString()); + } + + // parse candidateFields + if (jsonObject.contains(kCandidateFieldsKey)) + { + const QJsonArray jsonArray = jsonObject.value(kCandidateFieldsKey).toArray(); + parseCandidateFields(jsonArray); + } + + // parse countries + if (jsonObject.contains(kCountriesKey)) + { + const QJsonArray jsonArray = jsonObject.value(kCountriesKey).toArray(); + parseCountries(jsonArray); + } + + finishCategories(); + + m_geocodeServerReply->deleteLater(); +} + +void PlaceManagerEngineEsri::geocodeServerReplyError() +{ + if (m_categories.isEmpty() && !m_geocodeServerReply) + return; + + errorCaterogies(m_geocodeServerReply->errorString()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/placemanagerengine_esri.h b/src/plugins/geoservices/esri/placemanagerengine_esri.h new file mode 100644 index 00000000..2edb2d1a --- /dev/null +++ b/src/plugins/geoservices/esri/placemanagerengine_esri.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2018 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 PLACEMANAGERENGINEESRI_H +#define PLACEMANAGERENGINEESRI_H + +#include <QtLocation/QPlaceManagerEngine> +#include <QtLocation/QGeoServiceProvider> + +QT_BEGIN_NAMESPACE + +class PlaceCategoriesReplyEsri; +class QNetworkAccessManager; +class QNetworkReply; + +class PlaceManagerEngineEsri : public QPlaceManagerEngine +{ + Q_OBJECT + +public: + PlaceManagerEngineEsri(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString); + ~PlaceManagerEngineEsri(); + + QPlaceSearchReply *search(const QPlaceSearchRequest &request) override; + + QPlaceReply *initializeCategories() override; + QString parentCategoryId(const QString &categoryId) const override; + QStringList childCategoryIds(const QString &categoryId) const override; + QPlaceCategory category(const QString &categoryId) const override; + + QList<QPlaceCategory> childCategories(const QString &parentId) const override; + + QList<QLocale> locales() const override; + void setLocales(const QList<QLocale> &locales) override; + +private slots: + void geocodeServerReplyFinished(); + void geocodeServerReplyError(); + void replyFinished(); + void replyError(QPlaceReply::Error errorCode, const QString &errorString); + +private: + QNetworkAccessManager *m_networkManager = Q_NULLPTR; + + // geocode serveur + void initializeGeocodeServer(); + + QNetworkReply *m_geocodeServerReply = Q_NULLPTR; + + // categories + void finishCategories(); + void errorCaterogies(const QString &error); + void parseCategories(const QJsonArray &jsonArray, const QString &parentCategoryId); + + QList<PlaceCategoriesReplyEsri *> m_pendingCategoriesReply; + QHash<QString, QPlaceCategory> m_categories; + QHash<QString, QStringList> m_subcategories; + QHash<QString, QString> m_parentCategory; + + // localized names + QString localizedName(const QJsonObject &jsonObject); + void parseCandidateFields(const QJsonArray &jsonArray); + void parseCountries(const QJsonArray &jsonArray); + + QList<QLocale> m_locales; + QHash<QString, QString> m_candidateFieldsLocale; + QHash<QString, QString> m_countriesLocale; + void localizedName(); +}; + +QT_END_NAMESPACE + +#endif // PLACEMANAGERENGINEESRI_H diff --git a/src/plugins/geoservices/esri/placesearchreply_esri.cpp b/src/plugins/geoservices/esri/placesearchreply_esri.cpp new file mode 100644 index 00000000..a5a3585a --- /dev/null +++ b/src/plugins/geoservices/esri/placesearchreply_esri.cpp @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2018 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 "placesearchreply_esri.h" +#include "placemanagerengine_esri.h" + +#include <QtCore/QJsonDocument> +#include <QtCore/QJsonArray> +#include <QtCore/QJsonObject> +#include <QtNetwork/QNetworkReply> +#include <QtPositioning/QGeoCircle> +#include <QtPositioning/QGeoRectangle> +#include <QtLocation/QPlaceResult> +#include <QtLocation/QPlaceSearchRequest> +#include <QtLocation/private/qplacesearchrequest_p.h> + +static const QString kCandidatesKey(QStringLiteral("candidates")); +static const QString kAttributesKey(QStringLiteral("attributes")); +static const QString kAddressKey(QStringLiteral("address")); +static const QString kLongLabelKey(QStringLiteral("LongLabel")); +static const QString kCityKey(QStringLiteral("City")); +static const QString kCountryKey(QStringLiteral("Country")); +static const QString kRegionKey(QStringLiteral("Region")); +static const QString kPostalKey(QStringLiteral("Postal")); +static const QString kStAddrKey(QStringLiteral("StAddr")); +static const QString kStateKey(QStringLiteral("State")); +static const QString kDistrictKey(QStringLiteral("District")); +static const QString kLocationKey(QStringLiteral("location")); +static const QString kXKey(QStringLiteral("x")); +static const QString kYKey(QStringLiteral("y")); +static const QString kDistanceKey(QStringLiteral("Distance")); +static const QString kPhoneKey(QStringLiteral("Phone")); +static const QString kExtentKey(QStringLiteral("extent")); +static const QString kXminKey(QStringLiteral("xmin")); +static const QString kYminKey(QStringLiteral("ymin")); +static const QString kXmaxKey(QStringLiteral("xmax")); +static const QString kYmaxKey(QStringLiteral("ymax")); + +QT_BEGIN_NAMESPACE + +PlaceSearchReplyEsri::PlaceSearchReplyEsri(const QPlaceSearchRequest &request, QNetworkReply *reply, + const QHash<QString, QString> &candidateFields, + const QHash<QString, QString> &countries, PlaceManagerEngineEsri *parent) : + QPlaceSearchReply(parent), m_candidateFields(candidateFields), m_countries(countries) +{ + Q_ASSERT(parent); + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + setRequest(request); + + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkError(QNetworkReply::NetworkError))); + connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); +} + +PlaceSearchReplyEsri::~PlaceSearchReplyEsri() +{ +} + +void PlaceSearchReplyEsri::setError(QPlaceReply::Error errorCode, const QString &errorString) +{ + QPlaceReply::setError(errorCode, errorString); + emit error(errorCode, errorString); + setFinished(true); + emit finished(); +} + +void PlaceSearchReplyEsri::replyFinished() +{ + QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + if (!document.isObject()) + { + setError(ParseError, tr("Response parse error")); + return; + } + + QJsonValue suggestions = document.object().value(kCandidatesKey); + if (!suggestions.isArray()) + { + setError(ParseError, tr("Response parse error")); + return; + } + + QJsonArray resultsArray = suggestions.toArray(); + + QList<QPlaceSearchResult> results; + for (int i = 0; i < resultsArray.count(); ++i) + { + QJsonObject item = resultsArray.at(i).toObject(); + QPlaceResult placeResult = parsePlaceResult(item); + results.append(placeResult); + } + + setResults(results); + setFinished(true); + emit finished(); +} + +void PlaceSearchReplyEsri::networkError(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); + reply->deleteLater(); + setError(QPlaceReply::CommunicationError, reply->errorString()); +} + +QPlaceResult PlaceSearchReplyEsri::parsePlaceResult(const QJsonObject &item) const +{ + QPlace place; + QHash<QString, QString> keys; + + // set attributes + const QJsonObject attributes = item.value(kAttributesKey).toObject(); + for (const QString &key: attributes.keys()) + { + const QString value = attributes.value(key).toVariant().toString(); + if (!value.isEmpty()) + { + QPlaceAttribute attribute; + attribute.setLabel(m_candidateFields.value(key, key)); // local name or key + attribute.setText(value); + place.setExtendedAttribute(key, attribute); + keys.insert(key, value); + } + } + + if (keys.contains(kPhoneKey)) + { + QPlaceContactDetail contactDetail; + contactDetail.setLabel(m_candidateFields.value(kPhoneKey, kPhoneKey)); // local name or key + contactDetail.setValue(keys.value(kPhoneKey)); + place.appendContactDetail(QPlaceContactDetail::Phone, contactDetail); + } + + // set address + QGeoAddress geoAddress; + geoAddress.setCity(keys.value(kCityKey)); + geoAddress.setCountry(m_countries.value(keys.value(kCountryKey))); // mismatch code ISO2 vs ISO3 + geoAddress.setCounty(keys.value(kRegionKey)); + geoAddress.setPostalCode(keys.value(kPostalKey)); + geoAddress.setStreet(keys.value(kStAddrKey)); + geoAddress.setState(keys.value(kStateKey)); + geoAddress.setDistrict(keys.value(kDistrictKey)); + + // set location + const QJsonObject location = item.value(kLocationKey).toObject(); + const QGeoCoordinate coordinate = QGeoCoordinate(location.value(kYKey).toDouble(), + location.value(kXKey).toDouble()); + + // set boundingBox + const QJsonObject extent = item.value(kExtentKey).toObject(); + const QGeoCoordinate topLeft(extent.value(kYminKey).toDouble(), + extent.value(kXminKey).toDouble()); + const QGeoCoordinate bottomRight(extent.value(kYmaxKey).toDouble(), + extent.value(kXmaxKey).toDouble()); + const QGeoRectangle boundingBox(topLeft, bottomRight); + + // set geolocation + QGeoLocation geoLocation; + geoLocation.setCoordinate(coordinate); + geoLocation.setAddress(geoAddress); + geoLocation.setBoundingBox(boundingBox); + + // set place + place.setName(keys.value(kLongLabelKey)); + place.setLocation(geoLocation); + place.setPlaceId(attributes.value(kLongLabelKey).toString()); + + // set place result + QPlaceResult result; + result.setPlace(place); + result.setTitle(keys.value(kAddressKey)); + result.setDistance(keys.value(kDistanceKey).toDouble()); + + return result; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/placesearchreply_esri.h b/src/plugins/geoservices/esri/placesearchreply_esri.h new file mode 100644 index 00000000..195d650d --- /dev/null +++ b/src/plugins/geoservices/esri/placesearchreply_esri.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2018 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 PLACESEARCHREPLYESRI_H +#define PLACESEARCHREPLYESRI_H + +#include <QtLocation/QPlaceSearchReply> +#include <QNetworkReply> + +QT_BEGIN_NAMESPACE + +class PlaceManagerEngineEsri; +class QNetworkReply; +class QPlaceResult; + +class PlaceSearchReplyEsri : public QPlaceSearchReply +{ + Q_OBJECT + +public: + PlaceSearchReplyEsri(const QPlaceSearchRequest &request, QNetworkReply *reply, + const QHash<QString, QString> &candidateFields, + const QHash<QString, QString> &countries, PlaceManagerEngineEsri *parent); + ~PlaceSearchReplyEsri(); + + QString requestUrl; + +private slots: + void setError(QPlaceReply::Error errorCode, const QString &errorString); + void replyFinished(); + void networkError(QNetworkReply::NetworkError error); + +private: + QPlaceResult parsePlaceResult(const QJsonObject &item) const; + + const QHash<QString, QString> &m_candidateFields; + const QHash<QString, QString> &m_countries; +}; + +QT_END_NAMESPACE + +#endif // PLACESEARCHREPLYESRI_H diff --git a/src/plugins/geoservices/itemsoverlay/qgeomapitemsoverlay.cpp b/src/plugins/geoservices/itemsoverlay/qgeomapitemsoverlay.cpp index af0e263b..dede9dbc 100644 --- a/src/plugins/geoservices/itemsoverlay/qgeomapitemsoverlay.cpp +++ b/src/plugins/geoservices/itemsoverlay/qgeomapitemsoverlay.cpp @@ -70,10 +70,16 @@ public: #endif void updateObjectsGeometry(); + + void setVisibleArea(const QRectF &visibleArea) override; + QRectF visibleArea() const override; + protected: void changeViewportSize(const QSize &size) override; void changeCameraData(const QGeoCameraData &oldCameraData) override; void changeActiveMapType(const QGeoMapType mapType) override; + + QRectF m_visibleArea; }; QGeoMapItemsOverlay::QGeoMapItemsOverlay(QGeoMappingManagerEngineItemsOverlay *engine, QObject *parent) @@ -131,6 +137,24 @@ void QGeoMapItemsOverlay::removeMapObject(QGeoMapObject *obj) #endif } +void QGeoMapItemsOverlayPrivate::setVisibleArea(const QRectF &visibleArea) +{ + Q_Q(QGeoMapItemsOverlay); + const QRectF va = clampVisibleArea(visibleArea); + if (va == m_visibleArea) + return; + + m_visibleArea = va; + m_geoProjection->setVisibleArea(va); + + q->sgNodeChanged(); +} + +QRectF QGeoMapItemsOverlayPrivate::visibleArea() const +{ + return m_visibleArea; +} + QGeoMapItemsOverlayPrivate::QGeoMapItemsOverlayPrivate(QGeoMappingManagerEngineItemsOverlay *engine, QGeoMapItemsOverlay *map) : QGeoMapPrivate(engine, new QGeoProjectionWebMercator) { diff --git a/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp b/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp index c5f9d38c..8b0772ad 100644 --- a/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp +++ b/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp @@ -127,6 +127,13 @@ void QGeoRouteReplyMapbox::networkReplyFinished() QByteArray routeReply = reply->readAll(); QGeoRouteReply::Error error = parser->parseReply(routes, errorString, routeReply); + // Setting the request into the result + for (QGeoRoute &route : routes) { + route.setRequest(request()); + for (QGeoRoute &leg: route.routeLegs()) { + leg.setRequest(request()); + } + } QVariantMap metadata; metadata["osrm.reply-json"] = routeReply; diff --git a/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.cpp b/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.cpp index dfebc20d..ed36cd5f 100644 --- a/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.cpp +++ b/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.cpp @@ -106,12 +106,12 @@ QSGNode *QGeoMapMapboxGLPrivate::updateSceneGraph(QSGNode *node, QQuickWindow *w if (m_useFBO) { QSGMapboxGLTextureNode *mbglNode = new QSGMapboxGLTextureNode(m_settings, m_viewportSize, window->devicePixelRatio(), q); QObject::connect(mbglNode->map(), &QMapboxGL::mapChanged, q, &QGeoMapMapboxGL::onMapChanged); - m_syncState = MapTypeSync | CameraDataSync | ViewportSync; + m_syncState = MapTypeSync | CameraDataSync | ViewportSync | VisibleAreaSync; node = mbglNode; } else { QSGMapboxGLRenderNode *mbglNode = new QSGMapboxGLRenderNode(m_settings, m_viewportSize, window->devicePixelRatio(), q); QObject::connect(mbglNode->map(), &QMapboxGL::mapChanged, q, &QGeoMapMapboxGL::onMapChanged); - m_syncState = MapTypeSync | CameraDataSync | ViewportSync; + m_syncState = MapTypeSync | CameraDataSync | ViewportSync | VisibleAreaSync; node = mbglNode; } } @@ -125,7 +125,19 @@ QSGNode *QGeoMapMapboxGLPrivate::updateSceneGraph(QSGNode *node, QQuickWindow *w map->setStyleUrl(m_activeMapType.name()); } - if (m_syncState & CameraDataSync) { + if (m_syncState & VisibleAreaSync) { + if (m_visibleArea.isEmpty()) { + map->setMargins(QMargins()); + } else { + QMargins margins(m_visibleArea.x(), // left + m_visibleArea.y(), // top + m_viewportSize.width() - m_visibleArea.width() - m_visibleArea.x(), // right + m_viewportSize.height() - m_visibleArea.height() - m_visibleArea.y()); // bottom + map->setMargins(margins); + } + } + + if (m_syncState & CameraDataSync || m_syncState & VisibleAreaSync) { map->setZoom(zoomLevelFrom256(m_cameraData.zoomLevel() , MBGL_TILE_SIZE)); map->setBearing(m_cameraData.bearing()); map->setPitch(m_cameraData.tilt()); @@ -291,6 +303,25 @@ void QGeoMapMapboxGLPrivate::changeActiveMapType(const QGeoMapType) emit q->sgNodeChanged(); } +void QGeoMapMapboxGLPrivate::setVisibleArea(const QRectF &visibleArea) +{ + Q_Q(QGeoMapMapboxGL); + const QRectF va = clampVisibleArea(visibleArea); + if (va == m_visibleArea) + return; + + m_visibleArea = va; + m_geoProjection->setVisibleArea(va); + + m_syncState = m_syncState | VisibleAreaSync; + emit q->sgNodeChanged(); +} + +QRectF QGeoMapMapboxGLPrivate::visibleArea() const +{ + return m_visibleArea; +} + void QGeoMapMapboxGLPrivate::syncStyleChanges(QMapboxGL *map) { for (const auto& change : m_styleChanges) { @@ -347,7 +378,7 @@ QString QGeoMapMapboxGL::copyrightsStyleSheet() const return QStringLiteral("* { vertical-align: middle; font-weight: normal }"); } -void QGeoMapMapboxGL::setMapboxGLSettings(const QMapboxGLSettings& settings) +void QGeoMapMapboxGL::setMapboxGLSettings(const QMapboxGLSettings& settings, bool useChinaEndpoint) { Q_D(QGeoMapMapboxGL); @@ -355,8 +386,13 @@ void QGeoMapMapboxGL::setMapboxGLSettings(const QMapboxGLSettings& settings) // If the access token is not set, use the development access token. // This will only affect mapbox:// styles. + // Mapbox China requires a China-specific access token. if (d->m_settings.accessToken().isEmpty()) { - d->m_settings.setAccessToken(developmentToken); + if (useChinaEndpoint) { + qWarning("Mapbox China requires an access token: https://www.mapbox.com/contact/sales"); + } else { + d->m_settings.setAccessToken(developmentToken); + } } } @@ -376,7 +412,8 @@ QGeoMap::Capabilities QGeoMapMapboxGL::capabilities() const { return Capabilities(SupportsVisibleRegion | SupportsSetBearing - | SupportsAnchoringCoordinate); + | SupportsAnchoringCoordinate + | SupportsVisibleArea ); } QSGNode *QGeoMapMapboxGL::updateSceneGraph(QSGNode *oldNode, QQuickWindow *window) diff --git a/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.h b/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.h index 0ffaf4ea..5fc2260e 100644 --- a/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.h +++ b/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.h @@ -54,7 +54,7 @@ public: virtual ~QGeoMapMapboxGL(); QString copyrightsStyleSheet() const override; - void setMapboxGLSettings(const QMapboxGLSettings &); + void setMapboxGLSettings(const QMapboxGLSettings &, bool useChinaEndpoint); void setUseFBO(bool); void setMapItemsBefore(const QString &); Capabilities capabilities() const override; diff --git a/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl_p.h b/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl_p.h index ffb06208..f07b4c98 100644 --- a/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl_p.h +++ b/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl_p.h @@ -43,6 +43,7 @@ #include <QtCore/QSharedPointer> #include <QtCore/QTimer> #include <QtCore/QVariant> +#include <QtCore/QRectF> #include <QtLocation/private/qgeomap_p_p.h> #include <QtLocation/private/qgeomapparameter_p.h> @@ -70,9 +71,10 @@ public: /* Data members */ enum SyncState : int { NoSync = 0, - ViewportSync = 1 << 0, - CameraDataSync = 1 << 1, - MapTypeSync = 1 << 2 + ViewportSync = 1 << 0, + CameraDataSync = 1 << 1, + MapTypeSync = 1 << 2, + VisibleAreaSync = 1 << 3 }; Q_DECLARE_FLAGS(SyncStates, SyncState); @@ -96,11 +98,16 @@ protected: void changeCameraData(const QGeoCameraData &oldCameraData) override; void changeActiveMapType(const QGeoMapType mapType) override; + void setVisibleArea(const QRectF &visibleArea) override; + QRectF visibleArea() const override; + private: Q_DISABLE_COPY(QGeoMapMapboxGLPrivate); void syncStyleChanges(QMapboxGL *map); void threadedRenderingHack(QQuickWindow *window, QMapboxGL *map); + + QRectF m_visibleArea; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoMapMapboxGLPrivate::SyncStates) diff --git a/src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.cpp b/src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.cpp index cc48afb2..d2463106 100644 --- a/src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.cpp +++ b/src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.cpp @@ -69,30 +69,45 @@ QGeoMappingManagerEngineMapboxGL::QGeoMappingManagerEngineMapboxGL(const QVarian int mapId = 0; const QByteArray pluginName = "mapboxgl"; - mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox://styles/mapbox/streets-v10"), - tr("Streets"), false, false, ++mapId, pluginName, cameraCaps); - mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox://styles/mapbox/basic-v9"), - tr("Basic"), false, false, ++mapId, pluginName, cameraCaps); - mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox://styles/mapbox/bright-v9"), - tr("Bright"), false, false, ++mapId, pluginName, cameraCaps); - mapTypes << QGeoMapType(QGeoMapType::TerrainMap, QStringLiteral("mapbox://styles/mapbox/outdoors-v10"), - tr("Outdoors"), false, false, ++mapId, pluginName, cameraCaps); - mapTypes << QGeoMapType(QGeoMapType::SatelliteMapDay, QStringLiteral("mapbox://styles/mapbox/satellite-v9"), - tr("Satellite"), false, false, ++mapId, pluginName, cameraCaps); - mapTypes << QGeoMapType(QGeoMapType::HybridMap, QStringLiteral("mapbox://styles/mapbox/satellite-streets-v10"), - tr("Satellite Streets"), false, false, ++mapId, pluginName, cameraCaps); - mapTypes << QGeoMapType(QGeoMapType::GrayStreetMap, QStringLiteral("mapbox://styles/mapbox/light-v9"), - tr("Light"), false, false, ++mapId, pluginName, cameraCaps); - mapTypes << QGeoMapType(QGeoMapType::GrayStreetMap, QStringLiteral("mapbox://styles/mapbox/dark-v9"), - tr("Dark"), false, false, ++mapId, pluginName, cameraCaps); - mapTypes << QGeoMapType(QGeoMapType::TransitMap, QStringLiteral("mapbox://styles/mapbox/navigation-preview-day-v2"), - tr("Navigation Preview Day"), false, false, ++mapId, pluginName, cameraCaps); - mapTypes << QGeoMapType(QGeoMapType::TransitMap, QStringLiteral("mapbox://styles/mapbox/navigation-preview-night-v2"), - tr("Navigation Preview Night"), false, true, ++mapId, pluginName, cameraCaps); - mapTypes << QGeoMapType(QGeoMapType::CarNavigationMap, QStringLiteral("mapbox://styles/mapbox/navigation-guidance-day-v2"), - tr("Navigation Guidance Day"), false, false, ++mapId, pluginName, cameraCaps); - mapTypes << QGeoMapType(QGeoMapType::CarNavigationMap, QStringLiteral("mapbox://styles/mapbox/navigation-guidance-night-v2"), - tr("Navigation Guidance Night"), false, true, ++mapId, pluginName, cameraCaps); + if (parameters.contains(QStringLiteral("mapboxgl.china"))) { + m_useChinaEndpoint = parameters.value(QStringLiteral("mapboxgl.china")).toBool(); + } + + if (m_useChinaEndpoint) { + m_settings.setApiBaseUrl(QStringLiteral("https://api.mapbox.cn")); + + mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox://styles/mapbox/streets-zh-v1"), + tr("China Streets"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::GrayStreetMap, QStringLiteral("mapbox://styles/mapbox/light-zh-v1"), + tr("China Light"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::GrayStreetMap, QStringLiteral("mapbox://styles/mapbox/dark-zh-v1"), + tr("China Dark"), false, false, ++mapId, pluginName, cameraCaps); + } else { + mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox://styles/mapbox/streets-v10"), + tr("Streets"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox://styles/mapbox/basic-v9"), + tr("Basic"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox://styles/mapbox/bright-v9"), + tr("Bright"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::TerrainMap, QStringLiteral("mapbox://styles/mapbox/outdoors-v10"), + tr("Outdoors"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::SatelliteMapDay, QStringLiteral("mapbox://styles/mapbox/satellite-v9"), + tr("Satellite"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::HybridMap, QStringLiteral("mapbox://styles/mapbox/satellite-streets-v10"), + tr("Satellite Streets"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::GrayStreetMap, QStringLiteral("mapbox://styles/mapbox/light-v9"), + tr("Light"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::GrayStreetMap, QStringLiteral("mapbox://styles/mapbox/dark-v9"), + tr("Dark"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::TransitMap, QStringLiteral("mapbox://styles/mapbox/navigation-preview-day-v2"), + tr("Navigation Preview Day"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::TransitMap, QStringLiteral("mapbox://styles/mapbox/navigation-preview-night-v2"), + tr("Navigation Preview Night"), false, true, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::CarNavigationMap, QStringLiteral("mapbox://styles/mapbox/navigation-guidance-day-v2"), + tr("Navigation Guidance Day"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::CarNavigationMap, QStringLiteral("mapbox://styles/mapbox/navigation-guidance-night-v2"), + tr("Navigation Guidance Night"), false, true, ++mapId, pluginName, cameraCaps); + } if (parameters.contains(QStringLiteral("mapboxgl.mapping.additional_style_urls"))) { const QString ids = parameters.value(QStringLiteral("mapboxgl.mapping.additional_style_urls")).toString(); @@ -156,7 +171,7 @@ QGeoMappingManagerEngineMapboxGL::~QGeoMappingManagerEngineMapboxGL() QGeoMap *QGeoMappingManagerEngineMapboxGL::createMap() { QGeoMapMapboxGL* map = new QGeoMapMapboxGL(this, 0); - map->setMapboxGLSettings(m_settings); + map->setMapboxGLSettings(m_settings, m_useChinaEndpoint); map->setUseFBO(m_useFBO); map->setMapItemsBefore(m_mapItemsBefore); diff --git a/src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.h b/src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.h index b3afe77b..9ceb8ccc 100644 --- a/src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.h +++ b/src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.h @@ -59,6 +59,7 @@ public: private: QMapboxGLSettings m_settings; bool m_useFBO = true; + bool m_useChinaEndpoint = false; QString m_mapItemsBefore; }; diff --git a/src/plugins/geoservices/mapboxgl/qgeoserviceproviderpluginmapboxgl.cpp b/src/plugins/geoservices/mapboxgl/qgeoserviceproviderpluginmapboxgl.cpp index dd25c99e..afa723a5 100644 --- a/src/plugins/geoservices/mapboxgl/qgeoserviceproviderpluginmapboxgl.cpp +++ b/src/plugins/geoservices/mapboxgl/qgeoserviceproviderpluginmapboxgl.cpp @@ -40,16 +40,10 @@ #include <QtGui/QOpenGLContext> -static void initResources() -{ - Q_INIT_RESOURCE(mapboxgl); -} - QT_BEGIN_NAMESPACE QGeoServiceProviderFactoryMapboxGL::QGeoServiceProviderFactoryMapboxGL() { - initResources(); } QGeoCodingManagerEngine *QGeoServiceProviderFactoryMapboxGL::createGeocodingManagerEngine( diff --git a/src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp b/src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp index 6c47d3ee..b45fdef1 100644 --- a/src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp +++ b/src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp @@ -41,6 +41,7 @@ #include <QtCore/QRegularExpression> #include <QtCore/QStringList> #include <QtPositioning/QGeoPath> +#include <QtPositioning/QGeoPolygon> #include <QtQml/QJSValue> namespace { @@ -66,7 +67,7 @@ QString getId(QDeclarativeGeoMapItemBase *mapItem) // Mapbox GL supports geometry segments that spans above 180 degrees in // longitude. To keep visual expectations in parity with Qt, we need to adapt // the coordinates to always use the shortest path when in ambiguity. -bool geoRectangleCrossesDateLine(const QGeoRectangle &rect) { +static bool geoRectangleCrossesDateLine(const QGeoRectangle &rect) { return rect.topLeft().longitude() > rect.bottomRight().longitude(); } @@ -112,24 +113,35 @@ QMapbox::Feature featureFromMapCircle(QDeclarativeCircleMapItem *mapItem) return QMapbox::Feature(QMapbox::Feature::PolygonType, geometry, {}, getId(mapItem)); } -QMapbox::Feature featureFromMapPolygon(QDeclarativePolygonMapItem *mapItem) +static QMapbox::Coordinates qgeocoordinate2mapboxcoordinate(const QList<QGeoCoordinate> &crds, const bool crossesDateline, bool closed = false) { - const QGeoPath *path = static_cast<const QGeoPath *>(&mapItem->geoShape()); QMapbox::Coordinates coordinates; - const bool crossesDateline = geoRectangleCrossesDateLine(path->boundingGeoRectangle()); - for (const QGeoCoordinate &coordinate : path->path()) { + for (const QGeoCoordinate &coordinate : crds) { if (!coordinates.empty() && crossesDateline && qAbs(coordinate.longitude() - coordinates.last().second) > 180.0) { coordinates << QMapbox::Coordinate { coordinate.latitude(), coordinate.longitude() + (coordinate.longitude() >= 0 ? -360.0 : 360.0) }; } else { coordinates << QMapbox::Coordinate { coordinate.latitude(), coordinate.longitude() }; } } + if (closed && !coordinates.empty() && coordinates.last() != coordinates.first()) + coordinates.append(coordinates.first()); // closing the path + return coordinates; +} - if (!coordinates.empty()) - coordinates.append(coordinates.first()); // closing the path - - QMapbox::CoordinatesCollections geometry { { coordinates } }; +QMapbox::Feature featureFromMapPolygon(QDeclarativePolygonMapItem *mapItem) +{ + const QGeoPolygon *polygon = static_cast<const QGeoPolygon *>(&mapItem->geoShape()); + const bool crossesDateline = geoRectangleCrossesDateLine(polygon->boundingGeoRectangle()); + QMapbox::CoordinatesCollections geometry; + QMapbox::CoordinatesCollection poly; + QMapbox::Coordinates coordinates = qgeocoordinate2mapboxcoordinate(polygon->path(), crossesDateline, true); + poly.push_back(coordinates); + for (int i = 0; i < polygon->holesCount(); ++i) { + coordinates = qgeocoordinate2mapboxcoordinate(polygon->holePath(i), crossesDateline, true); + poly.push_back(coordinates); + } + geometry.push_back(poly); return QMapbox::Feature(QMapbox::Feature::PolygonType, geometry, {}, getId(mapItem)); } @@ -186,8 +198,7 @@ QList<QSharedPointer<QMapboxGLStyleChange>> QMapboxGLStyleChange::addMapParamete { static const QStringList acceptedParameterTypes = QStringList() << QStringLiteral("paint") << QStringLiteral("layout") << QStringLiteral("filter") - << QStringLiteral("layer") << QStringLiteral("source") << QStringLiteral("image") - << QStringLiteral("margins"); + << QStringLiteral("layer") << QStringLiteral("source") << QStringLiteral("image"); QList<QSharedPointer<QMapboxGLStyleChange>> changes; @@ -213,9 +224,6 @@ QList<QSharedPointer<QMapboxGLStyleChange>> QMapboxGLStyleChange::addMapParamete case 5: // image changes << QMapboxGLStyleAddImage::fromMapParameter(param); break; - case 6: // margins - changes << QMapboxGLMapMargins::fromMapParameter(param); - break; } return changes; @@ -641,38 +649,3 @@ QSharedPointer<QMapboxGLStyleChange> QMapboxGLStyleAddImage::fromMapParameter(QG return QSharedPointer<QMapboxGLStyleChange>(image); } - -// QMapboxGLMapMargins - -void QMapboxGLMapMargins::apply(QMapboxGL *map) -{ - // FIXME: Qt projection handlers are not yet aware of these margins, - // thus map items placement, {to,from}Coordinate, mouse area, etc. - // will require manual fixups. - map->setMargins(m_margins); -} - -QSharedPointer<QMapboxGLStyleChange> QMapboxGLMapMargins::fromMapParameter(QGeoMapParameter *param) -{ - Q_ASSERT(param->type() == "margins"); - - auto mapMargins = new QMapboxGLMapMargins(); - - QVariant leftMargin = param->property("left"); - if (leftMargin.isValid()) - mapMargins->m_margins.setLeft(leftMargin.toInt()); - - QVariant topMargin = param->property("top"); - if (topMargin.isValid()) - mapMargins->m_margins.setTop(topMargin.toInt()); - - QVariant rightMargin = param->property("right"); - if (rightMargin.isValid()) - mapMargins->m_margins.setRight(rightMargin.toInt()); - - QVariant bottomMargin = param->property("bottom"); - if (bottomMargin.isValid()) - mapMargins->m_margins.setBottom(bottomMargin.toInt()); - - return QSharedPointer<QMapboxGLStyleChange>(mapMargins); -} diff --git a/src/plugins/geoservices/mapboxgl/qmapboxglstylechange_p.h b/src/plugins/geoservices/mapboxgl/qmapboxglstylechange_p.h index 38aa87f8..fd5b9af4 100644 --- a/src/plugins/geoservices/mapboxgl/qmapboxglstylechange_p.h +++ b/src/plugins/geoservices/mapboxgl/qmapboxglstylechange_p.h @@ -190,17 +190,4 @@ private: QImage m_sprite; }; -class QMapboxGLMapMargins : public QMapboxGLStyleChange -{ -public: - static QSharedPointer<QMapboxGLStyleChange> fromMapParameter(QGeoMapParameter *); - - void apply(QMapboxGL *map) override; - -private: - QMapboxGLMapMargins() = default; - - QMargins m_margins; -}; - #endif // QQMAPBOXGLSTYLECHANGE_P_H diff --git a/src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.cpp b/src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.cpp index 9808b539..28aa930f 100644 --- a/src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.cpp +++ b/src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.cpp @@ -46,6 +46,7 @@ #include <QtLocation/QPlaceIcon> #include <QtLocation/QPlaceResult> #include <QtLocation/QPlaceProposedSearchResult> +#include <QtLocation/private/qplacesearchrequest_p.h> #include <QtCore/QDebug> @@ -113,15 +114,24 @@ void QPlaceSearchReplyHere::replyFinished() results.append(parseSearchResult(item)); } + QPlaceSearchRequest r_orig = request(); + QPlaceSearchRequestPrivate *rpimpl_orig = QPlaceSearchRequestPrivate::get(r_orig); + if (resultsObject.contains(QStringLiteral("next"))) { QPlaceSearchRequest request; request.setSearchContext(QUrl(resultsObject.value(QStringLiteral("next")).toString())); + QPlaceSearchRequestPrivate *rpimpl = QPlaceSearchRequestPrivate::get(request); + rpimpl->related = true; + rpimpl->page = rpimpl_orig->page + 1; setNextPageRequest(request); } if (resultsObject.contains(QStringLiteral("previous"))) { QPlaceSearchRequest request; request.setSearchContext(QUrl(resultsObject.value(QStringLiteral("previous")).toString())); + QPlaceSearchRequestPrivate *rpimpl = QPlaceSearchRequestPrivate::get(request); + rpimpl->related = true; + rpimpl->page = rpimpl_orig->page - 1; setPreviousPageRequest(request); } diff --git a/src/plugins/geoservices/nokia/qgeoroutexmlparser.cpp b/src/plugins/geoservices/nokia/qgeoroutexmlparser.cpp index 8e436a97..65e39dee 100644 --- a/src/plugins/geoservices/nokia/qgeoroutexmlparser.cpp +++ b/src/plugins/geoservices/nokia/qgeoroutexmlparser.cpp @@ -40,9 +40,12 @@ #include <QStringList> #include <QString> #include <QtCore/QThreadPool> +#include <QDebug> #include <QtPositioning/QGeoRectangle> +#include <QtPositioning/QGeoPath> #include <QtLocation/QGeoRoute> +#include <QtLocation/private/qgeoroutesegment_p.h> QT_BEGIN_NAMESPACE @@ -65,6 +68,10 @@ QGeoRouteXmlParser::~QGeoRouteXmlParser() void QGeoRouteXmlParser::parse(const QByteArray &data) { m_data = data; +// QFile file("/tmp/here.xml"); +// file.open(QIODevice::WriteOnly); +// file.write(data); +// file.close(); QThreadPool::globalInstance()->start(this); } @@ -134,8 +141,9 @@ bool QGeoRouteXmlParser::parseRoute(QGeoRoute *route) { Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "Route"); m_maneuvers.clear(); - m_segments.clear(); - +// m_segments.clear(); + m_legs.clear(); + int legIndex = 0; m_reader->readNext(); while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "Route") && !m_reader->hasError()) { @@ -161,7 +169,7 @@ bool QGeoRouteXmlParser::parseRoute(QGeoRoute *route) return false; route->setBounds(bounds); } else if (m_reader->name() == "Leg") { - if (!parseLeg()) + if (!parseLeg(legIndex++)) return false; } else if (m_reader->name() == "Summary") { if (!parseSummary(route)) @@ -179,20 +187,32 @@ bool QGeoRouteXmlParser::parseRoute(QGeoRoute *route) return postProcessRoute(route); } -bool QGeoRouteXmlParser::parseLeg() +bool QGeoRouteXmlParser::parseLeg(int legIndex) { - Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "Leg"); - + Q_ASSERT(m_reader->isStartElement() && m_reader->name() == QStringLiteral("Leg")); + QGeoRouteLeg leg; + leg.setLegIndex(legIndex); m_reader->readNext(); - while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "Leg") && + QList<QGeoManeuverContainer> maneuvers; + QList<QGeoRouteSegmentContainer> links; + while (!(m_reader->tokenType() == QXmlStreamReader::EndElement + && m_reader->name() == QStringLiteral("Leg")) && !m_reader->hasError()) { if (m_reader->tokenType() == QXmlStreamReader::StartElement) { - if (m_reader->name() == "Maneuver") { - if (!parseManeuver()) - return false; - } else if (m_reader->name() == "Link") { - if (!parseLink()) + if (m_reader->name() == QStringLiteral("Maneuver")) { + if (!parseManeuver(maneuvers)) return false; + } +// Currently unused, after requesting shape attribute in maneuvers. +// Links, however, contain additional info, such as speed limits, and might become needed in the future. +// else if (m_reader->name() == QStringLiteral("Link")) { +// if (!parseLink(links)) +// return false; +// } + else if (m_reader->name() == "TravelTime") { + leg.setTravelTime(qRound(m_reader->readElementText().toDouble())); + } else if (m_reader->name() == "Length") { + leg.setDistance(m_reader->readElementText().toDouble()); } else { m_reader->skipCurrentElement(); } @@ -200,81 +220,98 @@ bool QGeoRouteXmlParser::parseLeg() m_reader->readNext(); } - return !m_reader->hasError(); + if (m_reader->hasError()) + return false; + + m_legs << leg; +// m_segments << links; + m_maneuvers << maneuvers; + return true; } +//static bool fuzzyCompare(const QGeoCoordinate &a, const QGeoCoordinate& b) +//{ +// return qFuzzyCompare(a.latitude(), b.latitude()) && qFuzzyCompare(a.longitude(), b.longitude()); +//} + bool QGeoRouteXmlParser::postProcessRoute(QGeoRoute *route) { - QList<QGeoRouteSegment> routeSegments; + QList<QList<QGeoRouteSegment>> legSegments; + Q_ASSERT(m_maneuvers.size()); + - int maneuverIndex = 0; - for (int i = 0; i < m_segments.count(); ++i) { - // In case there is a maneuver in the middle of the list with no - // link ID attached, attach it to the next available segment - while ((maneuverIndex < m_maneuvers.size() - 1) && m_maneuvers.at(maneuverIndex).toId.isEmpty()) { + // Step 3: populate the linkMap, linkId -> linkContainer + for (int i = 0; i < m_maneuvers.size(); i++) { + legSegments << QList<QGeoRouteSegment>(); + QList<QGeoRouteSegment> &segments = legSegments[i]; + QList<QGeoManeuverContainer> &maneuvers = m_maneuvers[i]; + for (int j = 0; j < m_maneuvers.at(i).size(); j++) { + QGeoManeuverContainer &maneuver = maneuvers[j]; QGeoRouteSegment segment; - segment.setManeuver(m_maneuvers.at(maneuverIndex).maneuver); - QList<QGeoCoordinate> path; // use instruction position as one point segment path - path.append(m_maneuvers.at(maneuverIndex).maneuver.position()); - segment.setPath(path); - routeSegments.append(segment); - ++maneuverIndex; - } - QGeoRouteSegment segment = m_segments.at(i).segment; - if ((maneuverIndex < m_maneuvers.size()) && m_segments.at(i).id == m_maneuvers.at(maneuverIndex).toId) { - segment.setManeuver(m_maneuvers.at(maneuverIndex).maneuver); - ++maneuverIndex; + QVariantMap extendedAttributes; + extendedAttributes["first"] = maneuver.first; + extendedAttributes["last"] = maneuver.last; + extendedAttributes["legIndex"] = i; + extendedAttributes["id"] = maneuver.id; + extendedAttributes["toLink"] = maneuver.toLink; + extendedAttributes["index"] = j; + maneuver.maneuver.setExtendedAttributes(extendedAttributes); + + segment.setDistance(maneuver.maneuver.distanceToNextInstruction()); + segment.setTravelTime(maneuver.maneuver.timeToNextInstruction()); + segment.setPath(maneuver.path); + segment.setManeuver(maneuver.maneuver); + segments << segment; } - routeSegments.append(segment); - } - - // For the final maneuver in the list, make sure to attach it to the very - // last segment on the path, this is why we don't process the last - // maneuver in the loop above - while (maneuverIndex < m_maneuvers.size()) { - QGeoRouteSegment segment; - segment.setManeuver(m_maneuvers.at(maneuverIndex).maneuver); - QList<QGeoCoordinate> path; // use instruction position as one point segment path - path.append(m_maneuvers.at(maneuverIndex).maneuver.position()); - segment.setPath(path); - - routeSegments.append(segment); - ++maneuverIndex; } - QList<QGeoRouteSegment> compactedRouteSegments; - compactedRouteSegments.append(routeSegments.first()); - routeSegments.removeFirst(); - - while (routeSegments.size() > 0) { - QGeoRouteSegment segment = routeSegments.first(); - routeSegments.removeFirst(); - - QGeoRouteSegment lastSegment = compactedRouteSegments.last(); - - if (lastSegment.maneuver().isValid()) { - compactedRouteSegments.append(segment); - } else { - compactedRouteSegments.removeLast(); - lastSegment.setDistance(lastSegment.distance() + segment.distance()); - lastSegment.setTravelTime(lastSegment.travelTime() + segment.travelTime()); - QList<QGeoCoordinate> path = lastSegment.path(); - path.append(segment.path()); - lastSegment.setPath(path); - lastSegment.setManeuver(segment.maneuver()); - compactedRouteSegments.append(lastSegment); + // Step 7: connect all segments. + QGeoRouteSegment segment; + QGeoRouteSegment firstSegment; + for (auto &segments: legSegments) { + for (int j = 0; j < segments.size(); j++) { + if (segment.isValid()) { + segment.setNextRouteSegment(segments[j]); + } else { + firstSegment = segments[j]; + } + segment = segments[j]; + if (j == segments.size() - 1) { + QGeoRouteSegmentPrivate *sp = QGeoRouteSegmentPrivate::get(segment); + sp->setLegLastSegment(true); + } } } - if (compactedRouteSegments.size() > 0) { - route->setFirstRouteSegment(compactedRouteSegments.at(0)); - for (int i = 0; i < compactedRouteSegments.size() - 1; ++i) - compactedRouteSegments[i].setNextRouteSegment(compactedRouteSegments.at(i + 1)); + if (firstSegment.isValid()) + route->setFirstRouteSegment(firstSegment); + + // Step 8: fill route legs. + for (int i = 0; i < m_legs.size(); i++) { + m_legs[i].setTravelMode(route->travelMode()); + m_legs[i].setRequest(route->request()); + m_legs[i].setOverallRoute(*route); + m_legs[i].setLegIndex(i); + + m_legs[i].setFirstRouteSegment(legSegments[i].first()); + + // handle path + QList<QGeoCoordinate> path; + QGeoRouteSegment s = m_legs[i].firstRouteSegment(); + while (s.isValid()) { + path.append(s.path()); + if (s.isLegLastSegment()) + break; + s = s.nextRouteSegment(); + } + m_legs[i].setPath(path); + m_legs[i].setBounds(QGeoPath(path).boundingGeoRectangle()); } - + route->setRouteLegs(m_legs); + m_legs.clear(); +// m_segments.clear(); m_maneuvers.clear(); - m_segments.clear(); return true; } @@ -394,7 +431,7 @@ bool QGeoRouteXmlParser::parseCoordinates(QGeoCoordinate &coord) return !m_reader->hasError(); } -bool QGeoRouteXmlParser::parseManeuver() +bool QGeoRouteXmlParser::parseManeuver(QList<QGeoManeuverContainer> &maneuvers) { Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "Maneuver"); @@ -415,8 +452,14 @@ bool QGeoRouteXmlParser::parseManeuver() maneuverContainter.maneuver.setPosition(coordinates); } else if (m_reader->name() == "Instruction") { maneuverContainter.maneuver.setInstructionText(m_reader->readElementText()); + } else if (m_reader->name() == "Shape") { + QString elementName = m_reader->name().toString(); + QList<QGeoCoordinate> path; + if (!parseGeoPoints(m_reader->readElementText(), &path, elementName)) + return false; + maneuverContainter.path = path; } else if (m_reader->name() == "ToLink") { - maneuverContainter.toId = m_reader->readElementText(); + maneuverContainter.toLink = m_reader->readElementText(); } else if (m_reader->name() == "TravelTime") { maneuverContainter.maneuver.setTimeToNextInstruction(qRound(m_reader->readElementText().toDouble())); } else if (m_reader->name() == "Length") { @@ -457,11 +500,11 @@ bool QGeoRouteXmlParser::parseManeuver() if (m_reader->hasError()) return false; - m_maneuvers.append(maneuverContainter); + maneuvers.append(maneuverContainter); return true; } -bool QGeoRouteXmlParser::parseLink() +bool QGeoRouteXmlParser::parseLink(QList<QGeoRouteSegmentContainer> &links) { Q_ASSERT(m_reader->isStartElement() && m_reader->name() == QStringLiteral("Link")); m_reader->readNext(); @@ -498,8 +541,7 @@ bool QGeoRouteXmlParser::parseLink() if (m_reader->hasError()) return false; - - m_segments.append(segmentContainer); + links.append(segmentContainer); return true; } diff --git a/src/plugins/geoservices/nokia/qgeoroutexmlparser.h b/src/plugins/geoservices/nokia/qgeoroutexmlparser.h index e2feb728..7f2cc149 100644 --- a/src/plugins/geoservices/nokia/qgeoroutexmlparser.h +++ b/src/plugins/geoservices/nokia/qgeoroutexmlparser.h @@ -45,6 +45,7 @@ #include <QtLocation/QGeoRouteRequest> #include <QtLocation/QGeoRouteSegment> #include <QtLocation/QGeoManeuver> +#include <QtLocation/qgeoroute.h> QT_BEGIN_NAMESPACE @@ -58,7 +59,12 @@ class QGeoManeuverContainer public: QGeoManeuver maneuver; QString id; - QString toId; + QString toLink; // Id of the link this maneuver brings into + int legIndex = 0; + int index = 0; + QList<QGeoCoordinate> path; + bool first = false; + bool last = false; }; class QGeoRouteSegmentContainer @@ -67,6 +73,11 @@ public: QGeoRouteSegment segment; QString id; QString maneuverId; + + bool operator ==(const QGeoRouteSegmentContainer &other) const + { + return ( segment == other.segment && id == other.id && maneuverId == other.maneuverId ); + } }; class QGeoDynamicSpeedInfoContainer @@ -104,9 +115,9 @@ private: bool parseMode(QGeoRoute *route); bool parseSummary(QGeoRoute *route); bool parseGeoPoints(const QString &strPoints, QList<QGeoCoordinate> *geoPoints, const QString &elementName); - bool parseLeg(); - bool parseManeuver(); - bool parseLink(); + bool parseLeg(int legIndex); + bool parseManeuver(QList<QGeoManeuverContainer> &maneuvers); + bool parseLink(QList<QGeoRouteSegmentContainer> &links); bool postProcessRoute(QGeoRoute *route); bool parseBoundingBox(QGeoRectangle &bounds); @@ -117,8 +128,9 @@ private: QXmlStreamReader *m_reader; QList<QGeoRoute> m_results; - QList<QGeoManeuverContainer> m_maneuvers; - QList<QGeoRouteSegmentContainer> m_segments; + QList<QGeoRouteLeg> m_legs; + QList<QList<QGeoManeuverContainer>> m_maneuvers; + //QList<QList<QGeoRouteSegmentContainer>> m_segments; }; QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp b/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp index 1ae01636..73b998b1 100644 --- a/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp +++ b/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp @@ -399,46 +399,36 @@ QString QGeoRoutingManagerEngineNokia::routeRequestString(const QGeoRouteRequest requestString += trimDouble(area.bottomRight().longitude()); } -// TODO: work out what was going on here -// - segment and instruction/maneuever functions are mixed and matched -// - tried to implement sensible equivalents below -// QStringList legAttributes; -// if (request.instructionDetail() & QGeoRouteRequest::BasicSegmentData) { -// requestString += "&linkattributes=sh,le"; //shape,length -// legAttributes.append("links"); -// } -// -// if (request.instructionDetail() & QGeoRouteRequest::BasicInstructions) { -// legAttributes.append("maneuvers"); -// requestString += "&maneuverattributes=po,tt,le,di"; //position,traveltime,length,direction -// if (!(request.instructionDetail() & QGeoRouteRequest::NoSegmentData)) -// requestString += ",li"; //link -// } - QStringList legAttributes; - if (request.segmentDetail() & QGeoRouteRequest::BasicSegmentData) { +// if (request.segmentDetail() & QGeoRouteRequest::BasicSegmentData) // QTBUG-70501, this code expects to find links + { requestString += "&linkattributes=sh,le"; //shape,length legAttributes.append("links"); } - if (request.maneuverDetail() & QGeoRouteRequest::BasicManeuvers) { +// if (request.maneuverDetail() & QGeoRouteRequest::BasicManeuvers) // QTBUG-70501, this code expects to find maneuvers + { legAttributes.append("maneuvers"); - requestString += "&maneuverattributes=po,tt,le,di"; //position,traveltime,length,direction + //requestString += "&maneuverattributes=po,tt,le,di"; //position,traveltime,length,direction + requestString += "&maneuverattributes=all"; if (!(request.segmentDetail() & QGeoRouteRequest::NoSegmentData)) requestString += ",li"; //link } + // Handle QTBUG-70502, when API fixes it requestString += "&routeattributes=sm,sh,bb,lg"; //summary,shape,boundingBox,legs if (legAttributes.count() > 0) { requestString += "&legattributes="; requestString += legAttributes.join(","); } + // Handle QTBUG-70503, when API fixes it requestString += "&departure="; requestString += QDateTime::currentDateTime().toUTC().toString("yyyy-MM-ddThh:mm:ssZ"); requestString += "&instructionformat=text"; + // ToDo: make this request-able requestString += "&metricSystem="; if (QLocale::MetricSystem == measurementSystem()) requestString += "metric"; @@ -447,6 +437,7 @@ QString QGeoRoutingManagerEngineNokia::routeRequestString(const QGeoRouteRequest const QLocale loc(locale()); + // ToDo: make this request-able if (QLocale::C != loc.language() && QLocale::AnyLanguage != loc.language()) { requestString += "&language="; requestString += loc.name(); diff --git a/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp b/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp index f68a0d99..e4ef86d6 100644 --- a/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp +++ b/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp @@ -47,11 +47,6 @@ #include <QNetworkProxy> #include <QCoreApplication> -static void initResources() -{ - Q_INIT_RESOURCE(nokia); -} - QT_BEGIN_NAMESPACE namespace @@ -117,7 +112,6 @@ namespace QGeoServiceProviderFactoryNokia::QGeoServiceProviderFactoryNokia() { - initResources(); } QGeoCodingManagerEngine *QGeoServiceProviderFactoryNokia::createGeocodingManagerEngine( diff --git a/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp b/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp index ab575463..5094b72e 100644 --- a/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp +++ b/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp @@ -52,6 +52,7 @@ #include <QtCore/QJsonArray> #include <QtCore/QJsonDocument> #include <QtCore/QJsonObject> +#include <QtCore/QRegularExpression> #include <QtCore/QStandardPaths> #include <QtCore/QUrlQuery> #include <QtNetwork/QNetworkProxy> @@ -690,13 +691,14 @@ QPlaceIcon QPlaceManagerEngineNokiaV2::icon(const QString &remotePath, QPlaceIcon icon; QVariantMap params; - QRegExp rx("(.*)(/icons/categories/.*)"); + QRegularExpression rx("(.*)(/icons/categories/.*)"); + QRegularExpressionMatch match = rx.match(remotePath); QString iconPrefix; QString nokiaIcon; - if (rx.indexIn(remotePath) != -1 && !rx.cap(1).isEmpty() && !rx.cap(2).isEmpty()) { - iconPrefix = rx.cap(1); - nokiaIcon = rx.cap(2); + if (match.hasMatch() && !match.capturedRef(1).isEmpty() && !match.capturedRef(2).isEmpty()) { + iconPrefix = match.captured(1); + nokiaIcon = match.captured(2); if (QFile::exists(m_localDataPath + nokiaIcon)) iconPrefix = QString::fromLatin1("file://") + m_localDataPath; diff --git a/src/plugins/geoservices/osm/qgeoroutereplyosm.cpp b/src/plugins/geoservices/osm/qgeoroutereplyosm.cpp index 732e8d72..af2a03b1 100644 --- a/src/plugins/geoservices/osm/qgeoroutereplyosm.cpp +++ b/src/plugins/geoservices/osm/qgeoroutereplyosm.cpp @@ -75,6 +75,13 @@ void QGeoRouteReplyOsm::networkReplyFinished() QList<QGeoRoute> routes; QString errorString; QGeoRouteReply::Error error = parser->parseReply(routes, errorString, reply->readAll()); + // Setting the request into the result + for (QGeoRoute &route : routes) { + route.setRequest(request()); + for (QGeoRoute &leg: route.routeLegs()) { + leg.setRequest(request()); + } + } if (error == QGeoRouteReply::NoError) { setRoutes(routes.mid(0, request().numberAlternativeRoutes() + 1)); diff --git a/src/plugins/geoservices/osm/qplacemanagerengineosm.cpp b/src/plugins/geoservices/osm/qplacemanagerengineosm.cpp index 3c201e41..be66414f 100644 --- a/src/plugins/geoservices/osm/qplacemanagerengineosm.cpp +++ b/src/plugins/geoservices/osm/qplacemanagerengineosm.cpp @@ -191,7 +191,7 @@ QPlaceSearchReply *QPlaceManagerEngineOsm::search(const QPlaceSearchRequest &req this, SLOT(replyError(QPlaceReply::Error,QString))); if (m_debugQuery) - reply->requestUrl = requestUrl.toString(); + reply->requestUrl = requestUrl.url(QUrl::None); return reply; } diff --git a/src/plugins/geoservices/osm/qplacesearchreplyosm.cpp b/src/plugins/geoservices/osm/qplacesearchreplyosm.cpp index 0228a975..80c50d1b 100644 --- a/src/plugins/geoservices/osm/qplacesearchreplyosm.cpp +++ b/src/plugins/geoservices/osm/qplacesearchreplyosm.cpp @@ -48,6 +48,7 @@ #include <QtPositioning/QGeoRectangle> #include <QtLocation/QPlaceResult> #include <QtLocation/QPlaceSearchRequest> +#include <QtLocation/private/qplacesearchrequest_p.h> QT_BEGIN_NAMESPACE @@ -135,6 +136,9 @@ void QPlaceSearchReplyOsm::replyFinished() parameters.insert(QStringLiteral("ExcludePlaceIds"), epi); r.setSearchContext(parameters); + QPlaceSearchRequestPrivate *rpimpl = QPlaceSearchRequestPrivate::get(r); + rpimpl->related = true; + rpimpl->page--; setPreviousPageRequest(r); } @@ -147,6 +151,9 @@ void QPlaceSearchReplyOsm::replyFinished() parameters.insert(QStringLiteral("ExcludePlaceIds"), epi); r.setSearchContext(parameters); + QPlaceSearchRequestPrivate *rpimpl = QPlaceSearchRequestPrivate::get(r); + rpimpl->related = true; + rpimpl->page++; setNextPageRequest(r); } diff --git a/src/plugins/position/android/src/qgeopositioninfosource_android.cpp b/src/plugins/position/android/src/qgeopositioninfosource_android.cpp index 7b4706d8..59b8beab 100644 --- a/src/plugins/position/android/src/qgeopositioninfosource_android.cpp +++ b/src/plugins/position/android/src/qgeopositioninfosource_android.cpp @@ -223,7 +223,7 @@ void QGeoPositionInfoSourceAndroid::requestTimeout() const QGeoPositionInfo info = queuedSingleUpdates[i]; //anything newer by 20s is always better - const int timeDelta = best.timestamp().secsTo(info.timestamp()); + const qint64 timeDelta = best.timestamp().secsTo(info.timestamp()); if (abs(timeDelta) > 20) { if (timeDelta > 0) best = info; @@ -232,7 +232,7 @@ void QGeoPositionInfoSourceAndroid::requestTimeout() //compare accuracy if (info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy) && - info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) + best.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) { best = info.attribute(QGeoPositionInfo::HorizontalAccuracy) < best.attribute(QGeoPositionInfo::HorizontalAccuracy) ? info : best; diff --git a/src/plugins/position/corelocation/qgeopositioninfosource_cl.mm b/src/plugins/position/corelocation/qgeopositioninfosource_cl.mm index 94c5b807..6435963b 100644 --- a/src/plugins/position/corelocation/qgeopositioninfosource_cl.mm +++ b/src/plugins/position/corelocation/qgeopositioninfosource_cl.mm @@ -47,16 +47,16 @@ #define MINIMUM_UPDATE_INTERVAL 1000 @interface PositionLocationDelegate : NSObject <CLLocationManagerDelegate> +@end + +@implementation PositionLocationDelegate { QGeoPositionInfoSourceCL *m_positionInfoSource; } -@end -@implementation PositionLocationDelegate -- (id)initWithInfoSource:(QGeoPositionInfoSourceCL*) positionInfoSource +- (instancetype)initWithInfoSource:(QGeoPositionInfoSourceCL*) positionInfoSource { - self = [super init]; - if (self) { + if ((self = [self init])) { m_positionInfoSource = positionInfoSource; } return self; @@ -140,7 +140,39 @@ void QGeoPositionInfoSourceCL::setUpdateInterval(int msec) bool QGeoPositionInfoSourceCL::enableLocationManager() { if (!m_locationManager) { - m_locationManager = [[CLLocationManager alloc] init]; + if ([CLLocationManager locationServicesEnabled]) { + // Location Services Are Enabled + switch ([CLLocationManager authorizationStatus]) { + case kCLAuthorizationStatusNotDetermined: + // User has not yet made a choice with regards to this application + break; + case kCLAuthorizationStatusRestricted: + // This application is not authorized to use location services. Due + // to active restrictions on location services, the user cannot change + // this status, and may not have personally denied authorization + return false; + case kCLAuthorizationStatusDenied: + // User has explicitly denied authorization for this application, or + // location services are disabled in Settings + return false; + case kCLAuthorizationStatusAuthorizedAlways: + // This app is authorized to start location services at any time. + break; +#ifndef Q_OS_MACOS + case kCLAuthorizationStatusAuthorizedWhenInUse: + // This app is authorized to start most location services while running in the foreground. + break; +#endif + default: + // By default, try to enable it + break; + } + } else { + // Location Services Disabled + return false; + } + + m_locationManager = [[CLLocationManager alloc] init]; #if defined(Q_OS_IOS) || defined(Q_OS_WATCHOS) if (__builtin_available(watchOS 4.0, *)) { @@ -162,18 +194,29 @@ bool QGeoPositionInfoSourceCL::enableLocationManager() m_locationManager.desiredAccuracy = kCLLocationAccuracyBest; m_locationManager.delegate = [[PositionLocationDelegate alloc] initWithInfoSource:this]; - // These two methods are new in iOS 8. They require NSLocationAlwaysUsageDescription - // and NSLocationWhenInUseUsageDescription to be set in Info.plist to work (methods are - // noop if there are no such entries in plist). + // -requestAlwaysAuthorization is available on iOS (>= 8.0) and watchOS (>= 2.0). + // This method requires both NSLocationAlwaysAndWhenInUseUsageDescription and + // NSLocationWhenInUseUsageDescription entries present in Info.plist (otherwise, + // while probably a noop, the call generates a warning). + // -requestWhenInUseAuthorization only requires NSLocationWhenInUseUsageDescription + // entry in Info.plist (available on iOS (>= 8.0), tvOS (>= 9.0) and watchOS (>= 2.0). + #ifndef Q_OS_MACOS + NSDictionary<NSString *, id> *infoDict = NSBundle.mainBundle.infoDictionary; + const bool hasAlwaysUseUsage = !![infoDict objectForKey:@"NSLocationAlwaysAndWhenInUseUsageDescription"]; + const bool hasWhenInUseUsage = !![infoDict objectForKey:@"NSLocationWhenInUseUsageDescription"]; #ifndef Q_OS_TVOS - [m_locationManager requestAlwaysAuthorization]; -#endif - [m_locationManager requestWhenInUseAuthorization]; -#endif + if (hasAlwaysUseUsage && hasWhenInUseUsage) + [m_locationManager requestAlwaysAuthorization]; + else +#endif // !Q_OS_TVOS + if (hasWhenInUseUsage) + [m_locationManager requestWhenInUseAuthorization]; +#endif // !Q_OS_MACOS + } - return (m_locationManager != 0); + return (m_locationManager != nullptr); } void QGeoPositionInfoSourceCL::setTimeoutInterval(int msec) diff --git a/src/plugins/position/geoclue2/geoclue2.pro b/src/plugins/position/geoclue2/geoclue2.pro new file mode 100644 index 00000000..1bd129d5 --- /dev/null +++ b/src/plugins/position/geoclue2/geoclue2.pro @@ -0,0 +1,30 @@ +TARGET = qtposition_geoclue2 + +QT = core positioning dbus + +HEADERS += \ + qgeopositioninfosource_geoclue2_p.h \ + qgeopositioninfosourcefactory_geoclue2.h \ + geocluetypes.h + +SOURCES += \ + qgeopositioninfosource_geoclue2.cpp \ + qgeopositioninfosourcefactory_geoclue2.cpp \ + geocluetypes.cpp + +QDBUSXML2CPP_INTERFACE_HEADER_FLAGS += \ + "-N -i geocluetypes.h" + +DBUS_INTERFACES += \ + org.freedesktop.GeoClue2.Manager.xml \ + org.freedesktop.GeoClue2.Client.xml \ + org.freedesktop.GeoClue2.Location.xml + +INCLUDEPATH += $$QT.location.includes $$OUT_PWD + +OTHER_FILES += \ + plugin.json + +PLUGIN_TYPE = position +PLUGIN_CLASS_NAME = QGeoPositionInfoSourceFactoryGeoclue2 +load(qt_plugin) diff --git a/src/plugins/position/geoclue2/geocluetypes.cpp b/src/plugins/position/geoclue2/geocluetypes.cpp new file mode 100644 index 00000000..dec55d3c --- /dev/null +++ b/src/plugins/position/geoclue2/geocluetypes.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtPositioning 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 "geocluetypes.h" + +QT_BEGIN_NAMESPACE + +QDBusArgument &operator<<(QDBusArgument &arg, const Timestamp &ts) +{ + arg.beginStructure(); + arg << ts.m_seconds; + arg << ts.m_microseconds; + arg.endStructure(); + return arg; +} + +const QDBusArgument &operator>>(const QDBusArgument &arg, Timestamp &ts) +{ + arg.beginStructure(); + arg >> ts.m_seconds; + arg >> ts.m_microseconds; + arg.endStructure(); + return arg; +} + +QT_END_NAMESPACE diff --git a/src/plugins/position/geoclue2/geocluetypes.h b/src/plugins/position/geoclue2/geocluetypes.h new file mode 100644 index 00000000..0f3eb522 --- /dev/null +++ b/src/plugins/position/geoclue2/geocluetypes.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtPositioning 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 GEOCLUETYPES_H +#define GEOCLUETYPES_H + +#include <QtDBus/QDBusArgument> + +class Timestamp +{ +public: + quint64 m_seconds = 0; + quint64 m_microseconds = 0; +}; + +Q_DECLARE_METATYPE(Timestamp) + +QT_BEGIN_NAMESPACE + +Q_DECLARE_TYPEINFO(Timestamp, Q_MOVABLE_TYPE); + +QDBusArgument &operator<<(QDBusArgument &arg, const Timestamp &ts); +const QDBusArgument &operator>>(const QDBusArgument &arg, Timestamp &ts); + +QT_END_NAMESPACE + +#endif // GEOCLUETYPES_H diff --git a/src/plugins/position/geoclue2/org.freedesktop.GeoClue2.Client.xml b/src/plugins/position/geoclue2/org.freedesktop.GeoClue2.Client.xml new file mode 100644 index 00000000..4a9399b7 --- /dev/null +++ b/src/plugins/position/geoclue2/org.freedesktop.GeoClue2.Client.xml @@ -0,0 +1,122 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" +"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> + +<!-- + GeoClue 2.0 Interface Specification + + Copyright 2013 Red Hat, Inc. +--> + +<node> + + <!-- + org.freedesktop.GeoClue2.Client: + @short_description: The Application-specific client API + + This is the interface you use to retrieve location information and receive + location update signals from GeoClue service. You get the client object to + use this interface on from org.freedesktop.GeoClue2.Manager.GetClient() + method. + --> + <interface name="org.freedesktop.GeoClue2.Client"> + <!-- + Location: + + Current location as path to a #org.freedesktop.GeoClue2.Location object. + Please note that this property will be set to "/" (D-Bus equivalent of + null) initially, until Geoclue finds user's location. You want to delay + reading this property until your callback to + #org.freedesktop.GeoClue2.Client::LocationUpdated signal is called for + the first time after starting the client. + --> + <property name="Location" type="o" access="read"/> + + <!-- + DistanceThreshold: + + Contains the current distance threshold in meters. This value is used + by the service when it gets new location info. If the distance moved is + below the threshold, it won't emit the LocationUpdated signal. + The default value is 0. When TimeThreshold is zero, it always emits + the signal. + --> + <property name="DistanceThreshold" type="u" access="readwrite"> + <annotation name="org.freedesktop.Accounts.DefaultValue" value="0"/> + </property> + + <!-- + TimeThreshold: + + Contains the current time threshold in seconds. This value is used + by the service when it gets new location info. If the time since the + last update is below the threshold, it won't emit the LocationUpdated + signal. The default value is 0. When TimeThreshold is zero, it always + emits the signal. + --> + <property name="TimeThreshold" type="u" access="readwrite"> + <annotation name="org.freedesktop.Accounts.DefaultValue" value="0"/> + </property> + + <!-- + DesktopId: + + The desktop file id (the basename of the desktop file). This property + must be set by applications for authorization to work. + --> + <property name="DesktopId" type="s" access="readwrite"/> + + <!-- + RequestedAccuracyLevel: + + The level of accuracy requested by client, as + <link linkend="GClueAccuracyLevel">GClueAccuracyLevel</link>. + + Please keep in mind that the actual accuracy of location information is + dependent on available hardware on your machine, external resources + and/or how much accuracy user agrees to be confortable with. + --> + <property name="RequestedAccuracyLevel" type="u" access="readwrite"/> + + <!-- + Active: + + If client is active, i-e started successfully using + org.freedesktop.GeoClue2.Client.Start() and receiving location updates. + + Please keep in mind that geoclue can at any time stop and start the + client on user (agent) request. Applications that are interested in + in these changes, should watch for changes in this property. + --> + <property name="Active" type="b" access="read"/> + + <!-- + Start: + + Start receiving events about the current location. Applications should + hook-up to #org.freedesktop.GeoClue2.Client::LocationUpdated signal + before calling this method. + --> + <method name="Start"/> + + <!-- + Stop: + + Stop receiving events about the current location. + --> + <method name="Stop"/> + + <!-- + LocationUpdated: + @old: old location as path to a #org.freedesktop.GeoClue2.Location object + @new: new location as path to a #org.freedesktop.GeoClue2.Location object + + The signal is emitted every time the location changes. + The client should set the DistanceThreshold property to control how + often this signal is emitted. + --> + <signal name="LocationUpdated"> + <arg name="oldLocation" type="o"/> + <arg name="newLocation" type="o"/> + </signal> + </interface> +</node> diff --git a/src/plugins/position/geoclue2/org.freedesktop.GeoClue2.Location.xml b/src/plugins/position/geoclue2/org.freedesktop.GeoClue2.Location.xml new file mode 100644 index 00000000..ebf2ea6b --- /dev/null +++ b/src/plugins/position/geoclue2/org.freedesktop.GeoClue2.Location.xml @@ -0,0 +1,96 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" +"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> + +<!-- + GeoClue 2.0 Interface Specification + + Copyright 2013 Red Hat, Inc. +--> + +<node> + + <!-- + org.freedesktop.GeoClue2.Location: + @short_description: The Location interface + + This is the interface you use on location objects. + --> + <interface name="org.freedesktop.GeoClue2.Location"> + + <!-- + Latitude: + + The latitude of the location, in degrees. + --> + <property name="Latitude" type="d" access="read"/> + + <!-- + Longitude: + + The longitude of the location, in degrees. + --> + <property name="Longitude" type="d" access="read"/> + + <!-- + Accuracy: + + The accuracy of the location fix, in meters. + --> + <property name="Accuracy" type="d" access="read"/> + + <!-- + Altitude: + + The altitude of the location fix, in meters. When unknown, its set to + minimum double value, -1.7976931348623157e+308. + --> + <property name="Altitude" type="d" access="read"/> + + <!-- + Speed: + + The speed in meters per second. When unknown, it's set to -1.0. + --> + <property name="Speed" type="d" access="read"/> + + <!-- + Heading: + + The heading direction in degrees with respect to North direction, in + clockwise order. That means North becomes 0 degree, East: 90 degrees, + South: 180 degrees, West: 270 degrees and so on. When unknown, + it's set to -1.0. + --> + <property name="Heading" type="d" access="read"/> + + <!-- + Description: + + A human-readable description of the location, if available. + + WARNING: Applications should not rely on this property since not all + sources provide a description. If you really need a description (or + more details) about current location, use a reverse-geocoding API, e.g + geocode-glib. + --> + <property name="Description" type="s" access="read"/> + + + <!-- + Timestamp: + + The timestamp when the location was determined, in seconds and + microseconds since the Epoch. This is the time of measurement if the + backend provided that information, otherwise the time when GeoClue + received the new location. + + Note that GeoClue can't guarantee that the timestamp will always + monotonically increase, as a backend may not respect that. + Also note that a timestamp can be very old, e.g. because of a cached + location. + --> + <property name="Timestamp" type="(tt)" access="read"> + <annotation name="org.qtproject.QtDBus.QtTypeName" value="Timestamp"/> + </property> + </interface> +</node> diff --git a/src/plugins/position/geoclue2/org.freedesktop.GeoClue2.Manager.xml b/src/plugins/position/geoclue2/org.freedesktop.GeoClue2.Manager.xml new file mode 100644 index 00000000..cf9590f6 --- /dev/null +++ b/src/plugins/position/geoclue2/org.freedesktop.GeoClue2.Manager.xml @@ -0,0 +1,60 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" +"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> + +<!-- + GeoClue 2.0 Interface Specification + + Copyright 2013 Red Hat, Inc. +--> + +<node> + + <!-- + org.freedesktop.GeoClue2.Manager: + @short_description: The GeoClue service manager + + This is the interface you use to talk to main GeoClue2 manager object at + path "/org/freedesktop/GeoClue2/Manager". The only thing you do with this + interface is to call org.freedesktop.GeoClue2.Manager.GetClient() on it + to get your application specific client object. + --> + <interface name="org.freedesktop.GeoClue2.Manager"> + <!-- + InUse: + + Whether service is currently is use by any application. + --> + <property name="InUse" type="b" access="read"/> + + <!-- + AvailableAccuracyLevel: + + The level of available accuracy, as + <link linkend="GClueAccuracyLevel">GClueAccuracyLevel</link>. + --> + <property name="AvailableAccuracyLevel" type="u" access="read"/> + + <!-- + GetClient: + @client: The path for newly created client object + + Retrieves a client object which can only be used by the calling + application only. + --> + <method name="GetClient"> + <arg name="client" type="o" direction="out"/> + </method> + + <!-- + AddAgent: + @id: The Desktop ID (excluding .desktop) of the agent + + An API for user authorization agents to register themselves. Each agent + is responsible for the user it is running as. Application developers + can and should simply ignore this API. + --> + <method name="AddAgent"> + <arg name="id" type="s" direction="in"/> + </method> + </interface> +</node> diff --git a/src/plugins/position/geoclue2/plugin.json b/src/plugins/position/geoclue2/plugin.json new file mode 100644 index 00000000..c23d40e0 --- /dev/null +++ b/src/plugins/position/geoclue2/plugin.json @@ -0,0 +1,9 @@ +{ + "Keys": ["geoclue2"], + "Provider": "geoclue2", + "Position": true, + "Satellite": false, + "Monitor": false, + "Priority": 1000, + "Testable": false +} diff --git a/src/plugins/position/geoclue2/qgeopositioninfosource_geoclue2.cpp b/src/plugins/position/geoclue2/qgeopositioninfosource_geoclue2.cpp new file mode 100644 index 00000000..10484e3b --- /dev/null +++ b/src/plugins/position/geoclue2/qgeopositioninfosource_geoclue2.cpp @@ -0,0 +1,443 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtPositioning 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 "qgeopositioninfosource_geoclue2_p.h" + +#include <QtCore/QLoggingCategory> +#include <QtCore/QSaveFile> +#include <QtCore/QScopedPointer> +#include <QtCore/QTimer> +#include <QtDBus/QDBusPendingCallWatcher> + +// Auto-generated D-Bus files. +#include <client_interface.h> +#include <location_interface.h> + +Q_DECLARE_LOGGING_CATEGORY(lcPositioningGeoclue2) + +QT_BEGIN_NAMESPACE + +namespace { + +// NOTE: Copied from the /usr/include/libgeoclue-2.0/gclue-client.h +enum GClueAccuracyLevel { + GCLUE_ACCURACY_LEVEL_NONE = 0, + GCLUE_ACCURACY_LEVEL_COUNTRY = 1, + GCLUE_ACCURACY_LEVEL_CITY = 4, + GCLUE_ACCURACY_LEVEL_NEIGHBORHOOD = 5, + GCLUE_ACCURACY_LEVEL_STREET = 6, + GCLUE_ACCURACY_LEVEL_EXACT = 8 +}; + +const char GEOCLUE2_SERVICE_NAME[] = "org.freedesktop.GeoClue2"; +const int MINIMUM_UPDATE_INTERVAL = 1000; +const int UPDATE_TIMEOUT_COLD_START = 120000; + +static QString lastPositionFilePath() +{ + return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + + QStringLiteral("/qtposition-geoclue2"); +} + +} // namespace + +QGeoPositionInfoSourceGeoclue2::QGeoPositionInfoSourceGeoclue2(QObject *parent) + : QGeoPositionInfoSource(parent) + , m_requestTimer(new QTimer(this)) + , m_manager(QLatin1String(GEOCLUE2_SERVICE_NAME), + QStringLiteral("/org/freedesktop/GeoClue2/Manager"), + QDBusConnection::systemBus(), + this) +{ + qDBusRegisterMetaType<Timestamp>(); + + restoreLastPosition(); + + m_requestTimer->setSingleShot(true); + connect(m_requestTimer, &QTimer::timeout, + this, &QGeoPositionInfoSourceGeoclue2::requestUpdateTimeout); +} + +QGeoPositionInfoSourceGeoclue2::~QGeoPositionInfoSourceGeoclue2() +{ + saveLastPosition(); +} + +void QGeoPositionInfoSourceGeoclue2::setUpdateInterval(int msec) +{ + QGeoPositionInfoSource::setUpdateInterval(msec); + configureClient(); +} + +QGeoPositionInfo QGeoPositionInfoSourceGeoclue2::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const +{ + if (fromSatellitePositioningMethodsOnly && !m_lastPositionFromSatellite) + return QGeoPositionInfo(); + return m_lastPosition; +} + +QGeoPositionInfoSourceGeoclue2::PositioningMethods QGeoPositionInfoSourceGeoclue2::supportedPositioningMethods() const +{ + bool ok; + const auto accuracy = m_manager.property("AvailableAccuracyLevel").toUInt(&ok); + if (!ok) { + const_cast<QGeoPositionInfoSourceGeoclue2 *>(this)->setError(AccessError); + return NoPositioningMethods; + } + + switch (accuracy) { + case GCLUE_ACCURACY_LEVEL_COUNTRY: + case GCLUE_ACCURACY_LEVEL_CITY: + case GCLUE_ACCURACY_LEVEL_NEIGHBORHOOD: + case GCLUE_ACCURACY_LEVEL_STREET: + return NonSatellitePositioningMethods; + case GCLUE_ACCURACY_LEVEL_EXACT: + return AllPositioningMethods; + case GCLUE_ACCURACY_LEVEL_NONE: + default: + return NoPositioningMethods; + } +} + +void QGeoPositionInfoSourceGeoclue2::setPreferredPositioningMethods(PositioningMethods methods) +{ + QGeoPositionInfoSource::setPreferredPositioningMethods(methods); + configureClient(); +} + +int QGeoPositionInfoSourceGeoclue2::minimumUpdateInterval() const +{ + return MINIMUM_UPDATE_INTERVAL; +} + +QGeoPositionInfoSource::Error QGeoPositionInfoSourceGeoclue2::error() const +{ + return m_error; +} + +void QGeoPositionInfoSourceGeoclue2::startUpdates() +{ + if (m_running) { + qCWarning(lcPositioningGeoclue2) << "Already running"; + return; + } + + qCDebug(lcPositioningGeoclue2) << "Starting updates"; + m_running = true; + + startClient(); + + if (m_lastPosition.isValid()) { + QMetaObject::invokeMethod(this, "positionUpdated", Qt::QueuedConnection, + Q_ARG(QGeoPositionInfo, m_lastPosition)); + } +} + +void QGeoPositionInfoSourceGeoclue2::stopUpdates() +{ + if (!m_running) { + qCWarning(lcPositioningGeoclue2) << "Already stopped"; + return; + } + + qCDebug(lcPositioningGeoclue2) << "Stopping updates"; + m_running = false; + + stopClient(); +} + +void QGeoPositionInfoSourceGeoclue2::requestUpdate(int timeout) +{ + if (timeout < minimumUpdateInterval() && timeout != 0) { + emit updateTimeout(); + return; + } + + if (m_requestTimer->isActive()) { + qCDebug(lcPositioningGeoclue2) << "Request timer was active, ignoring startUpdates"; + return; + } + + m_requestTimer->start(timeout ? timeout : UPDATE_TIMEOUT_COLD_START); + startClient(); +} + +void QGeoPositionInfoSourceGeoclue2::setError(QGeoPositionInfoSource::Error error) +{ + m_error = error; + emit QGeoPositionInfoSource::error(m_error); +} + +void QGeoPositionInfoSourceGeoclue2::restoreLastPosition() +{ +#if !defined(QT_NO_DATASTREAM) + const auto filePath = lastPositionFilePath(); + QFile file(filePath); + if (file.open(QIODevice::ReadOnly)) { + QDataStream out(&file); + out >> m_lastPosition; + } +#endif +} + +void QGeoPositionInfoSourceGeoclue2::saveLastPosition() +{ +#if !defined(QT_NO_DATASTREAM) && QT_CONFIG(temporaryfile) + if (!m_lastPosition.isValid()) + return; + + const auto filePath = lastPositionFilePath(); + QSaveFile file(filePath); + if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + QDataStream out(&file); + // Only save position and timestamp. + out << QGeoPositionInfo(m_lastPosition.coordinate(), m_lastPosition.timestamp()); + file.commit(); + } +#endif +} + +void QGeoPositionInfoSourceGeoclue2::createClient() +{ + const QDBusPendingReply<QDBusObjectPath> reply = m_manager.GetClient(); + const auto watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, &QDBusPendingCallWatcher::finished, + [this](QDBusPendingCallWatcher *watcher) { + const QScopedPointer<QDBusPendingCallWatcher, QScopedPointerDeleteLater> + scopedWatcher(watcher); + const QDBusPendingReply<QDBusObjectPath> reply = *scopedWatcher; + if (reply.isError()) { + const auto error = reply.error(); + qCWarning(lcPositioningGeoclue2) << "Unable to obtain the client patch:" + << error.name() + error.message(); + setError(AccessError); + } else { + const QString clientPath = reply.value().path(); + qCDebug(lcPositioningGeoclue2) << "Client path is:" + << clientPath; + delete m_client; + m_client = new OrgFreedesktopGeoClue2ClientInterface( + QLatin1String(GEOCLUE2_SERVICE_NAME), + clientPath, + QDBusConnection::systemBus(), + this); + if (!m_client->isValid()) { + const auto error = m_client->lastError(); + qCCritical(lcPositioningGeoclue2) << "Unable to create the client object:" + << error.name() << error.message(); + setError(AccessError); + delete m_client; + } else { + connect(m_client.data(), &OrgFreedesktopGeoClue2ClientInterface::LocationUpdated, + this, &QGeoPositionInfoSourceGeoclue2::handleNewLocation); + + if (configureClient()) + startClient(); + } + } + }); +} + +void QGeoPositionInfoSourceGeoclue2::startClient() +{ + // only start the client if someone asked for it already + if (!m_running && !m_requestTimer->isActive()) + return; + + if (!m_client) { + createClient(); + return; + } + + const QDBusPendingReply<> reply = m_client->Start(); + const auto watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, &QDBusPendingCallWatcher::finished, + [this](QDBusPendingCallWatcher *watcher) { + const QScopedPointer<QDBusPendingCallWatcher, QScopedPointerDeleteLater> + scopedWatcher(watcher); + const QDBusPendingReply<> reply = *scopedWatcher; + if (reply.isError()) { + const auto error = reply.error(); + qCCritical(lcPositioningGeoclue2) << "Unable to start the client:" + << error.name() << error.message(); + setError(AccessError); + delete m_client; + } else { + qCDebug(lcPositioningGeoclue2) << "Client successfully started"; + + const QDBusObjectPath location = m_client->location(); + const QString path = location.path(); + if (path.isEmpty() || path == QLatin1String("/")) + return; + + handleNewLocation({}, location); + } + }); +} + +void QGeoPositionInfoSourceGeoclue2::stopClient() +{ + // Only stop client if updates are no longer wanted. + if (m_requestTimer->isActive() || m_running || !m_client) + return; + + const QDBusPendingReply<> reply = m_client->Stop(); + const auto watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, &QDBusPendingCallWatcher::finished, + [this](QDBusPendingCallWatcher *watcher) { + const QScopedPointer<QDBusPendingCallWatcher, QScopedPointerDeleteLater> + scopedWatcher(watcher); + const QDBusPendingReply<> reply = *scopedWatcher; + if (reply.isError()) { + const auto error = reply.error(); + qCCritical(lcPositioningGeoclue2) << "Unable to stop the client:" + << error.name() << error.message(); + setError(AccessError); + } else { + qCDebug(lcPositioningGeoclue2) << "Client successfully stopped"; + } + delete m_client; + }); +} + +bool QGeoPositionInfoSourceGeoclue2::configureClient() +{ + if (!m_client) + return false; + + auto desktopId = QString::fromUtf8(qgetenv("QT_GEOCLUE_APP_DESKTOP_ID")); + if (desktopId.isEmpty()) + desktopId = QCoreApplication::applicationName(); + if (desktopId.isEmpty()) { + qCCritical(lcPositioningGeoclue2) << "Unable to configure the client " + "due to the application desktop id " + "is not set via QT_GEOCLUE_APP_DESKTOP_ID " + "envirorment variable or QCoreApplication::applicationName"; + setError(AccessError); + return false; + } + + m_client->setDesktopId(desktopId); + + const auto msecs = updateInterval(); + const uint secs = qMax(uint(msecs), 0u) / 1000u; + m_client->setTimeThreshold(secs); + + const auto methods = preferredPositioningMethods(); + switch (methods) { + case SatellitePositioningMethods: + m_client->setRequestedAccuracyLevel(GCLUE_ACCURACY_LEVEL_EXACT); + break; + case NonSatellitePositioningMethods: + m_client->setRequestedAccuracyLevel(GCLUE_ACCURACY_LEVEL_STREET); + break; + case AllPositioningMethods: + m_client->setRequestedAccuracyLevel(GCLUE_ACCURACY_LEVEL_EXACT); + break; + default: + m_client->setRequestedAccuracyLevel(GCLUE_ACCURACY_LEVEL_NONE); + break; + } + + return true; +} + +void QGeoPositionInfoSourceGeoclue2::requestUpdateTimeout() +{ + qCDebug(lcPositioningGeoclue2) << "Request update timeout occurred"; + + emit updateTimeout(); + + stopClient(); +} + +void QGeoPositionInfoSourceGeoclue2::handleNewLocation(const QDBusObjectPath &oldLocation, + const QDBusObjectPath &newLocation) +{ + if (m_requestTimer->isActive()) + m_requestTimer->stop(); + + const auto oldPath = oldLocation.path(); + const auto newPath = newLocation.path(); + qCDebug(lcPositioningGeoclue2) << "Old location object path:" << oldPath; + qCDebug(lcPositioningGeoclue2) << "New location object path:" << newPath; + + OrgFreedesktopGeoClue2LocationInterface location( + QLatin1String(GEOCLUE2_SERVICE_NAME), + newPath, + QDBusConnection::systemBus(), + this); + if (!location.isValid()) { + const auto error = location.lastError(); + qCCritical(lcPositioningGeoclue2) << "Unable to create the location object:" + << error.name() << error.message(); + } else { + QGeoCoordinate coordinate(location.latitude(), + location.longitude()); + if (const auto altitude = location.altitude() > std::numeric_limits<double>::min()) + coordinate.setAltitude(altitude); + + const Timestamp ts = location.timestamp(); + if (ts.m_seconds == 0 && ts.m_microseconds == 0) { + const auto dt = QDateTime::currentDateTime(); + m_lastPosition = QGeoPositionInfo(coordinate, dt); + } else { + auto dt = QDateTime::fromSecsSinceEpoch(qint64(ts.m_seconds)); + dt = dt.addMSecs(ts.m_microseconds / 1000); + m_lastPosition = QGeoPositionInfo(coordinate, dt); + } + + const auto accuracy = location.accuracy(); + // We assume that an accuracy as 0.0 means that it comes from a sattelite. + m_lastPositionFromSatellite = qFuzzyCompare(accuracy, 0.0); + + m_lastPosition.setAttribute(QGeoPositionInfo::HorizontalAccuracy, accuracy); + if (const auto speed = location.speed() >= 0.0) + m_lastPosition.setAttribute(QGeoPositionInfo::GroundSpeed, speed); + if (const auto heading = location.heading() >= 0.0) + m_lastPosition.setAttribute(QGeoPositionInfo::Direction, heading); + + emit positionUpdated(m_lastPosition); + qCDebug(lcPositioningGeoclue2) << "New position:" << m_lastPosition; + } + + stopClient(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/position/geoclue2/qgeopositioninfosource_geoclue2_p.h b/src/plugins/position/geoclue2/qgeopositioninfosource_geoclue2_p.h new file mode 100644 index 00000000..16f5b9a1 --- /dev/null +++ b/src/plugins/position/geoclue2/qgeopositioninfosource_geoclue2_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtPositioning 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 QGEOPOSITIONINFOSOURCE_GEOCLUE2_P_H +#define QGEOPOSITIONINFOSOURCE_GEOCLUE2_P_H + +#include <QtPositioning/QGeoPositionInfoSource> +#include <QtCore/QPointer> +#include <manager_interface.h> + +class OrgFreedesktopGeoClue2ClientInterface; + +QT_BEGIN_NAMESPACE +class QDBusObjectPath; +class QTimer; + +class QGeoPositionInfoSourceGeoclue2 : public QGeoPositionInfoSource +{ + Q_OBJECT + +public: + explicit QGeoPositionInfoSourceGeoclue2(QObject *parent = nullptr); + ~QGeoPositionInfoSourceGeoclue2(); + + // From QGeoPositionInfoSource + 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 override; + + void startUpdates() override; + void stopUpdates() override; + void requestUpdate(int timeout = 5000) override; + +private: + void setError(QGeoPositionInfoSource::Error error); + void restoreLastPosition(); + void saveLastPosition(); + void createClient(); + bool configureClient(); + void startClient(); + void stopClient(); + void requestUpdateTimeout(); + void handleNewLocation(const QDBusObjectPath &oldLocation, + const QDBusObjectPath &newLocation); + + QTimer *m_requestTimer = nullptr; + OrgFreedesktopGeoClue2ManagerInterface m_manager; + QPointer<OrgFreedesktopGeoClue2ClientInterface> m_client; + bool m_running = false; + bool m_lastPositionFromSatellite = false; + QGeoPositionInfoSource::Error m_error = NoError; + QGeoPositionInfo m_lastPosition; +}; + +QT_END_NAMESPACE + +#endif // QGEOPOSITIONINFOSOURCE_GEOCLUE2_P_H diff --git a/src/plugins/position/geoclue2/qgeopositioninfosourcefactory_geoclue2.cpp b/src/plugins/position/geoclue2/qgeopositioninfosourcefactory_geoclue2.cpp new file mode 100644 index 00000000..a713c31f --- /dev/null +++ b/src/plugins/position/geoclue2/qgeopositioninfosourcefactory_geoclue2.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtPositioning 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 "qgeopositioninfosource_geoclue2_p.h" +#include "qgeopositioninfosourcefactory_geoclue2.h" + +#include <QtCore/QLoggingCategory> + +Q_LOGGING_CATEGORY(lcPositioningGeoclue2, "qt.positioning.geoclue2") + +QT_BEGIN_NAMESPACE + +QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryGeoclue2::positionInfoSource(QObject *parent) +{ + return new QGeoPositionInfoSourceGeoclue2(parent); +} + +QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryGeoclue2::satelliteInfoSource(QObject *parent) +{ + Q_UNUSED(parent) + return nullptr; +} + +QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryGeoclue2::areaMonitor(QObject *parent) +{ + Q_UNUSED(parent) + return nullptr; +} + +QT_END_NAMESPACE diff --git a/src/plugins/position/geoclue2/qgeopositioninfosourcefactory_geoclue2.h b/src/plugins/position/geoclue2/qgeopositioninfosourcefactory_geoclue2.h new file mode 100644 index 00000000..bfacd848 --- /dev/null +++ b/src/plugins/position/geoclue2/qgeopositioninfosourcefactory_geoclue2.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Denis Shienkov <denis.shienkov@gmail.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtPositioning 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 QGEOPOSITIONINFOSOURCEFACTORY_GEOCLUE2_H +#define QGEOPOSITIONINFOSOURCEFACTORY_GEOCLUE2_H + +#include <QtCore/QObject> +#include <QtPositioning/QGeoPositionInfoSourceFactory> + +QT_BEGIN_NAMESPACE + +/* + Qt Positioning plugin for Geoclue. This plugin supports Geoclue version 2.x. +*/ +class QGeoPositionInfoSourceFactoryGeoclue2 : public QObject, public QGeoPositionInfoSourceFactory +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/5.0" + FILE "plugin.json") + + Q_INTERFACES(QGeoPositionInfoSourceFactory) + +public: + QGeoPositionInfoSource *positionInfoSource(QObject *parent) override; + QGeoSatelliteInfoSource *satelliteInfoSource(QObject *parent) override; + QGeoAreaMonitorSource *areaMonitor(QObject *parent) override; +}; + +QT_END_NAMESPACE + +#endif // QGEOPOSITIONINFOSOURCEFACTORY_GEOCLUE2_H diff --git a/src/plugins/position/position.pro b/src/plugins/position/position.pro index b9832ff4..6800d5a3 100644 --- a/src/plugins/position/position.pro +++ b/src/plugins/position/position.pro @@ -2,7 +2,8 @@ TEMPLATE = subdirs QT_FOR_CONFIG += positioning-private -qtHaveModule(dbus):SUBDIRS += geoclue +linux:qtHaveModule(dbus):SUBDIRS += geoclue +linux:qtHaveModule(dbus):SUBDIRS += geoclue2 qtConfig(gypsy):SUBDIRS += gypsy qtConfig(winrt_geolocation):SUBDIRS += winrt qtHaveModule(simulator):SUBDIRS += simulator diff --git a/src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp b/src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp index 046d862e..139a6b3d 100644 --- a/src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp +++ b/src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp @@ -36,11 +36,12 @@ #include "qgeopositioninfosource_winrt_p.h" -#include <QCoreApplication> -#include <QMutex> -#include <qfunctions_winrt.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qfunctions_winrt.h> +#include <QtCore/qloggingcategory.h> +#include <QtCore/qmutex.h> #ifdef Q_OS_WINRT -#include <private/qeventdispatcher_winrt_p.h> +#include <QtCore/private/qeventdispatcher_winrt_p.h> #endif #include <functional> @@ -56,10 +57,11 @@ using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Foundation::Collections; typedef ITypedEventHandler<Geolocator *, PositionChangedEventArgs *> GeoLocatorPositionHandler; -typedef ITypedEventHandler<Geolocator *, StatusChangedEventArgs *> GeoLocatorStatusHandler; typedef IAsyncOperationCompletedHandler<Geoposition*> PositionHandler; typedef IAsyncOperationCompletedHandler<GeolocationAccessStatus> AccessHandler; +Q_DECLARE_LOGGING_CATEGORY(lcPositioningWinRT) + QT_BEGIN_NAMESPACE #ifndef Q_OS_WINRT @@ -70,7 +72,36 @@ HRESULT runOnXamlThread(const std::function<HRESULT ()> &delegate, bool waitForR return delegate(); } } -#endif + +static inline HRESULT await(const ComPtr<IAsyncOperation<GeolocationAccessStatus>> &asyncOp, + GeolocationAccessStatus *result) +{ + ComPtr<IAsyncInfo> asyncInfo; + HRESULT hr = asyncOp.As(&asyncInfo); + if (FAILED(hr)) + return hr; + + AsyncStatus status; + while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started) + QThread::yieldCurrentThread(); + + if (FAILED(hr) || status != AsyncStatus::Completed) { + HRESULT ec; + hr = asyncInfo->get_ErrorCode(&ec); + if (FAILED(hr)) + return hr; + hr = asyncInfo->Close(); + if (FAILED(hr)) + return hr; + return ec; + } + + if (FAILED(hr)) + return hr; + + return asyncOp->GetResults(result); +} +#endif // !Q_OS_WINRT class QGeoPositionInfoSourceWinRTPrivate { public: @@ -83,13 +114,37 @@ public: EventRegistrationToken positionToken; QMutex mutex; bool updatesOngoing; + int minimumUpdateInterval; + + PositionStatus nativeStatus() const; }; +PositionStatus QGeoPositionInfoSourceWinRTPrivate::nativeStatus() const +{ +#ifdef Q_OS_WINRT + qCDebug(lcPositioningWinRT) << __FUNCTION__; + + PositionStatus status; + HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([this, &status]() { + return locator->get_LocationStatus(&status); + }); + if (FAILED(hr)) { + qErrnoWarning(hr, "Could not query status"); + return PositionStatus_NotAvailable; + } + return status; +#else + return PositionStatus_Ready; +#endif +} + QGeoPositionInfoSourceWinRT::QGeoPositionInfoSourceWinRT(QObject *parent) : QGeoPositionInfoSource(parent) , d_ptr(new QGeoPositionInfoSourceWinRTPrivate) { + qCDebug(lcPositioningWinRT) << __FUNCTION__; + CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); Q_D(QGeoPositionInfoSourceWinRT); d->positionError = QGeoPositionInfoSource::NoError; d->updatesOngoing = false; @@ -97,10 +152,13 @@ QGeoPositionInfoSourceWinRT::QGeoPositionInfoSourceWinRT(QObject *parent) QGeoPositionInfoSourceWinRT::~QGeoPositionInfoSourceWinRT() { + qCDebug(lcPositioningWinRT) << __FUNCTION__; + CoUninitialize(); } int QGeoPositionInfoSourceWinRT::init() { + qCDebug(lcPositioningWinRT) << __FUNCTION__; Q_D(QGeoPositionInfoSourceWinRT); if (!requestAccess()) { qWarning ("Location access failed."); @@ -111,13 +169,11 @@ int QGeoPositionInfoSourceWinRT::init() &d->locator); RETURN_HR_IF_FAILED("Could not initialize native location services."); - hr = d->locator->add_StatusChanged(Callback<GeoLocatorStatusHandler>(this, - &QGeoPositionInfoSourceWinRT::onStatusChanged).Get(), - &d->statusToken); - RETURN_HR_IF_FAILED("Could not add status callback."); - - hr = d->locator->put_ReportInterval(1000); - RETURN_HR_IF_FAILED("Could not initialize report interval."); + UINT32 interval; + hr = d->locator->get_ReportInterval(&interval); + RETURN_HR_IF_FAILED("Could not retrieve report interval."); + d->minimumUpdateInterval = static_cast<int>(interval); + setUpdateInterval(d->minimumUpdateInterval); return hr; }); @@ -137,7 +193,6 @@ int QGeoPositionInfoSourceWinRT::init() d->positionToken.value = 0; d->periodicTimer.setSingleShot(true); - d->periodicTimer.setInterval(minimumUpdateInterval()); connect(&d->periodicTimer, &QTimer::timeout, this, &QGeoPositionInfoSourceWinRT::virtualPositionUpdate); d->singleUpdateTimer.setSingleShot(true); @@ -151,6 +206,7 @@ int QGeoPositionInfoSourceWinRT::init() QGeoPositionInfo QGeoPositionInfoSourceWinRT::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const { + qCDebug(lcPositioningWinRT) << __FUNCTION__; Q_D(const QGeoPositionInfoSourceWinRT); Q_UNUSED(fromSatellitePositioningMethodsOnly) return d->lastPosition; @@ -160,13 +216,8 @@ QGeoPositionInfoSource::PositioningMethods QGeoPositionInfoSourceWinRT::supporte { Q_D(const QGeoPositionInfoSourceWinRT); - PositionStatus status; - HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([d, &status]() { - HRESULT hr = d->locator->get_LocationStatus(&status); - return hr; - }); - if (FAILED(hr)) - return QGeoPositionInfoSource::NoPositioningMethods; + PositionStatus status = d->nativeStatus(); + qCDebug(lcPositioningWinRT) << __FUNCTION__ << status; switch (status) { case PositionStatus::PositionStatus_NoData: @@ -180,6 +231,7 @@ QGeoPositionInfoSource::PositioningMethods QGeoPositionInfoSourceWinRT::supporte void QGeoPositionInfoSourceWinRT::setPreferredPositioningMethods(QGeoPositionInfoSource::PositioningMethods methods) { + qCDebug(lcPositioningWinRT) << __FUNCTION__ << methods; Q_D(QGeoPositionInfoSourceWinRT); PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods(); @@ -187,7 +239,7 @@ void QGeoPositionInfoSourceWinRT::setPreferredPositioningMethods(QGeoPositionInf if (previousPreferredPositioningMethods == preferredPositioningMethods()) return; - bool needsRestart = d->positionToken.value != 0; + const bool needsRestart = d->positionToken.value != 0; if (needsRestart) stopHandler(); @@ -207,19 +259,20 @@ void QGeoPositionInfoSourceWinRT::setPreferredPositioningMethods(QGeoPositionInf void QGeoPositionInfoSourceWinRT::setUpdateInterval(int msec) { + qCDebug(lcPositioningWinRT) << __FUNCTION__ << msec; Q_D(QGeoPositionInfoSourceWinRT); - // Windows Phone 8.1 and Windows 10 do not support 0 interval -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) - if (msec == 0) - msec = minimumUpdateInterval(); -#endif - - // If msec is 0 we send updates as data becomes available, otherwise we force msec to be equal - // to or larger than the minimum update interval. + // minimumUpdateInterval is initialized to the lowest possible update interval in init(). + // Passing 0 will cause an error on Windows 10. + // See https://docs.microsoft.com/en-us/uwp/api/windows.devices.geolocation.geolocator.reportinterval if (msec != 0 && msec < minimumUpdateInterval()) msec = minimumUpdateInterval(); - HRESULT hr = d->locator->put_ReportInterval(msec); + const bool needsRestart = d->positionToken.value != 0; + + if (needsRestart) + stopHandler(); + + HRESULT hr = d->locator->put_ReportInterval(static_cast<UINT32>(msec)); if (FAILED(hr)) { setError(QGeoPositionInfoSource::UnknownSourceError); qErrnoWarning(hr, "Failed to set update interval"); @@ -229,17 +282,20 @@ void QGeoPositionInfoSourceWinRT::setUpdateInterval(int msec) d->periodicTimer.setInterval(qMax(msec, minimumUpdateInterval())); QGeoPositionInfoSource::setUpdateInterval(msec); + + if (needsRestart) + startHandler(); } int QGeoPositionInfoSourceWinRT::minimumUpdateInterval() const { - // We use one second to reduce potential timer events - // in case the platform itself stops reporting - return 1000; + Q_D(const QGeoPositionInfoSourceWinRT); + return d->minimumUpdateInterval; } void QGeoPositionInfoSourceWinRT::startUpdates() { + qCDebug(lcPositioningWinRT) << __FUNCTION__; Q_D(QGeoPositionInfoSourceWinRT); if (d->updatesOngoing) @@ -253,6 +309,7 @@ void QGeoPositionInfoSourceWinRT::startUpdates() void QGeoPositionInfoSourceWinRT::stopUpdates() { + qCDebug(lcPositioningWinRT) << __FUNCTION__; Q_D(QGeoPositionInfoSourceWinRT); stopHandler(); @@ -262,6 +319,7 @@ void QGeoPositionInfoSourceWinRT::stopUpdates() bool QGeoPositionInfoSourceWinRT::startHandler() { + qCDebug(lcPositioningWinRT) << __FUNCTION__; Q_D(QGeoPositionInfoSourceWinRT); // Check if already attached @@ -301,6 +359,7 @@ bool QGeoPositionInfoSourceWinRT::startHandler() void QGeoPositionInfoSourceWinRT::stopHandler() { + qCDebug(lcPositioningWinRT) << __FUNCTION__; Q_D(QGeoPositionInfoSourceWinRT); if (!d->positionToken.value) @@ -314,6 +373,7 @@ void QGeoPositionInfoSourceWinRT::stopHandler() void QGeoPositionInfoSourceWinRT::requestUpdate(int timeout) { + qCDebug(lcPositioningWinRT) << __FUNCTION__; Q_D(QGeoPositionInfoSourceWinRT); if (timeout != 0 && timeout < minimumUpdateInterval()) { @@ -330,6 +390,7 @@ void QGeoPositionInfoSourceWinRT::requestUpdate(int timeout) void QGeoPositionInfoSourceWinRT::virtualPositionUpdate() { + qCDebug(lcPositioningWinRT) << __FUNCTION__; Q_D(QGeoPositionInfoSourceWinRT); QMutexLocker locker(&d->mutex); @@ -367,6 +428,7 @@ void QGeoPositionInfoSourceWinRT::singleUpdateTimeOut() void QGeoPositionInfoSourceWinRT::updateSynchronized(QGeoPositionInfo currentInfo) { + qCDebug(lcPositioningWinRT) << __FUNCTION__ << currentInfo; Q_D(QGeoPositionInfoSourceWinRT); QMutexLocker locker(&d->mutex); @@ -388,11 +450,13 @@ void QGeoPositionInfoSourceWinRT::updateSynchronized(QGeoPositionInfo currentInf QGeoPositionInfoSource::Error QGeoPositionInfoSourceWinRT::error() const { Q_D(const QGeoPositionInfoSourceWinRT); + qCDebug(lcPositioningWinRT) << __FUNCTION__ << d->positionError; return d->positionError; } void QGeoPositionInfoSourceWinRT::setError(QGeoPositionInfoSource::Error positionError) { + qCDebug(lcPositioningWinRT) << __FUNCTION__ << positionError; Q_D(QGeoPositionInfoSourceWinRT); if (positionError == d->positionError) @@ -404,14 +468,9 @@ void QGeoPositionInfoSourceWinRT::setError(QGeoPositionInfoSource::Error positio bool QGeoPositionInfoSourceWinRT::checkNativeState() { Q_D(QGeoPositionInfoSourceWinRT); + qCDebug(lcPositioningWinRT) << __FUNCTION__; - PositionStatus status; - HRESULT hr = d->locator->get_LocationStatus(&status); - if (FAILED(hr)) { - setError(QGeoPositionInfoSource::UnknownSourceError); - qErrnoWarning(hr, "Could not query status"); - return false; - } + PositionStatus status = d->nativeStatus(); bool result = false; switch (status) { @@ -432,41 +491,53 @@ bool QGeoPositionInfoSourceWinRT::checkNativeState() HRESULT QGeoPositionInfoSourceWinRT::onPositionChanged(IGeolocator *locator, IPositionChangedEventArgs *args) { + qCDebug(lcPositioningWinRT) << __FUNCTION__; Q_UNUSED(locator); HRESULT hr; - ComPtr<IGeoposition> pos; - hr = args->get_Position(&pos); + ComPtr<IGeoposition> position; + hr = args->get_Position(&position); RETURN_HR_IF_FAILED("Could not access position object."); QGeoPositionInfo currentInfo; ComPtr<IGeocoordinate> coord; - hr = pos->get_Coordinate(&coord); + hr = position->get_Coordinate(&coord); if (FAILED(hr)) qErrnoWarning(hr, "Could not access coordinate"); - DOUBLE lat; - hr = coord->get_Latitude(&lat); + ComPtr<IGeocoordinateWithPoint> pointCoordinate; + hr = coord.As(&pointCoordinate); if (FAILED(hr)) - qErrnoWarning(hr, "Could not access latitude"); + qErrnoWarning(hr, "Could not cast coordinate."); - DOUBLE lon; - hr = coord->get_Longitude(&lon); + ComPtr<IGeopoint> point; + hr = pointCoordinate->get_Point(&point); if (FAILED(hr)) - qErrnoWarning(hr, "Could not access longitude"); - - // Depending on data source altitude can - // be identified or not - IReference<double> *alt; - hr = coord->get_Altitude(&alt); - if (SUCCEEDED(hr) && alt) { - double altd; - hr = alt->get_Value(&altd); - currentInfo.setCoordinate(QGeoCoordinate(lat, lon, altd)); - } else { - currentInfo.setCoordinate(QGeoCoordinate(lat, lon)); + qErrnoWarning(hr, "Could not obtain coordinate's point."); + + BasicGeoposition pos; + hr = point->get_Position(&pos); + if (FAILED(hr)) + qErrnoWarning(hr, "Could not obtain point's position."); + + DOUBLE lat = pos.Latitude; + DOUBLE lon = pos.Longitude; + DOUBLE alt = pos.Altitude; + + bool altitudeAvailable = false; + ComPtr<IGeoshape> shape; + hr = point.As(&shape); + if (SUCCEEDED(hr) && shape) { + AltitudeReferenceSystem altitudeSystem; + hr = shape->get_AltitudeReferenceSystem(&altitudeSystem); + if (SUCCEEDED(hr) && altitudeSystem == AltitudeReferenceSystem_Geoid) + altitudeAvailable = true; } + if (altitudeAvailable) + currentInfo.setCoordinate(QGeoCoordinate(lat, lon, alt)); + else + currentInfo.setCoordinate(QGeoCoordinate(lat, lon)); DOUBLE accuracy; hr = coord->get_Accuracy(&accuracy); @@ -477,7 +548,7 @@ HRESULT QGeoPositionInfoSourceWinRT::onPositionChanged(IGeolocator *locator, IPo hr = coord->get_AltitudeAccuracy(&altAccuracy); if (SUCCEEDED(hr) && altAccuracy) { double value; - hr = alt->get_Value(&value); + hr = altAccuracy->get_Value(&value); currentInfo.setAttribute(QGeoPositionInfo::VerticalAccuracy, value); } @@ -525,16 +596,9 @@ HRESULT QGeoPositionInfoSourceWinRT::onPositionChanged(IGeolocator *locator, IPo return S_OK; } -HRESULT QGeoPositionInfoSourceWinRT::onStatusChanged(IGeolocator*, IStatusChangedEventArgs *args) -{ - PositionStatus st; - args->get_Status(&st); - return S_OK; -} - bool QGeoPositionInfoSourceWinRT::requestAccess() const { -#ifdef Q_OS_WINRT + qCDebug(lcPositioningWinRT) << __FUNCTION__; static GeolocationAccessStatus accessStatus = GeolocationAccessStatus_Unspecified; static ComPtr<IGeolocatorStatics> statics; @@ -557,11 +621,12 @@ bool QGeoPositionInfoSourceWinRT::requestAccess() const Q_ASSERT_SUCCEEDED(hr); // We cannot wait inside the XamlThread as that would deadlock +#ifdef Q_OS_WINRT QWinRTFunctions::await(op, &accessStatus); +#else + await(op, &accessStatus); +#endif return accessStatus == GeolocationAccessStatus_Allowed; -#else // Q_OS_WINRT - return true; -#endif // Q_OS_WINRT } QT_END_NAMESPACE diff --git a/src/plugins/position/winrt/qgeopositioninfosource_winrt_p.h b/src/plugins/position/winrt/qgeopositioninfosource_winrt_p.h index 9f3a1c7f..4319ccae 100644 --- a/src/plugins/position/winrt/qgeopositioninfosource_winrt_p.h +++ b/src/plugins/position/winrt/qgeopositioninfosource_winrt_p.h @@ -51,7 +51,7 @@ #include "qgeopositioninfosource.h" #include "qgeopositioninfo.h" -#include <QTimer> +#include <QtCore/qtimer.h> #include <EventToken.h> #include <wrl.h> @@ -62,7 +62,6 @@ namespace ABI { namespace Geolocation{ struct IGeolocator; struct IPositionChangedEventArgs; - struct IStatusChangedEventArgs; } } } @@ -76,7 +75,7 @@ class QGeoPositionInfoSourceWinRT : public QGeoPositionInfoSource { Q_OBJECT public: - QGeoPositionInfoSourceWinRT(QObject *parent = 0); + QGeoPositionInfoSourceWinRT(QObject *parent = nullptr); ~QGeoPositionInfoSourceWinRT(); int init(); @@ -92,9 +91,6 @@ public: HRESULT onPositionChanged(ABI::Windows::Devices::Geolocation::IGeolocator *locator, ABI::Windows::Devices::Geolocation::IPositionChangedEventArgs *args); - HRESULT onStatusChanged(ABI::Windows::Devices::Geolocation::IGeolocator*, - ABI::Windows::Devices::Geolocation::IStatusChangedEventArgs *args); - bool requestAccess() const; Q_SIGNALS: void nativePositionUpdate(const QGeoPositionInfo); diff --git a/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.cpp b/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.cpp index e58744a0..b1ec6fb3 100644 --- a/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.cpp +++ b/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.cpp @@ -37,24 +37,37 @@ #include "qgeopositioninfosourcefactory_winrt.h" #include "qgeopositioninfosource_winrt_p.h" +#include <QtCore/qloggingcategory.h> + +Q_LOGGING_CATEGORY(lcPositioningWinRT, "qt.positioning.winrt") + +QT_BEGIN_NAMESPACE + QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryWinRT::positionInfoSource(QObject *parent) { + qCDebug(lcPositioningWinRT) << __FUNCTION__; QGeoPositionInfoSourceWinRT *src = new QGeoPositionInfoSourceWinRT(parent); if (src->init() < 0) { + qCDebug(lcPositioningWinRT) << __FUNCTION__ << "Source initialization failed."; delete src; - src = 0; + return nullptr; } + qCDebug(lcPositioningWinRT) << __FUNCTION__ << "Created position info source."; return src; } QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryWinRT::satelliteInfoSource(QObject *parent) { + qCDebug(lcPositioningWinRT) << __FUNCTION__; Q_UNUSED(parent); - return 0; + return nullptr; } QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryWinRT::areaMonitor(QObject *parent) { + qCDebug(lcPositioningWinRT) << __FUNCTION__; Q_UNUSED(parent); - return 0; + return nullptr; } + +QT_END_NAMESPACE diff --git a/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.h b/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.h index 46cd3853..d09ddb64 100644 --- a/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.h +++ b/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.h @@ -37,8 +37,8 @@ #ifndef QGEOPOSITIONINFOSOURCEFACTORY_WINRT_H #define QGEOPOSITIONINFOSOURCEFACTORY_WINRT_H -#include <QObject> -#include <QGeoPositionInfoSourceFactory> +#include <QtCore/qobject.h> +#include <QtPositioning/qgeopositioninfosourcefactory.h> QT_BEGIN_NAMESPACE |