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
qplacemanagerenginemapbox.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 Mapbox, Inc.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
8#include "qmapboxcommon.h"
9
10#include <QtCore/QUrlQuery>
11#include <QtCore/QXmlStreamReader>
12#include <QtCore/QRegularExpression>
13#include <QtNetwork/QNetworkAccessManager>
14#include <QtNetwork/QNetworkRequest>
15#include <QtNetwork/QNetworkReply>
16#include <QtPositioning/QGeoCircle>
17#include <QtLocation/QPlaceCategory>
18#include <QtLocation/QPlaceSearchRequest>
19#include <QtLocation/private/unsupportedreplies_p.h>
20
21#include <QtCore/QElapsedTimer>
22
23namespace {
24
25// https://www.mapbox.com/api-documentation/#poi-categories
26static const QStringList categories = QStringList()
27 << QStringLiteral("bakery")
28 << QStringLiteral("bank")
29 << QStringLiteral("bar")
30 << QStringLiteral("cafe")
31 << QStringLiteral("church")
32 << QStringLiteral("cinema")
33 << QStringLiteral("coffee")
34 << QStringLiteral("concert")
35 << QStringLiteral("fast food")
36 << QStringLiteral("finance")
37 << QStringLiteral("gallery")
38 << QStringLiteral("historic")
39 << QStringLiteral("hotel")
40 << QStringLiteral("landmark")
41 << QStringLiteral("museum")
42 << QStringLiteral("music")
43 << QStringLiteral("park")
44 << QStringLiteral("pizza")
45 << QStringLiteral("restaurant")
46 << QStringLiteral("retail")
47 << QStringLiteral("school")
48 << QStringLiteral("shop")
49 << QStringLiteral("tea")
50 << QStringLiteral("theater")
51 << QStringLiteral("university");
52
53} // namespace
54
55// Mapbox API does not provide support for paginated place queries. This
56// implementation is a wrapper around its Geocoding service:
57// https://www.mapbox.com/api-documentation/#geocoding
59 : QPlaceManagerEngine(parameters), m_networkManager(new QNetworkAccessManager(this))
60{
61 if (parameters.contains(QStringLiteral("mapbox.useragent")))
62 m_userAgent = parameters.value(QStringLiteral("mapbox.useragent")).toString().toLatin1();
63 else
64 m_userAgent = mapboxDefaultUserAgent;
65
66 m_accessToken = parameters.value(QStringLiteral("mapbox.access_token")).toString();
67
68 m_isEnterprise = parameters.value(QStringLiteral("mapbox.enterprise")).toBool();
69 m_urlPrefix = m_isEnterprise ? mapboxGeocodingEnterpriseApiPath : mapboxGeocodingApiPath;
70
72 errorString->clear();
73}
74
78
80{
81 return qobject_cast<QPlaceSearchReply *>(doSearch(request, PlaceSearchType::CompleteSearch));
82}
83
85{
86 return qobject_cast<QPlaceSearchSuggestionReply *>(doSearch(request, PlaceSearchType::SuggestionSearch));
87}
88
89QPlaceReply *QPlaceManagerEngineMapbox::doSearch(const QPlaceSearchRequest &request, PlaceSearchType searchType)
90{
91 const QGeoShape searchArea = request.searchArea();
92 const QString searchTerm = request.searchTerm();
93 const QString recommendationId = request.recommendationId();
94 const QList<QPlaceCategory> placeCategories = request.categories();
95
96 bool invalidRequest = false;
97
98 // QLocation::DeviceVisibility is not allowed for non-enterprise accounts.
99 if (!m_isEnterprise)
100 invalidRequest |= request.visibilityScope().testFlag(QLocation::DeviceVisibility);
101
102 // Must provide either a search term, categories or recommendation.
103 invalidRequest |= searchTerm.isEmpty() && placeCategories.isEmpty() && recommendationId.isEmpty();
104
105 // Category search must not provide recommendation, and vice-versa.
106 invalidRequest |= searchTerm.isEmpty() && !placeCategories.isEmpty() && !recommendationId.isEmpty();
107
108 if (invalidRequest) {
110 if (searchType == PlaceSearchType::CompleteSearch)
111 reply = new QPlaceSearchReplyMapbox(request, 0, this);
112 else
114
116 this, &QPlaceManagerEngineMapbox::onReplyFinished);
118 this, &QPlaceManagerEngineMapbox::onReplyError);
119
122 Q_ARG(QString, "Invalid request."));
123
124 return reply;
125 }
126
127 QString queryString;
128 if (!searchTerm.isEmpty()) {
129 queryString = searchTerm;
130 } else if (!recommendationId.isEmpty()) {
131 queryString = recommendationId;
132 } else {
133 QStringList similarIds;
134 for (const QPlaceCategory &placeCategory : placeCategories)
135 similarIds.append(placeCategory.categoryId());
136 queryString = similarIds.join(QLatin1Char(','));
137 }
138 queryString.append(QStringLiteral(".json"));
139
140 // https://www.mapbox.com/api-documentation/#request-format
141 QUrl requestUrl(m_urlPrefix + queryString);
142
143 QUrlQuery queryItems;
144 queryItems.addQueryItem(QStringLiteral("access_token"), m_accessToken);
145
146 // XXX: Investigate situations where we need to filter by 'country'.
147
148 QStringList languageCodes;
149 for (const QLocale& locale: std::as_const(m_locales)) {
150 // Returns the language and country of this locale as a string of the
151 // form "language_country", where language is a lowercase, two-letter
152 // ISO 639 language code, and country is an uppercase, two- or
153 // three-letter ISO 3166 country code.
154
155 if (locale.language() == QLocale::C)
156 continue;
157
158 const QString languageCode = locale.name().section(QLatin1Char('_'), 0, 0);
159 if (!languageCodes.contains(languageCode))
160 languageCodes.append(languageCode);
161 }
162
163 if (!languageCodes.isEmpty())
164 queryItems.addQueryItem(QStringLiteral("language"), languageCodes.join(QLatin1Char(',')));
165
166 if (searchArea.type() != QGeoShape::UnknownType) {
167 const QGeoCoordinate center = searchArea.center();
168 queryItems.addQueryItem(QStringLiteral("proximity"),
169 QString::number(center.longitude()) + QLatin1Char(',') + QString::number(center.latitude()));
170 }
171
172 queryItems.addQueryItem(QStringLiteral("type"), QStringLiteral("poi"));
173
174 // XXX: Investigate situations where 'autocomplete' should be disabled.
175
176 QGeoRectangle boundingBox = searchArea.boundingGeoRectangle();
177 if (!boundingBox.isEmpty()) {
178 queryItems.addQueryItem(QStringLiteral("bbox"),
179 QString::number(boundingBox.topLeft().longitude()) + QLatin1Char(',') +
180 QString::number(boundingBox.bottomRight().latitude()) + QLatin1Char(',') +
181 QString::number(boundingBox.bottomRight().longitude()) + QLatin1Char(',') +
182 QString::number(boundingBox.topLeft().latitude()));
183 }
184
185 if (request.limit() > 0)
186 queryItems.addQueryItem(QStringLiteral("limit"), QString::number(request.limit()));
187
188 // XXX: Investigate searchContext() use cases.
189
190 requestUrl.setQuery(queryItems);
191
192 QNetworkRequest networkRequest(requestUrl);
193 networkRequest.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent);
194
195 QNetworkReply *networkReply = m_networkManager->get(networkRequest);
197 if (searchType == PlaceSearchType::CompleteSearch)
198 reply = new QPlaceSearchReplyMapbox(request, networkReply, this);
199 else
200 reply = new QPlaceSearchSuggestionReplyMapbox(networkReply, this);
201
203 this, &QPlaceManagerEngineMapbox::onReplyFinished);
205 this, &QPlaceManagerEngineMapbox::onReplyError);
206
207 return reply;
208}
209
211{
212 if (m_categories.isEmpty()) {
213 for (const QString &categoryId : categories) {
216 category.setCategoryId(categoryId);
218 m_categories[categoryId] = category;
219 }
220 }
221
224 this, &QPlaceManagerEngineMapbox::onReplyFinished);
226 this, &QPlaceManagerEngineMapbox::onReplyError);
227
228 // Queue a future finished() emission from the reply.
230
231 return reply;
232}
233
235{
236 Q_UNUSED(categoryId);
237
238 // Only a single category level.
239 return QString();
240}
241
243{
244 // Only a single category level.
245 if (categoryId.isEmpty())
246 return m_categories.keys();
247
248 return QStringList();
249}
250
252{
253 return m_categories.value(categoryId);
254}
255
256QList<QPlaceCategory> QPlaceManagerEngineMapbox::childCategories(const QString &parentId) const
257{
258 // Only a single category level.
259 if (parentId.isEmpty())
260 return m_categories.values();
261
262 return QList<QPlaceCategory>();
263}
264
266{
267 return m_locales;
268}
269
270void QPlaceManagerEngineMapbox::setLocales(const QList<QLocale> &locales)
271{
272 m_locales = locales;
273}
274
275void QPlaceManagerEngineMapbox::onReplyFinished()
276{
277 QPlaceReply *reply = qobject_cast<QPlaceReply *>(sender());
278 if (reply)
280}
281
282void QPlaceManagerEngineMapbox::onReplyError(QPlaceReply::Error errorCode, const QString &errorString)
283{
284 QPlaceReply *reply = qobject_cast<QPlaceReply *>(sender());
285 if (reply)
286 emit errorOccurred(reply, errorCode, errorString);
287}
\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.
\inmodule QtPositioning
Definition qgeoshape.h:17
ShapeType type
This property holds the type of this geo shape.
Definition qgeoshape.h:19
Q_INVOKABLE QGeoRectangle boundingGeoRectangle() const
Returns a QGeoRectangle representing the geographical bounding rectangle of the geo shape,...
@ UnknownType
Definition qgeoshape.h:31
bool isEmpty
This property defines whether this geo shape is empty.
Definition qgeoshape.h:21
QGeoCoordinate center
Definition qgeoshape.h:22
QList< T > values() const
Returns a list containing all the values in the hash, in an arbitrary order.
Definition qhash.h:1098
QList< Key > keys() const
Returns a list containing all the keys in the hash, in an arbitrary order.
Definition qhash.h:1086
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
T value(const Key &key, const T &defaultValue=T()) const
Definition qmap.h:357
bool contains(const Key &key) const
Definition qmap.h:341
static QString mapboxNameForCategory(const QString &category)
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.
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
\inmodule QtLocation
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.
QList< QLocale > locales() const override
Returns a list of preferred locales.
QPlaceManagerEngineMapbox(const QVariantMap &parameters, QGeoServiceProvider::Error *, QString *errorString)
QPlaceSearchReply * search(const QPlaceSearchRequest &) 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.
QString parentCategoryId(const QString &categoryId) const override
Returns the parent category identifier of the category corresponding to categoryId.
void setLocales(const QList< QLocale > &locales) override
Set the list of preferred locales.
QPlaceCategory category(const QString &categoryId) const override
Returns the category corresponding to the given categoryId.
QPlaceSearchSuggestionReply * searchSuggestions(const QPlaceSearchRequest &) override
Requests a set of search term suggestions according to the parameters specified in request.
\inmodule QtLocation
void finished(QPlaceReply *reply)
This signal is emitted when reply has finished processing.
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.
\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
QByteArray toLatin1() const &
Definition qstring.h:630
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
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
QString toString() const
Returns the variant as a QString if the variant has a userType() including, but not limited to:
bool toBool() const
Returns the variant as a bool if the variant has userType() Bool.
#define this
Definition dialogs.cpp:9
const QLoggingCategory & category()
[1]
list append(new Employee("Blackpool", "Stephen"))
@ PublicVisibility
Definition qlocation.h:21
@ DeviceVisibility
Definition qlocation.h:19
Q_QML_EXPORT QV4::ReturnedValue locale(QV4::ExecutionEngine *engine, const QString &localeName)
Provides locale specific properties and formatted data.
@ QueuedConnection
QTextStream & center(QTextStream &stream)
Calls QTextStream::setFieldAlignment(QTextStream::AlignCenter) on stream and returns stream.
QList< QString > QStringList
Constructs a string list that contains the given string, str.
DBusConnection const char DBusError * error
static const QByteArray mapboxDefaultUserAgent
static const QString mapboxGeocodingApiPath
static const QString mapboxGeocodingEnterpriseApiPath
#define Q_ARG(Type, data)
Definition qobjectdefs.h:63
GLsizei GLenum * categories
#define QStringLiteral(str)
#define emit
#define Q_UNUSED(x)
QNetworkRequest request(url)
QNetworkReply * reply
\inmodule QtCore \reentrant
Definition qchar.h:18
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...