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
qgeocodejsonparser.cpp
Go to the documentation of this file.
1// Copyright (C) 2015 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtPositioning/QGeoShape>
7#include <QtPositioning/QGeoRectangle>
8#include <QtPositioning/QGeoAddress>
9#include <QtPositioning/QGeoCoordinate>
10
11#include <QtCore/QThreadPool>
12#include <QtCore/QJsonObject>
13#include <QtCore/QJsonArray>
14#include <QtCore/QJsonParseError>
15#include <QtCore/QVariantMap>
16
17#include <QtDebug>
18
20
21namespace {
22
23/*
24 Checks that the given Location object contains the information
25 we need and is not malformed in any way. We expect a Location
26 object of the following form:
27
28 "Location": {
29 "Address": {
30 "AdditionalData": [
31 {
32 "key": "CountryName",
33 "value": "Australia"
34 },
35 {
36 "key": "StateName",
37 "value": "New South Wales"
38 }
39 ],
40 "City": "Sydney",
41 "Country": "AUS",
42 "District": "Casula",
43 "Label": "Casula, Sydney, NSW, Australia",
44 "PostalCode": "2170",
45 "State": "NSW"
46 },
47 "DisplayPosition": {
48 "Latitude": -33.949509999999997,
49 "Longitude": 150.90386000000001
50 },
51 "LocationId": "NT_5UQ89lKoiI4DIYbOrIR0-D",
52 "LocationType": "area",
53 "MapReference": {
54 "CityId": "1469266800",
55 "CountryId": "1469256839",
56 "DistrictId": "1469267758",
57 "MapId": "NXAM16130",
58 "MapReleaseDate": "2016-10-05",
59 "MapVersion": "Q1/2016",
60 "ReferenceId": "868383156",
61 "SideOfStreet": "neither",
62 "StateId": "1469256831"
63 },
64 "MapView": {
65 "BottomRight": {
66 "Latitude": -33.966839999999998,
67 "Longitude": 150.91875999999999
68 },
69 "TopLeft": {
70 "Latitude": -33.937440000000002,
71 "Longitude": 150.87457000000001
72 }
73 }
74 }
75
76*/
77bool checkLocation(const QJsonObject &loc, QString *errorString)
78{
80 if (ait == loc.constEnd()) {
81 *errorString = QLatin1String("Expected Address element within Location object");
82 return false;
83 } else if (!ait.value().isObject()) {
84 *errorString = QLatin1String("Expected Address object within Location object");
85 return false;
86 }
87
88 QJsonObject::const_iterator dpit = loc.constFind(QLatin1String("DisplayPosition"));
89 if (dpit == loc.constEnd()) {
90 *errorString = QLatin1String("Expected DisplayPosition element within Location object");
91 return false;
92 } else if (!dpit.value().isObject()) {
93 *errorString = QLatin1String("Expected DisplayPosition object within Location object");
94 return false;
95 }
96 QJsonObject displayPosition = dpit.value().toObject();
97 QJsonObject::const_iterator latit = displayPosition.constFind(QLatin1String("Latitude"));
98 if (latit == displayPosition.constEnd()) {
99 *errorString = QLatin1String("Expected Latitude element within Location.DisplayPosition object");
100 return false;
101 } else if (!latit.value().isDouble()) {
102 *errorString = QLatin1String("Expected Latitude double within Location.DisplayPosition object");
103 return false;
104 }
105 QJsonObject::const_iterator lonit = displayPosition.constFind(QLatin1String("Longitude"));
106 if (lonit == displayPosition.constEnd()) {
107 *errorString = QLatin1String("Expected Longitude element within Location.DisplayPosition object");
108 return false;
109 } else if (!lonit.value().isDouble()) {
110 *errorString = QLatin1String("Expected Longitude double within Location.DisplayPosition object");
111 return false;
112 }
113
115 if (mvit == loc.constEnd()) {
116 *errorString = QLatin1String("Expected MapView element within Location object");
117 return false;
118 } else if (!mvit.value().isObject()) {
119 *errorString = QLatin1String("Expected MapView object within Location object");
120 return false;
121 }
122 QJsonObject mapView = mvit.value().toObject();
123 QJsonObject::const_iterator brit = mapView.constFind(QLatin1String("BottomRight"));
124 if (brit == mapView.constEnd()) {
125 *errorString = QLatin1String("Expected BottomRight element within Location.MapView object");
126 return false;
127 } else if (!brit.value().isObject()) {
128 *errorString = QLatin1String("Expected BottomRight object within Location.MapView object");
129 return false;
130 }
131 QJsonObject bottomRight = brit.value().toObject();
132 QJsonObject::const_iterator brlatit = bottomRight.constFind(QLatin1String("Latitude"));
133 if (brlatit == bottomRight.constEnd()) {
134 *errorString = QLatin1String("Expected Latitude element within Location.MapView.BottomRight object");
135 return false;
136 } else if (!brlatit.value().isDouble()) {
137 *errorString = QLatin1String("Expected Latitude double within Location.MapView.BottomRight object");
138 return false;
139 }
140 QJsonObject::const_iterator brlonit = bottomRight.constFind(QLatin1String("Longitude"));
141 if (brlonit == bottomRight.constEnd()) {
142 *errorString = QLatin1String("Expected Longitude element within Location.MapView.BottomRight object");
143 return false;
144 } else if (!brlonit.value().isDouble()) {
145 *errorString = QLatin1String("Expected Longitude double within Location.MapView.BottomRight object");
146 return false;
147 }
148 QJsonObject::const_iterator tlit = mapView.constFind(QLatin1String("TopLeft"));
149 if (tlit == mapView.constEnd()) {
150 *errorString = QLatin1String("Expected TopLeft element within Location.MapView object");
151 return false;
152 } else if (!tlit.value().isObject()) {
153 *errorString = QLatin1String("Expected TopLeft object within Location.MapView object");
154 return false;
155 }
156 QJsonObject topLeft = tlit.value().toObject();
157 QJsonObject::const_iterator tllatit = topLeft.constFind(QLatin1String("Latitude"));
158 if (tllatit == topLeft.constEnd()) {
159 *errorString = QLatin1String("Expected Latitude element within Location.MapView.TopLeft object");
160 return false;
161 } else if (!tllatit.value().isDouble()) {
162 *errorString = QLatin1String("Expected Latitude double within Location.MapView.TopLeft object");
163 return false;
164 }
165 QJsonObject::const_iterator tllonit = topLeft.constFind(QLatin1String("Longitude"));
166 if (tllonit == bottomRight.constEnd()) {
167 *errorString = QLatin1String("Expected Longitude element within Location.MapView.TopLeft object");
168 return false;
169 } else if (!tllonit.value().isDouble()) {
170 *errorString = QLatin1String("Expected Longitude double within Location.MapView.TopLeft object");
171 return false;
172 }
173
174 return true;
175}
176
177/*
178 Checks that the given document contains the required information
179 and is not malformed in any way. We expect a document like the
180 following:
181
182 {
183 "Response": {
184 "MetaInfo": {
185 "Timestamp": "2016-10-18T08:42:04.369+0000"
186 },
187 "View": [
188 {
189 "ViewId": 0,
190 "_type": "SearchResultsViewType",
191 "Result": [
192 {
193 "Direction": 72.099999999999994,
194 "Distance": -1885.2,
195 "Location": {
196 // OMITTED FOR BREVITY
197 },
198 "MatchLevel": "district",
199 "MatchQuality": {
200 "City": 1,
201 "Country": 1,
202 "District": 1,
203 "PostalCode": 1,
204 "State": 1
205 },
206 "Relevance": 1
207 }
208 ]
209 }
210 ]
211 }
212 }
213*/
214bool checkDocument(const QJsonDocument &doc, QString *errorString)
215{
216 if (!doc.isObject()) {
217 *errorString = QLatin1String("Expected JSON document containing object");
218 return false;
219 }
220
221 QJsonObject rootObject = doc.object();
222 QJsonObject::const_iterator it = rootObject.constFind(QLatin1String("Response"));
223 if (it == rootObject.constEnd()) {
224 *errorString = QLatin1String("Expected Response element within root object");
225 return false;
226 } else if (!it.value().isObject()) {
227 *errorString = QLatin1String("Expected Response object within root object");
228 return false;
229 }
230
231 QJsonObject response = it.value().toObject();
233 if (rit == response.constEnd()) {
234 *errorString = QLatin1String("Expected View element within Response object");
235 return false;
236 } else if (!rit.value().isArray()) {
237 *errorString = QLatin1String("Expected View array within Response object");
238 return false;
239 }
240
241 const QJsonArray view = rit.value().toArray();
242 for (const QJsonValueConstRef viewElement : view) {
243 if (!viewElement.isObject()) {
244 *errorString = QLatin1String("Expected View array element to be object");
245 return false;
246 }
247
248 QJsonObject viewObject = viewElement.toObject();
249 QJsonObject::const_iterator voit = viewObject.constFind(QLatin1String("Result"));
250 if (voit == viewObject.constEnd()) {
251 *errorString = QLatin1String("Expected Result element within View array object element");
252 return false;
253 } else if (!voit.value().isArray()) {
254 *errorString = QLatin1String("Expected Result array within View array object element");
255 return false;
256 }
257
258 const QJsonArray result = voit.value().toArray();
259 for (const QJsonValueConstRef resultElement : result) {
260 if (!resultElement.isObject()) {
261 *errorString = QLatin1String("Expected Result array element to be object");
262 return false;
263 }
264
265 QJsonObject resultObject = resultElement.toObject();
266 QJsonObject::const_iterator roit = resultObject.constFind("Location");
267 if (roit == resultObject.constEnd()) {
268 *errorString = QLatin1String("Expected Location element in Result array element object");
269 return false;
270 } else if (!roit.value().isObject()) {
271 *errorString = QLatin1String("Expected Location object in Result array element object");
272 return false;
273 }
274
276 if (!checkLocation(location, errorString)) {
277 return false;
278 }
279 }
280 }
281
282 return true;
283}
284
285bool parseLocation(const QJsonObject &obj, const QGeoShape &bounds, QGeoLocation *loc)
286{
287 QJsonObject displayPosition = obj.value("DisplayPosition").toObject();
288 QGeoCoordinate coordinate = QGeoCoordinate(displayPosition.value("Latitude").toDouble(), displayPosition.value("Longitude").toDouble());
289 if (bounds.isValid() && !bounds.contains(coordinate)) {
290 // manual bounds check failed, location can be omitted from results.
291 return false;
292 }
293
295 QJsonObject addr = obj.value("Address").toObject();
296 address.setCountryCode(addr.value("Country").toString());
297 address.setState(addr.value("State").toString());
298 address.setCounty(addr.value("County").toString());
299 address.setCity(addr.value("City").toString());
300 address.setDistrict(addr.value("District").toString());
301 QString houseNumber = addr.value("HouseNumber").toString();
302 QString street = addr.value("Street").toString();
303 address.setStreet(houseNumber.isEmpty() ? street : QString("%1 %2").arg(houseNumber, street));
304 address.setPostalCode(addr.value("PostalCode").toString());
305 QString label = addr.value("Label").toString().trimmed();
306 if (!label.isEmpty()) {
307 address.setText(label);
308 }
309 const QJsonArray additionalData = addr.value("AdditionalData").toArray();
310 for (const QJsonValueConstRef adv : additionalData) {
311 if (adv.isObject()) {
312 const QJsonObject &ado(adv.toObject());
313 if (ado.value("key").toString() == QLatin1String("CountryName")) {
314 address.setCountry(ado.value("value").toString());
315 }
316 }
317 }
318
319 QGeoRectangle boundingBox;
320 QJsonObject mapView = obj.value("MapView").toObject();
321 QJsonObject bottomRight = mapView.value("BottomRight").toObject();
322 QJsonObject topLeft = mapView.value("TopLeft").toObject();
323 boundingBox.setBottomRight(QGeoCoordinate(bottomRight.value("Latitude").toDouble(), bottomRight.value("Longitude").toDouble()));
324 boundingBox.setTopLeft(QGeoCoordinate(topLeft.value("Latitude").toDouble(), topLeft.value("Longitude").toDouble()));
325
326 loc->setAddress(address);
327 loc->setCoordinate(coordinate);
328 loc->setBoundingShape(boundingBox);
329
330 return true;
331}
332
333void parseDocument(const QJsonDocument &doc, const QGeoShape &bounds, QList<QGeoLocation> *locs)
334{
335 QJsonArray view = doc.object().value("Response").toObject().value("View").toArray();
336 for (const QJsonValueRef viewElement : view) {
337 QJsonArray result = viewElement.toObject().value("Result").toArray();
338 for (const QJsonValueRef resultElement : result) {
340 if (parseLocation(resultElement.toObject().value("Location").toObject(), bounds, &location)) {
341 locs->append(location);
342 }
343 }
344 }
345}
346
347} // namespace
348
350{
351 m_bounds = bounds;
352}
353
355{
356 m_data = data;
357 QThreadPool::globalInstance()->start(this);
358}
359
361{
362 // parse the document.
363 QJsonParseError perror;
364 m_document = QJsonDocument::fromJson(m_data, &perror);
365 if (perror.error != QJsonParseError::NoError) {
366 m_errorString = perror.errorString();
367 } else {
368 // ensure that the response is valid and contains the information we need.
369 if (checkDocument(m_document, &m_errorString)) {
370 // extract the location results from the response.
371 parseDocument(m_document, m_bounds, &m_results);
372 emit results(m_results);
373 return;
374 }
375 }
376
377 emit errorOccurred(m_errorString);
378}
379
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtPositioning
Definition qgeoaddress.h:18
void setBounds(const QGeoShape &bounds)
void run() override
Implement this pure virtual function in your subclass.
void errorOccurred(const QString &errorString)
void parse(const QByteArray &data)
\inmodule QtPositioning
\inmodule QtPositioning
void setCoordinate(const QGeoCoordinate &position)
Sets the coordinate of the location.
void setAddress(const QGeoAddress &address)
Sets the address of the location.
void setBoundingShape(const QGeoShape &shape)
\inmodule QtPositioning
void setTopLeft(const QGeoCoordinate &topLeft)
Sets the top left coordinate of this geo rectangle to topLeft.
void setBottomRight(const QGeoCoordinate &bottomRight)
Sets the bottom right coordinate of this geo rectangle to bottomRight.
\inmodule QtPositioning
Definition qgeoshape.h:17
bool isValid
This property holds the validity of the geo shape.
Definition qgeoshape.h:20
Q_INVOKABLE bool contains(const QGeoCoordinate &coordinate) const
Returns whether the coordinate coordinate is contained within this geo shape.
\inmodule QtCore\reentrant
Definition qjsonarray.h:18
\inmodule QtCore\reentrant
QJsonObject object() const
Returns the QJsonObject contained in the document.
bool isObject() const
Returns true if the document contains an object.
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
const_iterator constFind(const QString &key) const
Returns a const iterator pointing to the item with key key in the map.
const_iterator constEnd() const
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
QJsonValue value(const QString &key) const
Returns a QJsonValue representing the value for the key key.
\inmodule QtCore \reentrant
QJsonObject toObject() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
QJsonArray toArray() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
double toDouble(double defaultValue=0) const
Converts the value to a double and returns it.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
QString trimmed() const &
Definition qstring.h:447
static QThreadPool * globalInstance()
Returns the global QThreadPool instance.
QSet< QString >::iterator it
Combined button and popup list for selecting options.
bool parseLocation(const QJsonObject &obj, const QGeoShape &bounds, QGeoLocation *loc)
void parseDocument(const QJsonDocument &doc, const QGeoShape &bounds, QList< QGeoLocation > *locs)
bool checkDocument(const QJsonDocument &doc, QString *errorString)
bool checkLocation(const QJsonObject &loc, QString *errorString)
GLint location
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint GLsizei const GLchar * label
[43]
GLhandleARB obj
[2]
GLenum const void * addr
GLuint GLuint64EXT address
GLuint64EXT * result
[6]
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define emit
QQuickView * view
[0]
\inmodule QtCore\reentrant