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
qplacemanagerengineosm.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 Aaron McCarthy <mccarthy.aaron@gmail.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 <QtCore/QElapsedTimer>
9#include <QtCore/QLocale>
10#include <QtCore/QRegularExpression>
11#include <QtCore/QUrlQuery>
12#include <QtCore/QXmlStreamReader>
13
14#include <QtNetwork/QNetworkAccessManager>
15#include <QtNetwork/QNetworkRequest>
16#include <QtNetwork/QNetworkReply>
17
18#include <QtPositioning/QGeoCircle>
19
20#include <QtLocation/QPlaceCategory>
21#include <QtLocation/QPlaceSearchRequest>
22#include <QtLocation/private/unsupportedreplies_p.h>
23
24namespace
25{
26QString SpecialPhrasesBaseUrl = QStringLiteral("http://wiki.openstreetmap.org/wiki/Special:Export/Nominatim/Special_Phrases/");
27
28QString nameForTagKey(const QString &tagKey)
29{
30 if (tagKey == QLatin1String("aeroway"))
31 return QPlaceManagerEngineOsm::tr("Aeroway");
32 else if (tagKey == QLatin1String("amenity"))
33 return QPlaceManagerEngineOsm::tr("Amenity");
34 else if (tagKey == QLatin1String("building"))
35 return QPlaceManagerEngineOsm::tr("Building");
36 else if (tagKey == QLatin1String("highway"))
37 return QPlaceManagerEngineOsm::tr("Highway");
38 else if (tagKey == QLatin1String("historic"))
39 return QPlaceManagerEngineOsm::tr("Historic");
40 else if (tagKey == QLatin1String("landuse"))
41 return QPlaceManagerEngineOsm::tr("Land use");
42 else if (tagKey == QLatin1String("leisure"))
43 return QPlaceManagerEngineOsm::tr("Leisure");
44 else if (tagKey == QLatin1String("man_made"))
45 return QPlaceManagerEngineOsm::tr("Man made");
46 else if (tagKey == QLatin1String("natural"))
47 return QPlaceManagerEngineOsm::tr("Natural");
48 else if (tagKey == QLatin1String("place"))
49 return QPlaceManagerEngineOsm::tr("Place");
50 else if (tagKey == QLatin1String("railway"))
51 return QPlaceManagerEngineOsm::tr("Railway");
52 else if (tagKey == QLatin1String("shop"))
53 return QPlaceManagerEngineOsm::tr("Shop");
54 else if (tagKey == QLatin1String("tourism"))
55 return QPlaceManagerEngineOsm::tr("Tourism");
56 else if (tagKey == QLatin1String("waterway"))
57 return QPlaceManagerEngineOsm::tr("Waterway");
58 else
59 return tagKey;
60}
61
62}
63
66 QString *errorString)
67: QPlaceManagerEngine(parameters), m_networkManager(new QNetworkAccessManager(this)),
68 m_categoriesReply(0)
69{
70 if (parameters.contains(QStringLiteral("osm.useragent")))
71 m_userAgent = parameters.value(QStringLiteral("osm.useragent")).toString().toLatin1();
72 else
73 m_userAgent = "Qt Location based application";
74
75 if (parameters.contains(QStringLiteral("osm.places.host")))
76 m_urlPrefix = parameters.value(QStringLiteral("osm.places.host")).toString();
77 else
78 m_urlPrefix = QStringLiteral("http://nominatim.openstreetmap.org/search");
79
80
81 if (parameters.contains(QStringLiteral("osm.places.debug_query")))
82 m_debugQuery = parameters.value(QStringLiteral("osm.places.debug_query")).toBool();
83
84 if (parameters.contains(QStringLiteral("osm.places.page_size"))
85 && parameters.value(QStringLiteral("osm.places.page_size")).canConvert<int>())
86 m_pageSize = parameters.value(QStringLiteral("osm.places.page_size")).toInt();
87
89 errorString->clear();
90}
91
95
97{
98 bool unsupported = false;
99
100 // Only public visibility supported
101 unsupported |= request.visibilityScope() != QLocation::UnspecifiedVisibility &&
102 request.visibilityScope() != QLocation::PublicVisibility;
103 unsupported |= request.searchTerm().isEmpty() && request.categories().isEmpty();
104
105 if (unsupported)
107
108 QUrlQuery queryItems;
109
110 queryItems.addQueryItem(QStringLiteral("format"), QStringLiteral("jsonv2"));
111
112 //queryItems.addQueryItem(QStringLiteral("accept-language"), QStringLiteral("en"));
113
114 QGeoRectangle boundingBox = request.searchArea().boundingGeoRectangle();
115
116 if (!boundingBox.isEmpty()) {
117 queryItems.addQueryItem(QStringLiteral("bounded"), QStringLiteral("1"));
118 QString coordinates;
119 coordinates = QString::number(boundingBox.topLeft().longitude()) + QLatin1Char(',') +
120 QString::number(boundingBox.topLeft().latitude()) + QLatin1Char(',') +
121 QString::number(boundingBox.bottomRight().longitude()) + QLatin1Char(',') +
122 QString::number(boundingBox.bottomRight().latitude());
123 queryItems.addQueryItem(QStringLiteral("viewbox"), coordinates);
124 }
125
126 QStringList queryParts;
127 if (!request.searchTerm().isEmpty())
128 queryParts.append(request.searchTerm());
129
130 for (const QPlaceCategory &category : request.categories()) {
131 QString id = category.categoryId();
132 queryParts.append(QLatin1Char('[') + id + QLatin1Char(']'));
133 }
134
135 queryItems.addQueryItem(QStringLiteral("q"), queryParts.join(QLatin1Char('+')));
136
137 QVariantMap parameters = request.searchContext().toMap();
138
139 QStringList placeIds = parameters.value(QStringLiteral("ExcludePlaceIds")).toStringList();
140 if (!placeIds.isEmpty())
141 queryItems.addQueryItem(QStringLiteral("exclude_place_ids"), placeIds.join(QLatin1Char(',')));
142
143 queryItems.addQueryItem(QStringLiteral("addressdetails"), QStringLiteral("1"));
144 queryItems.addQueryItem(QStringLiteral("limit"), (request.limit() > 0) ? QString::number(request.limit())
145 : QString::number(m_pageSize));
146
147 QUrl requestUrl(m_urlPrefix);
148 requestUrl.setQuery(queryItems);
149
150 QNetworkRequest rq(requestUrl);
152 QNetworkReply *networkReply = m_networkManager->get(rq);
153
154 QPlaceSearchReplyOsm *reply = new QPlaceSearchReplyOsm(request, networkReply, this);
156 this, &QPlaceManagerEngineOsm::replyFinished);
158 this, &QPlaceManagerEngineOsm::replyError);
159
160 if (m_debugQuery)
161 reply->requestUrl = requestUrl.url(QUrl::None);
162
163 return reply;
164}
165
167{
168 // Only fetch categories once
169 if (m_categories.isEmpty() && !m_categoriesReply) {
170 m_categoryLocales = m_locales;
171 m_categoryLocales.append(QLocale(QLocale::English));
172 fetchNextCategoryLocale();
173 }
174
177 this, &QPlaceManagerEngineOsm::replyFinished);
179 this, &QPlaceManagerEngineOsm::replyError);
180
181 // TODO delayed finished() emission
182 if (!m_categories.isEmpty())
183 reply->emitFinished();
184
185 m_pendingCategoriesReply.append(reply);
186 return reply;
187}
188
190{
191 Q_UNUSED(categoryId);
192
193 // Only a two category levels
194 return QString();
195}
196
198{
199 return m_subcategories.value(categoryId);
200}
201
203{
204 return m_categories.value(categoryId);
205}
206
207QList<QPlaceCategory> QPlaceManagerEngineOsm::childCategories(const QString &parentId) const
208{
209 QList<QPlaceCategory> categories;
210 for (const QString &id : m_subcategories.value(parentId))
211 categories.append(m_categories.value(id));
212 return categories;
213}
214
216{
217 return m_locales;
218}
219
220void QPlaceManagerEngineOsm::setLocales(const QList<QLocale> &locales)
221{
222 m_locales = locales;
223}
224
225void QPlaceManagerEngineOsm::categoryReplyFinished()
226{
227 QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
229
230 QXmlStreamReader parser(reply);
231 while (!parser.atEnd() && parser.readNextStartElement()) {
232 if (parser.name() == QLatin1String("mediawiki"))
233 continue;
234 if (parser.name() == QLatin1String("page"))
235 continue;
236 if (parser.name() == QLatin1String("revision"))
237 continue;
238 if (parser.name() == QLatin1String("text")) {
239 // parse
240 QString page = parser.readElementText();
241 QRegularExpression regex(QStringLiteral("\\| ([^|]+) \\|\\| ([^|]+) \\|\\| ([^|]+) \\|\\| ([^|]+) \\|\\| ([\\-YN])"));
242 QRegularExpressionMatchIterator i = regex.globalMatch(page);
243 while (i.hasNext()) {
245 QString name = match.capturedView(1).toString();
246 QString tagKey = match.capturedView(2).toString();
247 QString tagValue = match.capturedView(3).toString();
248 QString op = match.capturedView(4).toString();
249 QString plural = match.capturedView(5).toString();
250
251 // Only interested in any operator plural forms
252 if (op != QLatin1String("-") || plural != QLatin1String("Y"))
253 continue;
254
255 if (!m_categories.contains(tagKey)) {
257 category.setCategoryId(tagKey);
258 category.setName(nameForTagKey(tagKey));
259 m_categories.insert(category.categoryId(), category);
260 m_subcategories[QString()].append(tagKey);
262 }
263
265 category.setCategoryId(tagKey + QLatin1Char('=') + tagValue);
266 category.setName(name);
267
268 if (!m_categories.contains(category.categoryId())) {
269 m_categories.insert(category.categoryId(), category);
270 m_subcategories[tagKey].append(category.categoryId());
271 emit categoryAdded(category, tagKey);
272 }
273 }
274 }
275
276 parser.skipCurrentElement();
277 }
278
279 if (m_categories.isEmpty() && !m_categoryLocales.isEmpty()) {
280 fetchNextCategoryLocale();
281 return;
282 } else {
283 m_categoryLocales.clear();
284 }
285
286 for (QPlaceCategoriesReplyOsm *reply : m_pendingCategoriesReply)
287 reply->emitFinished();
288 m_pendingCategoriesReply.clear();
289}
290
291void QPlaceManagerEngineOsm::categoryReplyError()
292{
293 for (QPlaceCategoriesReplyOsm *reply : m_pendingCategoriesReply)
294 reply->setError(QPlaceReply::CommunicationError, tr("Network request error"));
295}
296
297void QPlaceManagerEngineOsm::replyFinished()
298{
299 QPlaceReply *reply = qobject_cast<QPlaceReply *>(sender());
300 if (reply)
302}
303
304void QPlaceManagerEngineOsm::replyError(QPlaceReply::Error errorCode, const QString &errorString)
305{
306 QPlaceReply *reply = qobject_cast<QPlaceReply *>(sender());
307 if (reply)
308 emit errorOccurred(reply, errorCode, errorString);
309}
310
311void QPlaceManagerEngineOsm::fetchNextCategoryLocale()
312{
313 if (m_categoryLocales.isEmpty()) {
314 qWarning("No locales specified to fetch categories for");
315 return;
316 }
317
318 QLocale locale = m_categoryLocales.takeFirst();
319
320 // FIXME: Categories should be cached.
321 QUrl requestUrl = QUrl(SpecialPhrasesBaseUrl + locale.name().left(2).toUpper());
322
323 m_categoriesReply = m_networkManager->get(QNetworkRequest(requestUrl));
324 connect(m_categoriesReply, &QNetworkReply::finished,
325 this, &QPlaceManagerEngineOsm::categoryReplyFinished);
326 connect(m_categoriesReply, &QNetworkReply::errorOccurred,
327 this, &QPlaceManagerEngineOsm::categoryReplyError);
328}
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
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1007
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
bool isEmpty() const noexcept
Definition qlist.h:401
value_type takeFirst()
Definition qlist.h:566
void append(parameter_type t)
Definition qlist.h:458
void clear()
Definition qlist.h:434
@ English
Definition qlocale.h:119
T value(const Key &key, const T &defaultValue=T()) const
Definition qmap.h:357
bool contains(const Key &key) const
Definition qmap.h:341
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
QStringList childCategoryIds(const QString &categoryId) const override
Returns the child category identifiers of the category corresponding to categoryId.
QPlaceReply * initializeCategories() override
Initializes the categories of the manager engine.
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.
QPlaceCategory category(const QString &categoryId) const override
Returns the category corresponding to the given categoryId.
QPlaceSearchReply * search(const QPlaceSearchRequest &request) override
Searches for places according to the parameters specified in request.
QString parentCategoryId(const QString &categoryId) const override
Returns the parent category identifier of the category corresponding to categoryId.
QPlaceManagerEngineOsm(const QVariantMap &parameters, QGeoServiceProvider::Error *error, QString *errorString)
void setLocales(const QList< QLocale > &locales) override
Set the list of preferred locales.
\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 \reentrant
\inmodule QtCore \reentrant
\inmodule QtCore \reentrant
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QByteArray toLatin1() const &
Definition qstring.h:630
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 url(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2817
void setQuery(const QString &query, ParsingMode mode=TolerantMode)
Sets the query string of the URL to query.
Definition qurl.cpp:2550
@ None
Definition qurl.h:104
int toInt(bool *ok=nullptr) const
Returns the variant as an int if the variant has userType() \l QMetaType::Int, \l QMetaType::Bool,...
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]
@ 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.
DBusConnection const char DBusError * error
#define qWarning
Definition qlogging.h:166
GLuint name
GLsizei GLenum * categories
static void setError(QJsonObject *response, const QString &msg)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define tr(X)
#define emit
static QStringList toStringList(const QJsonArray &jsonArray)
#define Q_UNUSED(x)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
QByteArray page
[45]
QNetworkRequest request(url)
QNetworkReply * reply
\inmodule QtCore \reentrant
Definition qchar.h:18