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>
21#include <QtCore/QElapsedTimer>
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");
58QPlaceManagerEngineMapbox::QPlaceManagerEngineMapbox(
const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString)
59 : QPlaceManagerEngine(parameters), m_networkManager(
new QNetworkAccessManager(
this))
61 if (parameters.contains(QStringLiteral(
"mapbox.useragent")))
62 m_userAgent = parameters.value(QStringLiteral(
"mapbox.useragent")).toString().toLatin1();
64 m_userAgent = mapboxDefaultUserAgent;
66 m_accessToken = parameters.value(QStringLiteral(
"mapbox.access_token")).toString();
68 m_isEnterprise = parameters.value(QStringLiteral(
"mapbox.enterprise")).toBool();
69 m_urlPrefix = m_isEnterprise ? mapboxGeocodingEnterpriseApiPath : mapboxGeocodingApiPath;
71 *error = QGeoServiceProvider::NoError;
81 return qobject_cast<QPlaceSearchReply *>(doSearch(request, PlaceSearchType::CompleteSearch));
86 return qobject_cast<QPlaceSearchSuggestionReply *>(doSearch(request, PlaceSearchType::SuggestionSearch));
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();
96 bool invalidRequest =
false;
100 invalidRequest |= request.visibilityScope().testFlag(QLocation::DeviceVisibility);
103 invalidRequest |= searchTerm.isEmpty() && placeCategories.isEmpty() && recommendationId.isEmpty();
106 invalidRequest |= searchTerm.isEmpty() && !placeCategories.isEmpty() && !recommendationId.isEmpty();
108 if (invalidRequest) {
110 if (searchType == PlaceSearchType::CompleteSearch)
113 reply =
new QPlaceSearchSuggestionReplyMapbox(0,
this);
115 connect(reply, &QPlaceReply::finished,
116 this, &QPlaceManagerEngineMapbox::onReplyFinished);
117 connect(reply, &QPlaceReply::errorOccurred,
118 this, &QPlaceManagerEngineMapbox::onReplyError);
120 QMetaObject::invokeMethod(reply,
"setError", Qt::QueuedConnection,
121 Q_ARG(QPlaceReply::Error, QPlaceReply::BadArgumentError),
122 Q_ARG(QString,
"Invalid request."));
128 if (!searchTerm.isEmpty()) {
129 queryString = searchTerm;
130 }
else if (!recommendationId.isEmpty()) {
131 queryString = recommendationId;
133 QStringList similarIds;
134 for (
const QPlaceCategory &placeCategory : placeCategories)
135 similarIds.append(placeCategory.categoryId());
136 queryString = similarIds.join(QLatin1Char(
','));
138 queryString.append(QStringLiteral(
".json"));
141 QUrl requestUrl(m_urlPrefix + queryString);
143 QUrlQuery queryItems;
144 queryItems.addQueryItem(QStringLiteral(
"access_token"), m_accessToken);
148 QStringList languageCodes;
149 for (
const QLocale& locale: std::as_const(m_locales)) {
155 if (locale.language() == QLocale::C)
158 const QString languageCode = locale.name().section(QLatin1Char(
'_'), 0, 0);
159 if (!languageCodes.contains(languageCode))
160 languageCodes.append(languageCode);
163 if (!languageCodes.isEmpty())
164 queryItems.addQueryItem(QStringLiteral(
"language"), languageCodes.join(QLatin1Char(
',')));
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()));
172 queryItems.addQueryItem(QStringLiteral(
"type"), QStringLiteral(
"poi"));
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()));
185 if (request.limit() > 0)
186 queryItems.addQueryItem(QStringLiteral(
"limit"), QString::number(request.limit()));
190 requestUrl.setQuery(queryItems);
192 QNetworkRequest networkRequest(requestUrl);
193 networkRequest.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent);
195 QNetworkReply *networkReply = m_networkManager->get(networkRequest);
197 if (searchType == PlaceSearchType::CompleteSearch)
200 reply =
new QPlaceSearchSuggestionReplyMapbox(networkReply,
this);
202 connect(reply, &QPlaceReply::finished,
203 this, &QPlaceManagerEngineMapbox::onReplyFinished);
204 connect(reply, &QPlaceReply::errorOccurred,
205 this, &QPlaceManagerEngineMapbox::onReplyError);
212 if (m_categories.isEmpty()) {
213 for (
const QString &categoryId : categories) {
214 QPlaceCategory category;
215 category.setName(QMapboxCommon::mapboxNameForCategory(categoryId));
216 category.setCategoryId(categoryId);
217 category.setVisibility(QLocation::PublicVisibility);
218 m_categories[categoryId] = category;
222 QPlaceCategoriesReplyMapbox *reply =
new QPlaceCategoriesReplyMapbox(
this);
223 connect(reply, &QPlaceReply::finished,
224 this, &QPlaceManagerEngineMapbox::onReplyFinished);
225 connect(reply, &QPlaceReply::errorOccurred,
226 this, &QPlaceManagerEngineMapbox::onReplyError);
229 QMetaObject::invokeMethod(reply,
"finish", Qt::QueuedConnection);
236 Q_UNUSED(categoryId);
245 if (categoryId.isEmpty())
246 return m_categories.keys();
248 return QStringList();
253 return m_categories.value(categoryId);
259 if (parentId.isEmpty())
260 return m_categories.values();
262 return QList<QPlaceCategory>();
277 QPlaceReply *reply = qobject_cast<QPlaceReply *>(sender());
279 emit finished(reply);
284 QPlaceReply *reply = qobject_cast<QPlaceReply *>(sender());
286 emit errorOccurred(reply, errorCode, errorString);
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()
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.