1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 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 "qgeotileproviderosm.h"
38
39 #include <QtCore/QJsonDocument>
40 #include <QtCore/QJsonObject>
41 #include <QDebug>
42
43 QT_BEGIN_NAMESPACE
44
45 static const int maxValidZoom = 30;
46 static const QDateTime defaultTs = QDateTime::fromString(QStringLiteral("2016-06-01T00:00:00"), Qt::ISODate);
47
setSSL(QGeoMapType & mapType,bool isHTTPS)48 static void setSSL(QGeoMapType &mapType, bool isHTTPS)
49 {
50 QVariantMap metadata = mapType.metadata();
51 metadata["isHTTPS"] = isHTTPS;
52
53 mapType = QGeoMapType(mapType.style(), mapType.name(), mapType.description(), mapType.mobile(),
54 mapType.night(), mapType.mapId(), mapType.pluginName(), mapType.cameraCapabilities(),
55 metadata);
56 }
57
QGeoTileProviderOsm(QNetworkAccessManager * nm,const QGeoMapType & mapType,const QVector<TileProvider * > & providers,const QGeoCameraCapabilities & cameraCapabilities)58 QGeoTileProviderOsm::QGeoTileProviderOsm(QNetworkAccessManager *nm,
59 const QGeoMapType &mapType,
60 const QVector<TileProvider *> &providers,
61 const QGeoCameraCapabilities &cameraCapabilities)
62 : m_nm(nm), m_provider(nullptr), m_mapType(mapType), m_status(Idle), m_cameraCapabilities(cameraCapabilities)
63 {
64 for (int i = 0; i < providers.size(); ++i) {
65 TileProvider *p = providers[i];
66 if (!m_provider)
67 m_providerId = i;
68 addProvider(p);
69 }
70
71 if (!m_provider || m_provider->isValid())
72 m_status = Resolved;
73
74 if (m_provider && m_provider->isValid())
75 setSSL(m_mapType, m_provider->isHTTPS());
76
77 connect(this, &QGeoTileProviderOsm::resolutionFinished, this, &QGeoTileProviderOsm::updateCameraCapabilities);
78 }
79
~QGeoTileProviderOsm()80 QGeoTileProviderOsm::~QGeoTileProviderOsm()
81 {
82 }
83
tileAddress(int x,int y,int z) const84 QUrl QGeoTileProviderOsm::tileAddress(int x, int y, int z) const
85 {
86 if (m_status != Resolved || !m_provider)
87 return QUrl();
88 return m_provider->tileAddress(x, y, z);
89 }
90
mapCopyRight() const91 QString QGeoTileProviderOsm::mapCopyRight() const
92 {
93 if (m_status != Resolved || !m_provider)
94 return QString();
95 return m_provider->mapCopyRight();
96 }
97
dataCopyRight() const98 QString QGeoTileProviderOsm::dataCopyRight() const
99 {
100 if (m_status != Resolved || !m_provider)
101 return QString();
102 return m_provider->dataCopyRight();
103 }
104
styleCopyRight() const105 QString QGeoTileProviderOsm::styleCopyRight() const
106 {
107 if (m_status != Resolved || !m_provider)
108 return QString();
109 return m_provider->styleCopyRight();
110 }
111
format() const112 QString QGeoTileProviderOsm::format() const
113 {
114 if (m_status != Resolved || !m_provider)
115 return QString();
116 return m_provider->format();
117 }
118
minimumZoomLevel() const119 int QGeoTileProviderOsm::minimumZoomLevel() const
120 {
121 if (m_status != Resolved || !m_provider)
122 return 0;
123 return m_provider->minimumZoomLevel();
124 }
125
maximumZoomLevel() const126 int QGeoTileProviderOsm::maximumZoomLevel() const
127 {
128 if (m_status != Resolved || !m_provider)
129 return 20;
130 return m_provider->maximumZoomLevel();
131 }
132
isHighDpi() const133 bool QGeoTileProviderOsm::isHighDpi() const
134 {
135 if (!m_provider)
136 return false;
137 return m_provider->isHighDpi();
138 }
139
timestamp() const140 const QDateTime QGeoTileProviderOsm::timestamp() const
141 {
142 if (!m_provider)
143 return QDateTime();
144 return m_provider->timestamp();
145 }
146
cameraCapabilities() const147 QGeoCameraCapabilities QGeoTileProviderOsm::cameraCapabilities() const
148 {
149 return m_cameraCapabilities;
150 }
151
mapType() const152 const QGeoMapType &QGeoTileProviderOsm::mapType() const
153 {
154 return m_mapType;
155 }
156
isValid() const157 bool QGeoTileProviderOsm::isValid() const
158 {
159 if (m_status != Resolved || !m_provider)
160 return false;
161 return m_provider->isValid();
162 }
163
isResolved() const164 bool QGeoTileProviderOsm::isResolved() const
165 {
166 return (m_status == Resolved);
167 }
168
resolveProvider()169 void QGeoTileProviderOsm::resolveProvider()
170 {
171 if (m_status == Resolved || m_status == Resolving)
172 return;
173
174 m_status = Resolving;
175 // Provider can't be null while on Idle status.
176 connect(m_provider, &TileProvider::resolutionFinished, this, &QGeoTileProviderOsm::onResolutionFinished);
177 connect(m_provider, &TileProvider::resolutionError, this, &QGeoTileProviderOsm::onResolutionError);
178 m_provider->resolveProvider();
179 }
180
disableRedirection()181 void QGeoTileProviderOsm::disableRedirection()
182 {
183 if (m_provider && m_provider->isValid())
184 return;
185 bool found = false;
186 for (TileProvider *p: m_providerList) {
187 if (p->isValid() && !found) {
188 m_provider = p;
189 m_providerId = m_providerList.indexOf(p);
190 found = true;
191 }
192 p->disconnect(this);
193 }
194 m_status = Resolved;
195 }
196
onResolutionFinished(TileProvider * provider)197 void QGeoTileProviderOsm::onResolutionFinished(TileProvider *provider)
198 {
199 Q_UNUSED(provider);
200 // provider and m_provider are the same, at this point. m_status is Resolving.
201 m_status = Resolved;
202 emit resolutionFinished(this);
203 }
204
onResolutionError(TileProvider * provider)205 void QGeoTileProviderOsm::onResolutionError(TileProvider *provider)
206 {
207 Q_UNUSED(provider);
208 // provider and m_provider are the same at this point. m_status is Resolving.
209 if (!m_provider || m_provider->isInvalid()) {
210 m_provider = nullptr;
211 m_status = Resolved;
212 if (m_providerId >= m_providerList.size() -1) { // no hope left
213 emit resolutionError(this);
214 return;
215 }
216 // Advance the pointer in the provider list, and possibly start resolution on the next in the list.
217 for (int i = m_providerId + 1; i < m_providerList.size(); ++i) {
218 m_providerId = i;
219 TileProvider *p = m_providerList[m_providerId];
220 if (!p->isInvalid()) {
221 m_provider = p;
222 if (!p->isValid()) {
223 m_status = Idle;
224 #if 0 // leaving triggering the retry to the tile fetcher, instead of constantly spinning it in here.
225 m_status = Resolving;
226 p->resolveProvider();
227 #endif
228 emit resolutionRequired();
229 }
230 break;
231 }
232 }
233 if (!m_provider)
234 emit resolutionError(this);
235 } else if (m_provider->isValid()) {
236 m_status = Resolved;
237 emit resolutionFinished(this);
238 } else { // still not resolved. But network error is recoverable.
239 m_status = Idle;
240 #if 0 // leaving triggering the retry to the tile fetcher
241 m_provider->resolveProvider();
242 #endif
243 }
244 }
245
updateCameraCapabilities()246 void QGeoTileProviderOsm::updateCameraCapabilities()
247 {
248 // Set proper min/max ZoomLevel coming from the json, if available.
249 m_cameraCapabilities.setMinimumZoomLevel(minimumZoomLevel());
250 m_cameraCapabilities.setMaximumZoomLevel(maximumZoomLevel());
251
252 m_mapType = QGeoMapType(m_mapType.style(), m_mapType.name(), m_mapType.description(), m_mapType.mobile(),
253 m_mapType.night(), m_mapType.mapId(), m_mapType.pluginName(), m_cameraCapabilities,
254 m_mapType.metadata());
255
256 if (m_provider && m_provider->isValid())
257 setSSL(m_mapType, m_provider->isHTTPS());
258 }
259
addProvider(TileProvider * provider)260 void QGeoTileProviderOsm::addProvider(TileProvider *provider)
261 {
262 if (!provider)
263 return;
264 QScopedPointer<TileProvider> p(provider);
265 if (provider->status() == TileProvider::Invalid)
266 return; // if the provider is already resolved and invalid, no point in adding it.
267
268 provider = p.take();
269 provider->setNetworkManager(m_nm);
270 provider->setParent(this);
271 m_providerList.append(provider);
272 if (!m_provider)
273 m_provider = provider;
274 }
275
276
277 /*
278 Class TileProvder
279 */
280
sort2(int & a,int & b)281 static void sort2(int &a, int &b)
282 {
283 if (a > b) {
284 int temp=a;
285 a=b;
286 b=temp;
287 }
288 }
289
TileProvider()290 TileProvider::TileProvider() : m_status(Invalid), m_nm(nullptr), m_timestamp(defaultTs), m_highDpi(false)
291 {
292
293 }
294
TileProvider(const QUrl & urlRedirector,bool highDpi)295 TileProvider::TileProvider(const QUrl &urlRedirector, bool highDpi)
296 : m_status(Idle), m_urlRedirector(urlRedirector), m_nm(nullptr), m_timestamp(defaultTs), m_highDpi(highDpi)
297 {
298 if (!m_urlRedirector.isValid())
299 m_status = Invalid;
300 }
301
TileProvider(const QString & urlTemplate,const QString & format,const QString & copyRightMap,const QString & copyRightData,bool highDpi,int minimumZoomLevel,int maximumZoomLevel)302 TileProvider::TileProvider(const QString &urlTemplate,
303 const QString &format,
304 const QString ©RightMap,
305 const QString ©RightData,
306 bool highDpi,
307 int minimumZoomLevel,
308 int maximumZoomLevel)
309 : m_status(Invalid), m_nm(nullptr), m_urlTemplate(urlTemplate),
310 m_format(format), m_copyRightMap(copyRightMap), m_copyRightData(copyRightData),
311 m_minimumZoomLevel(minimumZoomLevel), m_maximumZoomLevel(maximumZoomLevel), m_timestamp(defaultTs), m_highDpi(highDpi)
312 {
313 setupProvider();
314 }
315
~TileProvider()316 TileProvider::~TileProvider()
317 {
318 }
319
resolveProvider()320 void TileProvider::resolveProvider()
321 {
322 if (!m_nm)
323 return;
324
325 switch (m_status) {
326 case Resolving:
327 case Invalid:
328 case Valid:
329 return;
330 case Idle:
331 m_status = Resolving;
332 break;
333 }
334
335 QNetworkRequest request;
336 request.setHeader(QNetworkRequest::UserAgentHeader, QByteArrayLiteral("QGeoTileFetcherOsm"));
337 request.setUrl(m_urlRedirector);
338 request.setAttribute(QNetworkRequest::BackgroundRequestAttribute, true);
339 request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork);
340 QNetworkReply *reply = m_nm->get(request);
341 connect(reply, SIGNAL(finished()), this, SLOT(onNetworkReplyFinished()) );
342 connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(onNetworkReplyError(QNetworkReply::NetworkError)));
343 }
344
handleError(QNetworkReply::NetworkError error)345 void TileProvider::handleError(QNetworkReply::NetworkError error)
346 {
347 switch (error) {
348 case QNetworkReply::ConnectionRefusedError:
349 case QNetworkReply::TooManyRedirectsError:
350 case QNetworkReply::InsecureRedirectError:
351 case QNetworkReply::ContentAccessDenied:
352 case QNetworkReply::ContentOperationNotPermittedError:
353 case QNetworkReply::ContentNotFoundError:
354 case QNetworkReply::AuthenticationRequiredError:
355 case QNetworkReply::ContentGoneError:
356 case QNetworkReply::OperationNotImplementedError:
357 case QNetworkReply::ServiceUnavailableError:
358 // Errors we don't expect to recover from in the near future, which
359 // prevent accessing the redirection info but not the actual providers.
360 m_status = Invalid;
361 default:
362 //qWarning() << "QGeoTileProviderOsm network error:" << error;
363 break;
364 }
365 }
366
onNetworkReplyFinished()367 void TileProvider::onNetworkReplyFinished()
368 {
369 QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
370 reply->deleteLater();
371
372 switch (m_status) {
373 case Resolving:
374 m_status = Idle;
375 case Idle: // should not happen
376 case Invalid: // should not happen
377 break;
378 case Valid: // should not happen
379 emit resolutionFinished(this);
380 return;
381 }
382
383 QObject errorEmitter;
384 QMetaObject::Connection errorEmitterConnection = connect(&errorEmitter, &QObject::destroyed, [this](){ this->resolutionError(this); });
385
386 if (reply->error() != QNetworkReply::NoError) {
387 handleError(reply->error());
388 return;
389 }
390 m_status = Invalid;
391
392 /*
393 * The content of a provider information file must be in JSON format, containing
394 * (as of Qt 5.6.2) the following fields:
395 *
396 * {
397 * "Enabled" : bool, (optional)
398 * "UrlTemplate" : "<url template>", (mandatory)
399 * "ImageFormat" : "<image format>", (mandatory)
400 * "MapCopyRight" : "<copyright>", (mandatory)
401 * "DataCopyRight" : "<copyright>", (mandatory)
402 * "StyleCopyRight" : "<copyright>", (optional)
403 * "MinimumZoomLevel" : <minimumZoomLevel>, (optional)
404 * "MaximumZoomLevel" : <maximumZoomLevel>, (optional)
405 * "Timestamp" : <timestamp>, (optional)
406 * }
407 *
408 * Enabled is optional, and allows to temporarily disable a tile provider if it becomes
409 * unavailable, without making the osm plugin fire requests to it. Default is true.
410 *
411 * MinimumZoomLevel and MaximumZoomLevel are also optional, and allow to prevent invalid tile
412 * requests to the providers, if they do not support the specific ZL. Default is 0 and 20,
413 * respectively.
414 *
415 * UrlTemplate is required, and is the tile url template, with %x, %y and %z as
416 * placeholders for the actual parameters.
417 * Example:
418 * http://localhost:8080/maps/%z/%x/%y.png
419 *
420 * ImageFormat is required, and is the format of the tile.
421 * Examples:
422 * "png", "jpg"
423 *
424 * MapCopyRight is required and is the string that will be displayed in the "Map (c)" part
425 * of the on-screen copyright notice. Can be an empty string.
426 * Example:
427 * "<a href='http://www.mapquest.com/'>MapQuest</a>"
428 *
429 * DataCopyRight is required and is the string that will be displayed in the "Data (c)" part
430 * of the on-screen copyright notice. Can be an empty string.
431 * Example:
432 * "<a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors"
433 *
434 * StyleCopyRight is optional and is the string that will be displayed in the optional "Style (c)" part
435 * of the on-screen copyright notice.
436 *
437 * Timestamp is optional, and if set will cause QtLocation to clear the content of the cache older
438 * than this timestamp. The purpose is to prevent mixing tiles from different providers in the cache
439 * upon provider change. The value must be a string in ISO 8601 format (see Qt::ISODate)
440 */
441
442 QJsonParseError error;
443 QJsonDocument d = QJsonDocument::fromJson(reply->readAll(), &error);
444 if (error.error != QJsonParseError::NoError) {
445 qWarning() << "QGeoTileProviderOsm: Error parsing redirection data: "<<error.errorString() << "at "<<m_urlRedirector;
446 return;
447 }
448 if (!d.isObject()) {
449 qWarning() << "QGeoTileProviderOsm: Invalid redirection data" << "at "<<m_urlRedirector;
450 return;
451 }
452 const QJsonObject json = d.object();
453 const QJsonValue urlTemplate = json.value(QLatin1String("UrlTemplate"));
454 const QJsonValue imageFormat = json.value(QLatin1String("ImageFormat"));
455 const QJsonValue copyRightMap = json.value(QLatin1String("MapCopyRight"));
456 const QJsonValue copyRightData = json.value(QLatin1String("DataCopyRight"));
457 if ( urlTemplate == QJsonValue::Undefined
458 || imageFormat == QJsonValue::Undefined
459 || copyRightMap == QJsonValue::Undefined
460 || copyRightData == QJsonValue::Undefined
461 || !urlTemplate.isString()
462 || !imageFormat.isString()
463 || !copyRightMap.isString()
464 || !copyRightData.isString()) {
465 qWarning() << "QGeoTileProviderOsm: Incomplete redirection data" << "at "<<m_urlRedirector;
466 return;
467 }
468
469 m_urlTemplate = urlTemplate.toString();
470 m_format = imageFormat.toString();
471 m_copyRightMap = copyRightMap.toString();
472 m_copyRightData = copyRightData.toString();
473
474 const QJsonValue enabled = json.value(QLatin1String("Enabled"));
475 if (enabled.isBool() && ! enabled.toBool()) {
476 qWarning() << "QGeoTileProviderOsm: Tileserver disabled" << "at "<<m_urlRedirector;
477 return;
478 }
479
480 const QJsonValue copyRightStyle = json.value(QLatin1String("StyleCopyRight"));
481 if (copyRightStyle != QJsonValue::Undefined && copyRightStyle.isString())
482 m_copyRightStyle = copyRightStyle.toString();
483
484 m_minimumZoomLevel = 0;
485 m_maximumZoomLevel = 20;
486 const QJsonValue minZoom = json.value(QLatin1String("MinimumZoomLevel"));
487 if (minZoom.isDouble())
488 m_minimumZoomLevel = qBound(0, int(minZoom.toDouble()), maxValidZoom);
489 const QJsonValue maxZoom = json.value(QLatin1String("MaximumZoomLevel"));
490 if (maxZoom.isDouble())
491 m_maximumZoomLevel = qBound(0, int(maxZoom.toDouble()), maxValidZoom);
492
493 const QJsonValue ts = json.value(QLatin1String("Timestamp"));
494 if (ts.isString())
495 m_timestamp = QDateTime::fromString(ts.toString(), Qt::ISODate);
496
497 setupProvider();
498 if (isValid()) {
499 QObject::disconnect(errorEmitterConnection);
500 emit resolutionFinished(this);
501 }
502 }
503
onNetworkReplyError(QNetworkReply::NetworkError error)504 void TileProvider::onNetworkReplyError(QNetworkReply::NetworkError error)
505 {
506 if (m_status == Resolving)
507 m_status = Idle;
508
509 handleError(error);
510 static_cast<QNetworkReply *>(sender())->deleteLater();
511 emit resolutionError(this);
512 }
513
setupProvider()514 void TileProvider::setupProvider()
515 {
516 if (m_urlTemplate.isEmpty())
517 return;
518
519 if (m_format.isEmpty())
520 return;
521
522 if (m_minimumZoomLevel < 0 || m_minimumZoomLevel > 30)
523 return;
524
525 if (m_maximumZoomLevel < 0 || m_maximumZoomLevel > 30 || m_maximumZoomLevel < m_minimumZoomLevel)
526 return;
527
528 // Currently supporting only %x, %y and &z
529 int offset[3];
530 offset[0] = m_urlTemplate.indexOf(QLatin1String("%x"));
531 if (offset[0] < 0)
532 return;
533
534 offset[1] = m_urlTemplate.indexOf(QLatin1String("%y"));
535 if (offset[1] < 0)
536 return;
537
538 offset[2] = m_urlTemplate.indexOf(QLatin1String("%z"));
539 if (offset[2] < 0)
540 return;
541
542 int sortedOffsets[3];
543 std::copy(offset, offset + 3, sortedOffsets);
544 sort2(sortedOffsets[0] ,sortedOffsets[1]);
545 sort2(sortedOffsets[1] ,sortedOffsets[2]);
546 sort2(sortedOffsets[0] ,sortedOffsets[1]);
547
548 int min = sortedOffsets[0];
549 int max = sortedOffsets[2];
550 int mid = sortedOffsets[1];
551
552 // Initing LUT
553 for (int i=0; i<3; i++) {
554 if (offset[0] == sortedOffsets[i])
555 paramsLUT[i] = 0;
556 else if (offset[1] == sortedOffsets[i])
557 paramsLUT[i] = 1;
558 else
559 paramsLUT[i] = 2;
560 }
561
562 m_urlPrefix = m_urlTemplate.mid(0 , min);
563 m_urlSuffix = m_urlTemplate.mid(max + 2, m_urlTemplate.size() - max - 2);
564
565 paramsSep[0] = m_urlTemplate.mid(min + 2, mid - min - 2);
566 paramsSep[1] = m_urlTemplate.mid(mid + 2, max - mid - 2);
567 m_status = Valid;
568 }
569
isValid() const570 bool TileProvider::isValid() const
571 {
572 return m_status == Valid;
573 }
574
isInvalid() const575 bool TileProvider::isInvalid() const
576 {
577 return m_status == Invalid;
578 }
579
isResolved() const580 bool TileProvider::isResolved() const
581 {
582 return (m_status == Valid || m_status == Invalid);
583 }
584
mapCopyRight() const585 QString TileProvider::mapCopyRight() const
586 {
587 return m_copyRightMap;
588 }
589
dataCopyRight() const590 QString TileProvider::dataCopyRight() const
591 {
592 return m_copyRightData;
593 }
594
styleCopyRight() const595 QString TileProvider::styleCopyRight() const
596 {
597 return m_copyRightStyle;
598 }
599
format() const600 QString TileProvider::format() const
601 {
602 return m_format;
603 }
604
minimumZoomLevel() const605 int TileProvider::minimumZoomLevel() const
606 {
607 return m_minimumZoomLevel;
608 }
609
maximumZoomLevel() const610 int TileProvider::maximumZoomLevel() const
611 {
612 return m_maximumZoomLevel;
613 }
614
timestamp() const615 const QDateTime &TileProvider::timestamp() const
616 {
617 return m_timestamp;
618 }
619
isHighDpi() const620 bool TileProvider::isHighDpi() const
621 {
622 return m_highDpi;
623 }
624
isHTTPS() const625 bool TileProvider::isHTTPS() const
626 {
627 return m_urlTemplate.startsWith(QStringLiteral("https"));
628 }
629
setStyleCopyRight(const QString & copyright)630 void TileProvider::setStyleCopyRight(const QString ©right)
631 {
632 m_copyRightStyle = copyright;
633 }
634
setTimestamp(const QDateTime & timestamp)635 void TileProvider::setTimestamp(const QDateTime ×tamp)
636 {
637 m_timestamp = timestamp;
638 }
639
tileAddress(int x,int y,int z) const640 QUrl TileProvider::tileAddress(int x, int y, int z) const
641 {
642 if (z < m_minimumZoomLevel || z > m_maximumZoomLevel)
643 return QUrl();
644 int params[3] = { x, y, z};
645 QString url;
646 url += m_urlPrefix;
647 url += QString::number(params[paramsLUT[0]]);
648 url += paramsSep[0];
649 url += QString::number(params[paramsLUT[1]]);
650 url += paramsSep[1];
651 url += QString::number(params[paramsLUT[2]]);
652 url += m_urlSuffix;
653 return QUrl(url);
654 }
655
setNetworkManager(QNetworkAccessManager * nm)656 void TileProvider::setNetworkManager(QNetworkAccessManager *nm)
657 {
658 m_nm = nm;
659 }
660
status() const661 TileProvider::Status TileProvider::status() const
662 {
663 return m_status;
664 }
665
666
667 QT_END_NAMESPACE
668