1/****************************************************************************
2**
3** Copyright (C) 2015 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the QtLocation module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
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 http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "qgeocodingmanagerengine_nokia.h"
38#include "qgeocodereply_nokia.h"
39#include "marclanguagecodes.h"
40#include "qgeonetworkaccessmanager.h"
41#include "qgeouriprovider.h"
42#include "uri_constants.h"
43
44#include <QtPositioning/QGeoAddress>
45#include <QtPositioning/QGeoCoordinate>
46#include <QtPositioning/QGeoCircle>
47#include <QtPositioning/QGeoRectangle>
48#include <QtPositioning/QGeoShape>
49
50#include <QUrl>
51#include <QMap>
52#include <QStringList>
53
54QT_BEGIN_NAMESPACE
55
56QGeoCodingManagerEngineNokia::QGeoCodingManagerEngineNokia(
57 QGeoNetworkAccessManager *networkManager,
58 const QVariantMap &parameters,
59 QGeoServiceProvider::Error *error,
60 QString *errorString)
61 : QGeoCodingManagerEngine(parameters)
62 , m_networkManager(networkManager)
63 , m_uriProvider(new QGeoUriProvider(this, parameters, QStringLiteral("here.geocoding.host"), GEOCODING_HOST))
64 , m_reverseGeocodingUriProvider(new QGeoUriProvider(this, parameters, QStringLiteral("here.reversegeocoding.host"), REVERSE_GEOCODING_HOST))
65{
66 Q_ASSERT(networkManager);
67 m_networkManager->setParent(this);
68
69 if (parameters.contains(QStringLiteral("here.token")))
70 m_token = parameters.value(QStringLiteral("here.token")).toString();
71
72 if (parameters.contains(QStringLiteral("here.app_id")))
73 m_applicationId = parameters.value(QStringLiteral("here.app_id")).toString();
74
75 if (error)
76 *error = QGeoServiceProvider::NoError;
77
78 if (errorString)
79 *errorString = "";
80}
81
82QGeoCodingManagerEngineNokia::~QGeoCodingManagerEngineNokia() {}
83
84QString QGeoCodingManagerEngineNokia::getAuthenticationString() const
85{
86 QString authenticationString;
87
88 if (!m_token.isEmpty() && !m_applicationId.isEmpty()) {
89 authenticationString += "?app_code=";
90 authenticationString += m_token;
91
92 authenticationString += "&app_id=";
93 authenticationString += m_applicationId;
94 }
95
96 return authenticationString;
97}
98
99
100QGeoCodeReply *QGeoCodingManagerEngineNokia::geocode(const QGeoAddress &address,
101 const QGeoShape &bounds)
102{
103 QString requestString = "https://";
104 requestString += m_uriProvider->getCurrentHost();
105 requestString += "/6.2/geocode.json";
106
107 requestString += getAuthenticationString();
108 requestString += "&gen=9";
109
110 requestString += "&language=";
111 requestString += languageToMarc(language: locale().language());
112
113 bool manualBoundsRequired = false;
114 if (bounds.type() == QGeoShape::UnknownType) {
115 manualBoundsRequired = true;
116 } else if (bounds.type() == QGeoShape::CircleType) {
117 QGeoCircle circ(bounds);
118 if (circ.isValid()) {
119 requestString += "?prox=";
120 requestString += trimDouble(degree: circ.center().latitude());
121 requestString += ",";
122 requestString += trimDouble(degree: circ.center().longitude());
123 requestString += ",";
124 requestString += trimDouble(degree: circ.radius());
125 }
126 } else {
127 QGeoRectangle rect = bounds.boundingGeoRectangle();
128 if (rect.isValid()) {
129 requestString += "&bbox=";
130 requestString += trimDouble(degree: rect.topLeft().latitude());
131 requestString += ",";
132 requestString += trimDouble(degree: rect.topLeft().longitude());
133 requestString += ";";
134 requestString += trimDouble(degree: rect.bottomRight().latitude());
135 requestString += ",";
136 requestString += trimDouble(degree: rect.bottomRight().longitude());
137 }
138 }
139
140 if (address.country().isEmpty()) {
141 QStringList parts;
142
143 if (!address.state().isEmpty())
144 parts << address.state();
145
146 if (!address.city().isEmpty())
147 parts << address.city();
148
149 if (!address.postalCode().isEmpty())
150 parts << address.postalCode();
151
152 if (!address.street().isEmpty())
153 parts << address.street();
154
155 requestString += "&searchtext=";
156 requestString += parts.join(sep: "+").replace(before: ' ', after: '+');
157 } else {
158 requestString += "&country=";
159 requestString += address.country();
160
161 if (!address.state().isEmpty()) {
162 requestString += "&state=";
163 requestString += address.state();
164 }
165
166 if (!address.city().isEmpty()) {
167 requestString += "&city=";
168 requestString += address.city();
169 }
170
171 if (!address.postalCode().isEmpty()) {
172 requestString += "&postalcode=";
173 requestString += address.postalCode();
174 }
175
176 if (!address.street().isEmpty()) {
177 requestString += "&street=";
178 requestString += address.street();
179 }
180 }
181
182 return geocode(requestString, bounds, manualBoundsRequired);
183}
184
185QGeoCodeReply *QGeoCodingManagerEngineNokia::geocode(const QString &address,
186 int limit,
187 int offset,
188 const QGeoShape &bounds)
189{
190 QString requestString = "https://";
191 requestString += m_uriProvider->getCurrentHost();
192 requestString += "/6.2/geocode.json";
193
194 requestString += getAuthenticationString();
195 requestString += "&gen=9";
196
197 requestString += "&language=";
198 requestString += languageToMarc(language: locale().language());
199
200 requestString += "&searchtext=";
201 requestString += QString(address).replace(before: ' ', after: '+');
202
203 if (limit > 0) {
204 requestString += "&maxresults=";
205 requestString += QString::number(limit);
206 }
207 if (offset > 0) {
208 // We cannot do this precisely, since HERE doesn't allow
209 // precise result-set offset to be supplied; instead, it
210 // returns "pages" of results at a time.
211 // So, we tell HERE which page of results we want, and the
212 // client has to filter out duplicates if they changed
213 // the limit param since the last call.
214 requestString += "&pageinformation=";
215 requestString += QString::number(offset/limit);
216 }
217
218 bool manualBoundsRequired = false;
219 if (bounds.type() == QGeoShape::RectangleType) {
220 QGeoRectangle rect(bounds);
221 if (rect.isValid()) {
222 requestString += "&bbox=";
223 requestString += trimDouble(degree: rect.topLeft().latitude());
224 requestString += ",";
225 requestString += trimDouble(degree: rect.topLeft().longitude());
226 requestString += ";";
227 requestString += trimDouble(degree: rect.bottomRight().latitude());
228 requestString += ",";
229 requestString += trimDouble(degree: rect.bottomRight().longitude());
230 }
231 } else if (bounds.type() == QGeoShape::CircleType) {
232 QGeoCircle circ(bounds);
233 if (circ.isValid()) {
234 requestString += "?prox=";
235 requestString += trimDouble(degree: circ.center().latitude());
236 requestString += ",";
237 requestString += trimDouble(degree: circ.center().longitude());
238 requestString += ",";
239 requestString += trimDouble(degree: circ.radius());
240 }
241 } else {
242 manualBoundsRequired = true;
243 }
244
245 return geocode(requestString, bounds, manualBoundsRequired, limit, offset);
246}
247
248QGeoCodeReply *QGeoCodingManagerEngineNokia::geocode(QString requestString,
249 const QGeoShape &bounds,
250 bool manualBoundsRequired,
251 int limit,
252 int offset)
253{
254 QGeoCodeReplyNokia *reply = new QGeoCodeReplyNokia(
255 m_networkManager->get(request: QNetworkRequest(QUrl(requestString))),
256 limit, offset, bounds, manualBoundsRequired, this);
257
258 connect(sender: reply, signal: &QGeoCodeReplyNokia::finished,
259 receiver: this, slot: &QGeoCodingManagerEngineNokia::placesFinished);
260
261 connect(sender: reply, signal: static_cast<void (QGeoCodeReply::*)(QGeoCodeReply::Error, const QString &)>(&QGeoCodeReplyNokia::error),
262 receiver: this, slot: &QGeoCodingManagerEngineNokia::placesError);
263
264 return reply;
265}
266
267QGeoCodeReply *QGeoCodingManagerEngineNokia::reverseGeocode(const QGeoCoordinate &coordinate,
268 const QGeoShape &bounds)
269{
270 QString requestString = "https://";
271 requestString += m_reverseGeocodingUriProvider->getCurrentHost();
272 requestString += "/6.2/reversegeocode.json";
273
274 requestString += getAuthenticationString();
275 requestString += "&gen=9";
276
277 requestString += "&mode=retrieveAddresses";
278
279 requestString += "&prox=";
280 requestString += trimDouble(degree: coordinate.latitude());
281 requestString += ",";
282 requestString += trimDouble(degree: coordinate.longitude());
283
284 bool manualBoundsRequired = false;
285 if (bounds.type() == QGeoShape::CircleType) {
286 QGeoCircle circ(bounds);
287 if (circ.isValid() && circ.center() == coordinate) {
288 requestString += ",";
289 requestString += trimDouble(degree: circ.radius());
290 } else {
291 manualBoundsRequired = true;
292 }
293 } else {
294 manualBoundsRequired = true;
295 }
296
297 requestString += "&language=";
298 requestString += languageToMarc(language: locale().language());
299
300 return geocode(requestString, bounds, manualBoundsRequired);
301}
302
303QString QGeoCodingManagerEngineNokia::trimDouble(double degree, int decimalDigits)
304{
305 QString sDegree = QString::number(degree, f: 'g', prec: decimalDigits);
306
307 int index = sDegree.indexOf(c: '.');
308
309 if (index == -1)
310 return sDegree;
311 else
312 return QString::number(degree, f: 'g', prec: decimalDigits + index);
313}
314
315void QGeoCodingManagerEngineNokia::placesFinished()
316{
317 QGeoCodeReply *reply = qobject_cast<QGeoCodeReply *>(object: sender());
318
319 if (!reply)
320 return;
321
322 if (receivers(SIGNAL(finished(QGeoCodeReply*))) == 0) {
323 reply->deleteLater();
324 return;
325 }
326
327 emit finished(reply);
328}
329
330void QGeoCodingManagerEngineNokia::placesError(QGeoCodeReply::Error error, const QString &errorString)
331{
332 QGeoCodeReply *reply = qobject_cast<QGeoCodeReply *>(object: sender());
333
334 if (!reply)
335 return;
336
337 if (receivers(SIGNAL(error(QGeoCodeReply*,QGeoCodeReply::Error,QString))) == 0) {
338 reply->deleteLater();
339 return;
340 }
341
342 emit this->error(reply, error, errorString);
343}
344
345QString QGeoCodingManagerEngineNokia::languageToMarc(QLocale::Language language)
346{
347 uint offset = 3 * (uint(language));
348 if (language == QLocale::C || offset + 3 > sizeof(marc_language_code_list))
349 return QLatin1String("eng");
350
351 const unsigned char *c = marc_language_code_list + offset;
352 if (c[0] == 0)
353 return QLatin1String("eng");
354
355 QString code(3, Qt::Uninitialized);
356 code[0] = ushort(c[0]);
357 code[1] = ushort(c[1]);
358 code[2] = ushort(c[2]);
359
360 return code;
361}
362
363QT_END_NAMESPACE
364

source code of qtlocation/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.cpp