Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
placemanagerengine_esri.cpp
Go to the documentation of this file.
1// Copyright (C) 2013-2018 Esri <contracts@esri.com>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
7
8#include <QJsonDocument>
9#include <QJsonObject>
10#include <QJsonArray>
11#include <QtCore/QUrlQuery>
12
13#include <QtPositioning/QGeoRectangle>
14#include <QtPositioning/QGeoShape>
15
16#include <QtLocation/QPlaceCategory>
17#include <QtLocation/QPlaceSearchRequest>
18
20
21// https://developers.arcgis.com/rest/geocode/api-reference/geocoding-find-address-candidates.htm
22// https://developers.arcgis.com/rest/geocode/api-reference/geocoding-category-filtering.htm
23// https://developers.arcgis.com/rest/geocode/api-reference/geocoding-service-output.htm
24
25static const QString kCategoriesKey(QStringLiteral("categories"));
26static const QString kSingleLineKey(QStringLiteral("singleLine"));
27static const QString kLocationKey(QStringLiteral("location"));
28static const QString kNameKey(QStringLiteral("name"));
29static const QString kOutFieldsKey(QStringLiteral("outFields"));
30static const QString kCandidateFieldsKey(QStringLiteral("candidateFields"));
31static const QString kCountriesKey(QStringLiteral("detailedCountries"));
32static const QString kLocalizedNamesKey(QStringLiteral("localizedNames"));
33static const QString kMaxLocationsKey(QStringLiteral("maxLocations"));
34
36 "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer?f=pjson");
37static const QUrl kUrlFindAddressCandidates("https://geocode.arcgis.com/arcgis/rest/services/World/"
38 "GeocodeServer/findAddressCandidates");
39
41 QString *errorString) :
42 QPlaceManagerEngine(parameters),
43 m_networkManager(new QNetworkAccessManager(this))
44{
46 errorString->clear();
47}
48
52
53QList<QLocale> PlaceManagerEngineEsri::locales() const
54{
55 return m_locales;
56}
57
58void PlaceManagerEngineEsri::setLocales(const QList<QLocale> &locales)
59{
60 m_locales = locales;
61}
62
63/***** Search *****/
64
66{
67 bool unsupported = false;
68
69 // Only public visibility supported
70 unsupported |= request.visibilityScope() != QLocation::UnspecifiedVisibility &&
71 request.visibilityScope() != QLocation::PublicVisibility;
72 unsupported |= request.searchTerm().isEmpty() && request.categories().isEmpty();
73
74 if (unsupported)
76
77 QUrlQuery queryItems;
78 queryItems.addQueryItem(QStringLiteral("f"), QStringLiteral("json"));
79
80 const QGeoCoordinate center = request.searchArea().center();
81 if (center.isValid())
82 {
83 const QString location = QString("%1,%2").arg(center.longitude()).arg(center.latitude());
85 }
86
87 const QGeoRectangle boundingBox = request.searchArea().boundingGeoRectangle();
88 if (!boundingBox.isEmpty())
89 {
90 const QString searchExtent = QString("%1,%2,%3,%4")
91 .arg(boundingBox.topLeft().longitude())
92 .arg(boundingBox.topLeft().latitude())
93 .arg(boundingBox.bottomRight().longitude())
94 .arg(boundingBox.bottomRight().latitude());
95 queryItems.addQueryItem(QStringLiteral("searchExtent"), searchExtent);
96 }
97
98 if (!request.searchTerm().isEmpty())
99 queryItems.addQueryItem(kSingleLineKey, request.searchTerm());
100
102 if (!request.categories().isEmpty())
103 {
104 for (const QPlaceCategory &placeCategory : request.categories())
105 categories.append(placeCategory.categoryId());
106 queryItems.addQueryItem("category", categories.join(","));
107 }
108
109 if (request.limit() > 0)
111
112 queryItems.addQueryItem(kOutFieldsKey, QStringLiteral("*"));
113
115 requestUrl.setQuery(queryItems);
116
117 QNetworkRequest networkRequest(requestUrl);
119 QNetworkReply *networkReply = m_networkManager->get(networkRequest);
120
121 PlaceSearchReplyEsri *reply = new PlaceSearchReplyEsri(request, networkReply, m_candidateFieldsLocale, m_countriesLocale, this);
123 this, &PlaceManagerEngineEsri::replyFinished);
125 this, &PlaceManagerEngineEsri::replyError);
126
127 return reply;
128}
129
130void PlaceManagerEngineEsri::replyFinished()
131{
132 QPlaceReply *reply = qobject_cast<QPlaceReply *>(sender());
133 if (reply)
135}
136
137void PlaceManagerEngineEsri::replyError(QPlaceReply::Error errorCode, const QString &errorString)
138{
139 QPlaceReply *reply = qobject_cast<QPlaceReply *>(sender());
140 if (reply)
141 emit errorOccurred(reply, errorCode, errorString);
142}
143
144/***** Categories *****/
145
147{
148 initializeGeocodeServer();
149
152 this, &PlaceManagerEngineEsri::replyFinished);
154 this, &PlaceManagerEngineEsri::replyError);
155
156 // TODO delayed finished() emission
157 if (!m_categories.isEmpty())
158 reply->emitFinished();
159
160 m_pendingCategoriesReply.append(reply);
161 return reply;
162}
163
164void PlaceManagerEngineEsri::parseCategories(const QJsonArray &jsonArray, const QString &parentCategoryId)
165{
166 for (const QJsonValueConstRef jsonValue : jsonArray)
167 {
168 if (!jsonValue.isObject())
169 continue;
170
171 const QJsonObject jsonCategory = jsonValue.toObject();
172 const QString key = jsonCategory.value(kNameKey).toString();
173 const QString localeName = localizedName(jsonCategory);
174
175 if (key.isEmpty())
176 continue;
177
179 category.setCategoryId(key);
180 category.setName(localeName.isEmpty() ? key : localeName); // localizedNames
181 m_categories.insert(key, category);
182 m_subcategories[parentCategoryId].append(key);
183 m_parentCategory.insert(key, parentCategoryId);
185
186 if (jsonCategory.contains(kCategoriesKey))
187 {
188 const QJsonArray jsonArray = jsonCategory.value(kCategoriesKey).toArray();
189 parseCategories(jsonArray, key);
190 }
191 }
192}
193
195{
196 return m_parentCategory.value(categoryId);
197}
198
200{
201 return m_subcategories.value(categoryId);
202}
203
205{
206 return m_categories.value(categoryId);
207}
208
209QList<QPlaceCategory> PlaceManagerEngineEsri::childCategories(const QString &parentId) const
210{
211 QList<QPlaceCategory> categories;
212 for (const QString &id : m_subcategories.value(parentId))
213 categories.append(m_categories.value(id));
214 return categories;
215}
216
217void PlaceManagerEngineEsri::finishCategories()
218{
219 for (PlaceCategoriesReplyEsri *reply : m_pendingCategoriesReply)
220 reply->emitFinished();
221 m_pendingCategoriesReply.clear();
222}
223
224void PlaceManagerEngineEsri::errorCaterogies(const QString &error)
225{
226 for (PlaceCategoriesReplyEsri *reply : m_pendingCategoriesReply)
227 reply->setError(QPlaceReply::CommunicationError, error);
228}
229
230/***** GeocodeServer *****/
231
232void PlaceManagerEngineEsri::initializeGeocodeServer()
233{
234 // Only fetch categories once
235 if (m_categories.isEmpty() && !m_geocodeServerReply)
236 {
237 m_geocodeServerReply = m_networkManager->get(QNetworkRequest(kUrlGeocodeServer));
238 connect(m_geocodeServerReply, &QNetworkReply::finished,
239 this, &PlaceManagerEngineEsri::geocodeServerReplyFinished);
240 connect(m_geocodeServerReply, &QNetworkReply::errorOccurred,
241 this, &PlaceManagerEngineEsri::geocodeServerReplyError);
242 }
243}
244
245QString PlaceManagerEngineEsri::localizedName(const QJsonObject &jsonObject)
246{
247 const QJsonObject localizedNames = jsonObject.value(kLocalizedNamesKey).toObject();
248
249 for (const QLocale &locale : std::as_const(m_locales)) {
250 const QString localeStr = locale.name();
251 if (localizedNames.contains(localeStr))
252 {
253 return localizedNames.value(localeStr).toString();
254 }
255
256 const QString shortLocale = localeStr.left(2);
257 if (localizedNames.contains(shortLocale))
258 {
259 return localizedNames.value(shortLocale).toString();
260 }
261 }
262 return QString();
263}
264
265void PlaceManagerEngineEsri::parseCandidateFields(const QJsonArray &jsonArray)
266{
267 for (const QJsonValueConstRef jsonValue : jsonArray)
268 {
269 if (!jsonValue.isObject())
270 continue;
271
272 const QJsonObject jsonCandidateField = jsonValue.toObject();
273 if (!jsonCandidateField.contains(kLocalizedNamesKey))
274 continue;
275
276 const QString key = jsonCandidateField.value(kNameKey).toString();
277 m_candidateFieldsLocale.insert(key, localizedName(jsonCandidateField));
278 }
279}
280
281void PlaceManagerEngineEsri::parseCountries(const QJsonArray &jsonArray)
282{
283 for (const QJsonValueConstRef jsonValue : jsonArray)
284 {
285 if (!jsonValue.isObject())
286 continue;
287
288 const QJsonObject jsonCountry = jsonValue.toObject();
289 if (!jsonCountry.contains(kLocalizedNamesKey))
290 continue;
291
292 const QString key = jsonCountry.value(kNameKey).toString();
293 m_countriesLocale.insert(key, localizedName(jsonCountry));
294 }
295}
296
297void PlaceManagerEngineEsri::geocodeServerReplyFinished()
298{
299 if (!m_geocodeServerReply)
300 return;
301
302 QJsonDocument document = QJsonDocument::fromJson(m_geocodeServerReply->readAll());
303 if (!document.isObject())
304 {
305 errorCaterogies(m_geocodeServerReply->errorString());
306 return;
307 }
308
309 QJsonObject jsonObject = document.object();
310
311 // parse categories
312 if (jsonObject.contains(kCategoriesKey))
313 {
314 const QJsonArray jsonArray = jsonObject.value(kCategoriesKey).toArray();
315 parseCategories(jsonArray, QString());
316 }
317
318 // parse candidateFields
319 if (jsonObject.contains(kCandidateFieldsKey))
320 {
321 const QJsonArray jsonArray = jsonObject.value(kCandidateFieldsKey).toArray();
322 parseCandidateFields(jsonArray);
323 }
324
325 // parse countries
326 if (jsonObject.contains(kCountriesKey))
327 {
328 const QJsonArray jsonArray = jsonObject.value(kCountriesKey).toArray();
329 parseCountries(jsonArray);
330 }
331
332 finishCategories();
333
334 m_geocodeServerReply->deleteLater();
335}
336
337void PlaceManagerEngineEsri::geocodeServerReplyError()
338{
339 if (m_categories.isEmpty() && !m_geocodeServerReply)
340 return;
341
342 errorCaterogies(m_geocodeServerReply->errorString());
343}
344
QPlaceReply * initializeCategories() override
Initializes the categories of the manager engine.
QStringList childCategoryIds(const QString &categoryId) const override
Returns the child category identifiers of the category corresponding to categoryId.
QPlaceSearchReply * search(const QPlaceSearchRequest &request) override
Searches for places according to the parameters specified in request.
QList< QPlaceCategory > childCategories(const QString &parentId) const override
Returns a list of categories that are children of the category corresponding to parentId.
QList< QLocale > locales() const override
Returns a list of preferred locales.
void setLocales(const QList< QLocale > &locales) override
Set the list of preferred locales.
PlaceManagerEngineEsri(const QVariantMap &parameters, QGeoServiceProvider::Error *error, QString *errorString)
QPlaceCategory category(const QString &categoryId) const override
Returns the category corresponding to the given categoryId.
QString parentCategoryId(const QString &categoryId) const override
Returns the parent category identifier of the category corresponding to categoryId.
\inmodule QtPositioning
double longitude
This property holds the longitude in decimal degrees.
double latitude
This property holds the latitude in decimal degrees.
\inmodule QtPositioning
QGeoCoordinate topLeft
This property holds the top left coordinate of this geo rectangle.
QGeoCoordinate bottomRight
This property holds the bottom right coordinate of this geo rectangle.
Error
Describes an error related to the loading and setup of a service provider plugin.
bool isEmpty
This property defines whether this geo shape is empty.
Definition qgeoshape.h:21
T value(const Key &key) const noexcept
Definition qhash.h:1054
bool isEmpty() const noexcept
Returns true if the hash contains no items; otherwise returns false.
Definition qhash.h:928
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
QString errorString() const
Returns a human-readable description of the last device error that occurred.
\inmodule QtCore\reentrant
Definition qjsonarray.h:18
\inmodule QtCore\reentrant
static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error=nullptr)
Parses json as a UTF-8 encoded JSON document, and creates a QJsonDocument from it.
\inmodule QtCore\reentrant
Definition qjsonobject.h:20
QJsonValue value(const QString &key) const
Returns a QJsonValue representing the value for the key key.
QJsonObject toObject() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
void append(parameter_type t)
Definition qlist.h:458
void clear()
Definition qlist.h:434
The QNetworkAccessManager class allows the application to send network requests and receive replies.
QNetworkReply * get(const QNetworkRequest &request)
Posts a request to obtain the contents of the target request and returns a new QNetworkReply object o...
The QNetworkReply class contains the data and headers for a request sent with QNetworkAccessManager.
void errorOccurred(QNetworkReply::NetworkError)
void finished()
This signal is emitted when the reply has finished processing.
The QNetworkRequest class holds a request to be sent with QNetworkAccessManager.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
QObject * sender() const
Returns a pointer to the object that sent the signal, if called in a slot activated by a signal; othe...
Definition qobject.cpp:2658
void deleteLater()
\threadsafe
Definition qobject.cpp:2435
\inmodule QtLocation
\inmodule QtLocation
void finished(QPlaceReply *reply)
This signal is emitted when reply has finished processing.
virtual QPlaceSearchReply * search(const QPlaceSearchRequest &request)
Searches for places according to the parameters specified in request.
void errorOccurred(QPlaceReply *, QPlaceReply::Error error, const QString &errorString=QString())
This signal is emitted when an error has been detected in the processing of reply.
void categoryAdded(const QPlaceCategory &category, const QString &parentCategoryId)
This signal is emitted if a category has been added to the manager engine's datastore.
\inmodule QtLocation
Definition qplacereply.h:15
void errorOccurred(QPlaceReply::Error error, const QString &errorString=QString())
This signal is emitted when an error has been detected in the processing of this reply.
void finished()
This signal is emitted when this reply has finished processing.
Error
Describes an error which occurred during an operation.
Definition qplacereply.h:18
\inmodule QtLocation
\inmodule QtLocation
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString left(qsizetype n) const &
Definition qstring.h:363
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
\inmodule QtCore
Definition qurlquery.h:20
void addQueryItem(const QString &key, const QString &value)
Appends the pair key = value to the end of the query string of the URL.
\inmodule QtCore
Definition qurl.h:94
void setQuery(const QString &query, ParsingMode mode=TolerantMode)
Sets the query string of the URL to query.
Definition qurl.cpp:2550
#define this
Definition dialogs.cpp:9
const QLoggingCategory & category()
[1]
@ PublicVisibility
Definition qlocation.h:21
@ UnspecifiedVisibility
Definition qlocation.h:18
Q_QML_EXPORT QV4::ReturnedValue locale(QV4::ExecutionEngine *engine, const QString &localeName)
Provides locale specific properties and formatted data.
Combined button and popup list for selecting options.
emscripten::val document()
Definition qwasmdom.h:49
static const QString kSingleLineKey(QStringLiteral("singleLine"))
static const QString kCandidateFieldsKey(QStringLiteral("candidateFields"))
static const QString kCountriesKey(QStringLiteral("detailedCountries"))
static const QString kNameKey(QStringLiteral("name"))
static QT_BEGIN_NAMESPACE const QString kCategoriesKey(QStringLiteral("categories"))
static const QUrl kUrlFindAddressCandidates("https://geocode.arcgis.com/arcgis/rest/services/World/" "GeocodeServer/findAddressCandidates")
static const QString kMaxLocationsKey(QStringLiteral("maxLocations"))
static const QUrl kUrlGeocodeServer("https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer?f=pjson")
static const QString kLocalizedNamesKey(QStringLiteral("localizedNames"))
static const QString kLocationKey(QStringLiteral("location"))
static const QString kOutFieldsKey(QStringLiteral("outFields"))
DBusConnection const char DBusError * error
GLint location
GLuint64 key
GLsizei GLenum * categories
static void setError(QJsonObject *response, const QString &msg)
#define QStringLiteral(str)
#define emit
QNetworkRequest request(url)
QNetworkReply * reply