diff options
Diffstat (limited to 'src/plugins/geoservices/nokia/qgeocodejsonparser.cpp')
-rw-r--r-- | src/plugins/geoservices/nokia/qgeocodejsonparser.cpp | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/src/plugins/geoservices/nokia/qgeocodejsonparser.cpp b/src/plugins/geoservices/nokia/qgeocodejsonparser.cpp new file mode 100644 index 00000000..128f7fd2 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeocodejsonparser.cpp @@ -0,0 +1,413 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeocodejsonparser.h" + +#include <QtPositioning/QGeoShape> +#include <QtPositioning/QGeoRectangle> +#include <QtPositioning/QGeoAddress> +#include <QtPositioning/QGeoCoordinate> + +#include <QtCore/QThreadPool> +#include <QtCore/QJsonObject> +#include <QtCore/QJsonArray> +#include <QtCore/QJsonParseError> +#include <QtCore/QVariantMap> + +#include <QtDebug> + +QT_BEGIN_NAMESPACE + +namespace { + +/* + Checks that the given Location object contains the information + we need and is not malformed in any way. We expect a Location + object of the following form: + + "Location": { + "Address": { + "AdditionalData": [ + { + "key": "CountryName", + "value": "Australia" + }, + { + "key": "StateName", + "value": "New South Wales" + } + ], + "City": "Sydney", + "Country": "AUS", + "District": "Casula", + "Label": "Casula, Sydney, NSW, Australia", + "PostalCode": "2170", + "State": "NSW" + }, + "DisplayPosition": { + "Latitude": -33.949509999999997, + "Longitude": 150.90386000000001 + }, + "LocationId": "NT_5UQ89lKoiI4DIYbOrIR0-D", + "LocationType": "area", + "MapReference": { + "CityId": "1469266800", + "CountryId": "1469256839", + "DistrictId": "1469267758", + "MapId": "NXAM16130", + "MapReleaseDate": "2016-10-05", + "MapVersion": "Q1/2016", + "ReferenceId": "868383156", + "SideOfStreet": "neither", + "StateId": "1469256831" + }, + "MapView": { + "BottomRight": { + "Latitude": -33.966839999999998, + "Longitude": 150.91875999999999 + }, + "TopLeft": { + "Latitude": -33.937440000000002, + "Longitude": 150.87457000000001 + } + } + } + +*/ +bool checkLocation(const QJsonObject &loc, QString *errorString) +{ + QJsonObject::const_iterator ait = loc.constFind(QLatin1String("Address")); + if (ait == loc.constEnd()) { + *errorString = QLatin1String("Expected Address element within Location object"); + return false; + } else if (!ait.value().isObject()) { + *errorString = QLatin1String("Expected Address object within Location object"); + return false; + } + + QJsonObject::const_iterator dpit = loc.constFind(QLatin1String("DisplayPosition")); + if (dpit == loc.constEnd()) { + *errorString = QLatin1String("Expected DisplayPosition element within Location object"); + return false; + } else if (!dpit.value().isObject()) { + *errorString = QLatin1String("Expected DisplayPosition object within Location object"); + return false; + } + QJsonObject displayPosition = dpit.value().toObject(); + QJsonObject::const_iterator latit = displayPosition.constFind(QLatin1String("Latitude")); + if (latit == displayPosition.constEnd()) { + *errorString = QLatin1String("Expected Latitude element within Location.DisplayPosition object"); + return false; + } else if (!latit.value().isDouble()) { + *errorString = QLatin1String("Expected Latitude double within Location.DisplayPosition object"); + return false; + } + QJsonObject::const_iterator lonit = displayPosition.constFind(QLatin1String("Longitude")); + if (lonit == displayPosition.constEnd()) { + *errorString = QLatin1String("Expected Longitude element within Location.DisplayPosition object"); + return false; + } else if (!lonit.value().isDouble()) { + *errorString = QLatin1String("Expected Longitude double within Location.DisplayPosition object"); + return false; + } + + QJsonObject::const_iterator mvit = loc.constFind(QLatin1String("MapView")); + if (mvit == loc.constEnd()) { + *errorString = QLatin1String("Expected MapView element within Location object"); + return false; + } else if (!mvit.value().isObject()) { + *errorString = QLatin1String("Expected MapView object within Location object"); + return false; + } + QJsonObject mapView = mvit.value().toObject(); + QJsonObject::const_iterator brit = mapView.constFind(QLatin1String("BottomRight")); + if (brit == mapView.constEnd()) { + *errorString = QLatin1String("Expected BottomRight element within Location.MapView object"); + return false; + } else if (!brit.value().isObject()) { + *errorString = QLatin1String("Expected BottomRight object within Location.MapView object"); + return false; + } + QJsonObject bottomRight = brit.value().toObject(); + QJsonObject::const_iterator brlatit = bottomRight.constFind(QLatin1String("Latitude")); + if (brlatit == bottomRight.constEnd()) { + *errorString = QLatin1String("Expected Latitude element within Location.MapView.BottomRight object"); + return false; + } else if (!brlatit.value().isDouble()) { + *errorString = QLatin1String("Expected Latitude double within Location.MapView.BottomRight object"); + return false; + } + QJsonObject::const_iterator brlonit = bottomRight.constFind(QLatin1String("Longitude")); + if (brlonit == bottomRight.constEnd()) { + *errorString = QLatin1String("Expected Longitude element within Location.MapView.BottomRight object"); + return false; + } else if (!brlonit.value().isDouble()) { + *errorString = QLatin1String("Expected Longitude double within Location.MapView.BottomRight object"); + return false; + } + QJsonObject::const_iterator tlit = mapView.constFind(QLatin1String("TopLeft")); + if (tlit == mapView.constEnd()) { + *errorString = QLatin1String("Expected TopLeft element within Location.MapView object"); + return false; + } else if (!tlit.value().isObject()) { + *errorString = QLatin1String("Expected TopLeft object within Location.MapView object"); + return false; + } + QJsonObject topLeft = tlit.value().toObject(); + QJsonObject::const_iterator tllatit = topLeft.constFind(QLatin1String("Latitude")); + if (tllatit == topLeft.constEnd()) { + *errorString = QLatin1String("Expected Latitude element within Location.MapView.TopLeft object"); + return false; + } else if (!tllatit.value().isDouble()) { + *errorString = QLatin1String("Expected Latitude double within Location.MapView.TopLeft object"); + return false; + } + QJsonObject::const_iterator tllonit = topLeft.constFind(QLatin1String("Longitude")); + if (tllonit == bottomRight.constEnd()) { + *errorString = QLatin1String("Expected Longitude element within Location.MapView.TopLeft object"); + return false; + } else if (!tllonit.value().isDouble()) { + *errorString = QLatin1String("Expected Longitude double within Location.MapView.TopLeft object"); + return false; + } + + return true; +} + +/* + Checks that the given document contains the required information + and is not malformed in any way. We expect a document like the + following: + + { + "Response": { + "MetaInfo": { + "Timestamp": "2016-10-18T08:42:04.369+0000" + }, + "View": [ + { + "ViewId": 0, + "_type": "SearchResultsViewType", + "Result": [ + { + "Direction": 72.099999999999994, + "Distance": -1885.2, + "Location": { + // OMITTED FOR BREVITY + }, + "MatchLevel": "district", + "MatchQuality": { + "City": 1, + "Country": 1, + "District": 1, + "PostalCode": 1, + "State": 1 + }, + "Relevance": 1 + } + ] + } + ] + } + } +*/ +bool checkDocument(const QJsonDocument &doc, QString *errorString) +{ + if (!doc.isObject()) { + *errorString = QLatin1String("Expected JSON document containing object"); + return false; + } + + QJsonObject rootObject = doc.object(); + QJsonObject::const_iterator it = rootObject.constFind(QLatin1String("Response")); + if (it == rootObject.constEnd()) { + *errorString = QLatin1String("Expected Response element within root object"); + return false; + } else if (!it.value().isObject()) { + *errorString = QLatin1String("Expected Response object within root object"); + return false; + } + + QJsonObject response = it.value().toObject(); + QJsonObject::const_iterator rit = response.constFind(QLatin1String("View")); + if (rit == response.constEnd()) { + *errorString = QLatin1String("Expected View element within Response object"); + return false; + } else if (!rit.value().isArray()) { + *errorString = QLatin1String("Expected View array within Response object"); + return false; + } + + QJsonArray view = rit.value().toArray(); + Q_FOREACH (const QJsonValue &viewElement, view) { + if (!viewElement.isObject()) { + *errorString = QLatin1String("Expected View array element to be object"); + return false; + } + + QJsonObject viewObject = viewElement.toObject(); + QJsonObject::const_iterator voit = viewObject.constFind(QLatin1String("Result")); + if (voit == viewObject.constEnd()) { + *errorString = QLatin1String("Expected Result element within View array object element"); + return false; + } else if (!voit.value().isArray()) { + *errorString = QLatin1String("Expected Result array within View array object element"); + return false; + } + + QJsonArray result = voit.value().toArray(); + Q_FOREACH (const QJsonValue &resultElement, result) { + if (!resultElement.isObject()) { + *errorString = QLatin1String("Expected Result array element to be object"); + return false; + } + + QJsonObject resultObject = resultElement.toObject(); + QJsonObject::const_iterator roit = resultObject.constFind("Location"); + if (roit == resultObject.constEnd()) { + *errorString = QLatin1String("Expected Location element in Result array element object"); + return false; + } else if (!roit.value().isObject()) { + *errorString = QLatin1String("Expected Location object in Result array element object"); + return false; + } + + QJsonObject location = roit.value().toObject(); + if (!checkLocation(location, errorString)) { + return false; + } + } + } + + return true; +} + +bool parseLocation(const QJsonObject &obj, const QGeoShape &bounds, QGeoLocation *loc) +{ + QJsonObject displayPosition = obj.value("DisplayPosition").toObject(); + QGeoCoordinate coordinate = QGeoCoordinate(displayPosition.value("Latitude").toDouble(), displayPosition.value("Longitude").toDouble()); + if (bounds.isValid() && !bounds.contains(coordinate)) { + // manual bounds check failed, location can be omitted from results. + return false; + } + + QGeoAddress address; + QJsonObject addr = obj.value("Address").toObject(); + address.setCountryCode(addr.value("Country").toString()); + address.setState(addr.value("State").toString()); + address.setCounty(addr.value("County").toString()); + address.setCity(addr.value("City").toString()); + address.setDistrict(addr.value("District").toString()); + QString houseNumber = addr.value("HouseNumber").toString(); + QString street = addr.value("Street").toString(); + address.setStreet(houseNumber.isEmpty() ? street : QString("%1 %2").arg(houseNumber, street)); + address.setPostalCode(addr.value("PostalCode").toString()); + QString label = addr.value("Label").toString().trimmed(); + if (!label.isEmpty()) { + address.setText(label); + } + QJsonArray additionalData = addr.value("AdditionalData").toArray(); + Q_FOREACH (const QJsonValue &adv, additionalData) { + if (adv.isObject()) { + const QJsonObject &ado(adv.toObject()); + if (ado.value("key").toString() == QLatin1String("CountryName")) { + address.setCountry(ado.value("value").toString()); + } + } + } + + QGeoRectangle boundingBox; + QJsonObject mapView = obj.value("MapView").toObject(); + QJsonObject bottomRight = mapView.value("BottomRight").toObject(); + QJsonObject topLeft = mapView.value("TopLeft").toObject(); + boundingBox.setBottomRight(QGeoCoordinate(bottomRight.value("Latitude").toDouble(), bottomRight.value("Longitude").toDouble())); + boundingBox.setTopLeft(QGeoCoordinate(topLeft.value("Latitude").toDouble(), topLeft.value("Longitude").toDouble())); + + loc->setAddress(address); + loc->setCoordinate(coordinate); + loc->setBoundingBox(boundingBox); + + return true; +} + +void parseDocument(const QJsonDocument &doc, const QGeoShape &bounds, QList<QGeoLocation> *locs) +{ + QJsonArray view = doc.object().value("Response").toObject().value("View").toArray(); + Q_FOREACH (const QJsonValue &viewElement, view) { + QJsonArray result = viewElement.toObject().value("Result").toArray(); + Q_FOREACH (const QJsonValue &resultElement, result) { + QGeoLocation location; + if (parseLocation(resultElement.toObject().value("Location").toObject(), bounds, &location)) { + locs->append(location); + } + } + } +} + +} // namespace + +void QGeoCodeJsonParser::setBounds(const QGeoShape &bounds) +{ + m_bounds = bounds; +} + +void QGeoCodeJsonParser::parse(const QByteArray &data) +{ + m_data = data; + QThreadPool::globalInstance()->start(this); +} + +void QGeoCodeJsonParser::run() +{ + // parse the document. + QJsonParseError perror; + m_document = QJsonDocument::fromJson(m_data, &perror); + if (perror.error != QJsonParseError::NoError) { + m_errorString = perror.errorString(); + } else { + // ensure that the response is valid and contains the information we need. + if (checkDocument(m_document, &m_errorString)) { + // extract the location results from the response. + parseDocument(m_document, m_bounds, &m_results); + emit results(m_results); + return; + } + } + + emit error(m_errorString); +} + +QT_END_NAMESPACE |