| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2013-2018 Esri <contracts@esri.com> | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the QtLocation module of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU Lesser General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the | 
| 21 | ** packaging of this file. Please review the following information to | 
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements | 
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. | 
| 24 | ** | 
| 25 | ** GNU General Public License Usage | 
| 26 | ** Alternatively, this file may be used under the terms of the GNU | 
| 27 | ** General Public License version 2.0 or (at your option) the GNU General | 
| 28 | ** Public license version 3 or any later version approved by the KDE Free | 
| 29 | ** Qt Foundation. The licenses are as published by the Free Software | 
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 | 
| 31 | ** included in the packaging of this file. Please review the following | 
| 32 | ** information to ensure the GNU General Public License requirements will | 
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and | 
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. | 
| 35 | ** | 
| 36 | ** $QT_END_LICENSE$ | 
| 37 | ** | 
| 38 | ****************************************************************************/ | 
| 39 |  | 
| 40 | #include "geocodingmanagerengine_esri.h" | 
| 41 | #include "geocodereply_esri.h" | 
| 42 |  | 
| 43 | #include <QVariantMap> | 
| 44 | #include <QUrl> | 
| 45 | #include <QUrlQuery> | 
| 46 | #include <QLocale> | 
| 47 | #include <QNetworkAccessManager> | 
| 48 | #include <QNetworkRequest> | 
| 49 | #include <QGeoCoordinate> | 
| 50 | #include <QGeoAddress> | 
| 51 | #include <QGeoShape> | 
| 52 | #include <QGeoRectangle> | 
| 53 |  | 
| 54 | QT_BEGIN_NAMESPACE | 
| 55 |  | 
| 56 | // https://developers.arcgis.com/rest/geocode/api-reference/geocoding-find-address-candidates.htm | 
| 57 | // https://developers.arcgis.com/rest/geocode/api-reference/geocoding-reverse-geocode.htm | 
| 58 |  | 
| 59 | static const QString kPrefixEsri(QStringLiteral("esri." )); | 
| 60 | static const QString kParamUserAgent(kPrefixEsri + QStringLiteral("useragent" )); | 
| 61 |  | 
| 62 | static const QString kUrlGeocode(QStringLiteral("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates" )); | 
| 63 | static const QString kUrlReverseGeocode(QStringLiteral("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode" )); | 
| 64 |  | 
| 65 | static QString addressToQuery(const QGeoAddress &address) | 
| 66 | { | 
| 67 |     return address.street() + QStringLiteral(", " ) | 
| 68 |            + address.district() + QStringLiteral(", " ) | 
| 69 |            + address.city() + QStringLiteral(", " ) | 
| 70 |            + address.state() + QStringLiteral(", " ) | 
| 71 |            + address.country(); | 
| 72 | } | 
| 73 |  | 
| 74 | static QString boundingBoxToLtrb(const QGeoRectangle &rect) | 
| 75 | { | 
| 76 |     return QString::number(rect.topLeft().longitude()) + QLatin1Char(',') | 
| 77 |            + QString::number(rect.topLeft().latitude()) + QLatin1Char(',') | 
| 78 |            + QString::number(rect.bottomRight().longitude()) + QLatin1Char(',') | 
| 79 |            + QString::number(rect.bottomRight().latitude()); | 
| 80 | } | 
| 81 |  | 
| 82 | GeoCodingManagerEngineEsri::GeoCodingManagerEngineEsri(const QVariantMap ¶meters, | 
| 83 |                                                        QGeoServiceProvider::Error *error, | 
| 84 |                                                        QString *errorString) | 
| 85 | :   QGeoCodingManagerEngine(parameters), m_networkManager(new QNetworkAccessManager(this)) | 
| 86 | { | 
| 87 |     if (parameters.contains(akey: kParamUserAgent)) | 
| 88 |         m_userAgent = parameters.value(akey: kParamUserAgent).toString().toLatin1(); | 
| 89 |     else | 
| 90 |         m_userAgent = QByteArrayLiteral("Qt Location based application" ); | 
| 91 |  | 
| 92 |     *error = QGeoServiceProvider::NoError; | 
| 93 |     errorString->clear(); | 
| 94 | } | 
| 95 |  | 
| 96 | GeoCodingManagerEngineEsri::~GeoCodingManagerEngineEsri() | 
| 97 | { | 
| 98 | } | 
| 99 |  | 
| 100 | QGeoCodeReply *GeoCodingManagerEngineEsri::geocode(const QGeoAddress &address, | 
| 101 |                                                    const QGeoShape &bounds) | 
| 102 | { | 
| 103 |     return geocode(address: addressToQuery(address), limit: 1, offset: -1, bounds); | 
| 104 | } | 
| 105 |  | 
| 106 | QGeoCodeReply *GeoCodingManagerEngineEsri::geocode(const QString &address, int limit, int offset, | 
| 107 |                                                    const QGeoShape &bounds) | 
| 108 | { | 
| 109 |     Q_UNUSED(offset); | 
| 110 |  | 
| 111 |     QNetworkRequest request; | 
| 112 |     request.setHeader(header: QNetworkRequest::UserAgentHeader, value: m_userAgent); | 
| 113 |  | 
| 114 |     QUrl url(kUrlGeocode); | 
| 115 |  | 
| 116 |     QUrlQuery query; | 
| 117 |     query.addQueryItem(QStringLiteral("singleLine" ), value: address); | 
| 118 |     query.addQueryItem(QStringLiteral("f" ), QStringLiteral("json" )); | 
| 119 |     query.addQueryItem(QStringLiteral("outFields" ), value: "*" ); | 
| 120 |  | 
| 121 |     if (bounds.type() != QGeoShape::UnknownType) | 
| 122 |         query.addQueryItem(QStringLiteral("searchExtent" ), value: boundingBoxToLtrb(rect: bounds.boundingGeoRectangle())); | 
| 123 |  | 
| 124 |     if (limit != -1) | 
| 125 |         query.addQueryItem(QStringLiteral("maxLocations" ), value: QString::number(limit)); | 
| 126 |  | 
| 127 |     url.setQuery(query); | 
| 128 |     request.setUrl(url); | 
| 129 |  | 
| 130 |     QNetworkReply *reply = m_networkManager->get(request); | 
| 131 |     GeoCodeReplyEsri *geocodeReply = new GeoCodeReplyEsri(reply, GeoCodeReplyEsri::Geocode, this); | 
| 132 |  | 
| 133 |     connect(sender: geocodeReply, SIGNAL(finished()), receiver: this, SLOT(replyFinished())); | 
| 134 |     connect(sender: geocodeReply, SIGNAL(error(QGeoCodeReply::Error,QString)), | 
| 135 |             receiver: this, SLOT(replyError(QGeoCodeReply::Error,QString))); | 
| 136 |  | 
| 137 |     return geocodeReply; | 
| 138 | } | 
| 139 |  | 
| 140 | QGeoCodeReply *GeoCodingManagerEngineEsri::reverseGeocode(const QGeoCoordinate &coordinate, | 
| 141 |                                                           const QGeoShape &bounds) | 
| 142 | { | 
| 143 |     Q_UNUSED(bounds); | 
| 144 |  | 
| 145 |     QNetworkRequest request; | 
| 146 |     request.setHeader(header: QNetworkRequest::UserAgentHeader, value: m_userAgent); | 
| 147 |  | 
| 148 |     QUrl url(kUrlReverseGeocode); | 
| 149 |  | 
| 150 |     QUrlQuery query; | 
| 151 |  | 
| 152 |     query.addQueryItem(QStringLiteral("f" ), QStringLiteral("json" )); | 
| 153 |     query.addQueryItem(QStringLiteral("langCode" ), value: locale().name().left(n: 2)); | 
| 154 |     query.addQueryItem(QStringLiteral("location" ), value: QString::number(coordinate.longitude()) + QLatin1Char(',') | 
| 155 |                        + QString::number(coordinate.latitude())); | 
| 156 |  | 
| 157 |     url.setQuery(query); | 
| 158 |     request.setUrl(url); | 
| 159 |  | 
| 160 |     QNetworkReply *reply = m_networkManager->get(request); | 
| 161 |     GeoCodeReplyEsri *geocodeReply = new GeoCodeReplyEsri(reply, GeoCodeReplyEsri::ReverseGeocode, | 
| 162 |                                                           this); | 
| 163 |  | 
| 164 |     connect(sender: geocodeReply, SIGNAL(finished()), receiver: this, SLOT(replyFinished())); | 
| 165 |     connect(sender: geocodeReply, SIGNAL(error(QGeoCodeReply::Error,QString)), | 
| 166 |             receiver: this, SLOT(replyError(QGeoCodeReply::Error,QString))); | 
| 167 |  | 
| 168 |     return geocodeReply; | 
| 169 | } | 
| 170 |  | 
| 171 | void GeoCodingManagerEngineEsri::replyFinished() | 
| 172 | { | 
| 173 |     QGeoCodeReply *reply = qobject_cast<QGeoCodeReply *>(object: sender()); | 
| 174 |     if (reply) | 
| 175 |         emit finished(reply); | 
| 176 | } | 
| 177 |  | 
| 178 | void GeoCodingManagerEngineEsri::replyError(QGeoCodeReply::Error errorCode, | 
| 179 |                                             const QString &errorString) | 
| 180 | { | 
| 181 |     QGeoCodeReply *reply = qobject_cast<QGeoCodeReply *>(object: sender()); | 
| 182 |     if (reply) | 
| 183 |         emit error(reply, error: errorCode, errorString); | 
| 184 | } | 
| 185 |  | 
| 186 | QT_END_NAMESPACE | 
| 187 |  |