QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgsvectortileloader.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectortileloader.cpp
3 --------------------------------------
4 Date : March 2020
5 Copyright : (C) 2020 by Martin Dobias
6 Email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsvectortileloader.h"
17
18#include "qgsapplication.h"
19#include "qgsfeedback.h"
20#include "qgslogger.h"
23#include "qgsvectortileutils.h"
24
25#include <QEventLoop>
26#include <QString>
27
28#include "moc_qgsvectortileloader.cpp"
29
30using namespace Qt::StringLiterals;
31
32QgsVectorTileLoader::QgsVectorTileLoader( const QgsVectorTileDataProvider *provider, const QgsTileMatrixSet &tileMatrixSet, const QgsTileRange &range, int zoomLevel, const QPointF &viewCenter, QgsFeedback *feedback, Qgis::RendererUsage usage )
33 : mEventLoop( new QEventLoop )
34 , mFeedback( feedback )
35{
36 if ( feedback )
37 {
38 connect( feedback, &QgsFeedback::canceled, this, &QgsVectorTileLoader::canceled, Qt::QueuedConnection );
39
40 // rendering could have been canceled before we started to listen to canceled() signal
41 // so let's check before doing the download and maybe quit prematurely
42 if ( feedback->isCanceled() )
43 return;
44 }
45
46 QgsDebugMsgLevel( u"Starting network loader"_s, 2 );
47 QVector<QgsTileXYZ> tiles = tileMatrixSet.tilesInRange( range, zoomLevel );
49 for ( QgsTileXYZ id : std::as_const( tiles ) )
50 {
51 loadFromNetworkAsync( id, tileMatrixSet, provider, usage );
52 }
53}
54
56{
57 QgsDebugMsgLevel( u"Terminating network loader"_s, 2 );
58
59 if ( !mReplies.isEmpty() )
60 {
61 // this can happen when the loader is terminated without getting requests finalized
62 // (e.g. downloadBlocking() was not called)
63 canceled();
64 }
65}
66
68{
69 if ( mFeedback && mFeedback->isCanceled() )
70 {
71 QgsDebugMsgLevel( u"downloadBlocking - not staring event loop - canceled"_s, 2 );
72 return; // nothing to do
73 }
74
75 int repliesCount = std::accumulate( mReplies.constBegin(), mReplies.constEnd(), 0, []( int count, QList<QgsTileDownloadManagerReply *> replies ) {return count + replies.count();} );
76 Q_UNUSED( repliesCount )
77 QgsDebugMsgLevel( u"Starting event loop with %1 requests"_s.arg( repliesCount ), 2 );
78
79 mEventLoop->exec( QEventLoop::ExcludeUserInputEvents );
80
81 QgsDebugMsgLevel( u"downloadBlocking finished"_s, 2 );
82
83 Q_ASSERT( mReplies.isEmpty() );
84}
85
86void QgsVectorTileLoader::loadFromNetworkAsync( const QgsTileXYZ &id, const QgsTileMatrixSet &tileMatrixSet, const QgsVectorTileDataProvider *provider, Qgis::RendererUsage usage )
87{
88 const QList<QNetworkRequest> requests = provider->tileRequests( tileMatrixSet, id, usage );
89
90 for ( const QNetworkRequest &request : requests )
91 {
93 connect( reply, &QgsTileDownloadManagerReply::finished, this, &QgsVectorTileLoader::tileReplyFinished );
94 mReplies[id].append( reply );
95 }
96}
97
98void QgsVectorTileLoader::tileReplyFinished()
99{
100 QgsTileDownloadManagerReply *reply = qobject_cast<QgsTileDownloadManagerReply *>( sender() );
101
102 int reqX = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_COLUMN ) ).toInt();
103 int reqY = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_ROW ) ).toInt();
104 int reqZ = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_ZOOM ) ).toInt();
105 QString sourceId = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_SOURCE_ID ) ).toString();
106
107 QgsTileXYZ tileID( reqX, reqY, reqZ );
108
109 if ( reply->error() == QNetworkReply::NoError )
110 {
111 // TODO: handle redirections?
112
113 QgsDebugMsgLevel( u"Tile download successful: "_s + tileID.toString(), 2 );
114 QByteArray rawData = reply->data();
115 mReplies[tileID].removeOne( reply );
116 mPendingRawData[tileID][sourceId] = rawData;
117 reply->deleteLater();
118
119 if ( mReplies[tileID].count() == 0 )
120 {
121 mReplies.remove( tileID );
122 emit tileRequestFinished( QgsVectorTileRawData( tileID, mPendingRawData.take( tileID ) ) );
123 }
124 }
125 else
126 {
127 if ( reply->error() == QNetworkReply::ContentAccessDenied )
128 {
129 if ( reply->data().isEmpty() )
130 mError = tr( "Access denied" );
131 else
132 mError = tr( "Access denied: %1" ).arg( QString( reply->data() ) );
133 }
134
135 QgsDebugError( u"Tile download failed! "_s + reply->errorString() );
136 mReplies[tileID].removeOne( reply );
137 reply->deleteLater();
138
139 if ( mReplies[tileID].count() == 0 )
140 {
141 mReplies.remove( tileID );
142 emit tileRequestFinished( QgsVectorTileRawData( tileID ) );
143 }
144 }
145
146 if ( mReplies.isEmpty() )
147 {
148 // exist the event loop
149 QMetaObject::invokeMethod( mEventLoop.get(), "quit", Qt::QueuedConnection );
150 }
151}
152
153void QgsVectorTileLoader::canceled()
154{
155 int repliesCount = std::accumulate( mReplies.constBegin(), mReplies.constEnd(), 0, []( int count, QList<QgsTileDownloadManagerReply *> replies ) {return count + replies.count();} );
156 Q_UNUSED( repliesCount )
157 QgsDebugMsgLevel( u"Canceling %1 pending requests"_s.arg( repliesCount ), 2 );
158 QHash<QgsTileXYZ, QList<QgsTileDownloadManagerReply *>>::iterator it = mReplies.begin();
159 for ( ; it != mReplies.end(); ++it )
160 qDeleteAll( it.value() );
161 mReplies.clear();
162
163 // stop blocking download
164 mEventLoop->quit();
165
166}
167
169{
170 return mError;
171}
172
174
175QList<QgsVectorTileRawData> QgsVectorTileLoader::blockingFetchTileRawData( const QgsVectorTileDataProvider *provider, const QgsTileMatrixSet &tileMatrixSet, const QPointF &viewCenter, const QgsTileRange &range, int zoomLevel, QgsFeedback *feedback, Qgis::RendererUsage usage )
176{
177 if ( feedback && feedback->isCanceled() )
178 return {};
179
180 QVector<QgsTileXYZ> tiles = tileMatrixSet.tilesInRange( range, zoomLevel );
181
182 // if a tile matrix results in a HUGE number of tile requests, we skip the sort -- it can be expensive
183 if ( tiles.size() < 10000 )
185
186 return provider->readTiles( tileMatrixSet, tiles, feedback, usage );
187}
RendererUsage
Usage of the renderer.
Definition qgis.h:3504
static QgsTileDownloadManager * tileDownloadManager()
Returns the application's tile download manager, used for download of map tiles when rendering.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:55
void canceled()
Internal routines can connect to this signal if they use event loop.
Reply object for tile download manager requests returned from calls to QgsTileDownloadManager::get().
QString errorString() const
Returns error string (only valid when already finished).
QNetworkRequest request() const
Returns the original request for this reply object.
QByteArray data() const
Returns binary data returned in the reply (only valid when already finished).
QNetworkReply::NetworkError error() const
Returns error code (only valid when already finished).
void finished()
Emitted when the reply has finished (either with a success or with a failure).
QgsTileDownloadManagerReply * get(const QNetworkRequest &request)
Starts a request.
Defines a set of tile matrices for multiple zoom levels.
Definition qgstiles.h:278
QVector< QgsTileXYZ > tilesInRange(QgsTileRange range, int zoomLevel) const
Returns a list of tiles in the given tile range.
Definition qgstiles.cpp:432
A range of tiles in a tile matrix.
Definition qgstiles.h:118
Stores coordinates of a tile in a tile matrix set.
Definition qgstiles.h:43
Base class for vector tile layer data providers.
virtual QList< QgsVectorTileRawData > readTiles(const QgsTileMatrixSet &tileMatrixSet, const QVector< QgsTileXYZ > &tiles, QgsFeedback *feedback=nullptr, Qgis::RendererUsage usage=Qgis::RendererUsage::Unknown) const =0
Returns raw tile data for a range of tiles.
static int DATA_ZOOM
Role to set zoom attribute in the request so it can be retrieved later.
static int DATA_ROW
Role to set row attribute in the request so it can be retrieved later.
virtual QList< QNetworkRequest > tileRequests(const QgsTileMatrixSet &tileMatrixSet, const QgsTileXYZ &id, Qgis::RendererUsage usage) const
Returns a network request for a tile.
static int DATA_SOURCE_ID
Role to set source ID attribute in the request so it can be retrieved later.
static int DATA_COLUMN
Role to set column attribute in the request so it can be retrieved later.
void tileRequestFinished(const QgsVectorTileRawData &rawTile)
Emitted when a tile request has finished. If a tile request has failed, the returned raw tile byte ar...
QString error() const
Returns a eventual error that occurred during loading, void if no error.
void downloadBlocking()
Blocks the caller until all asynchronous requests are finished (with a success or a failure).
static QList< QgsVectorTileRawData > blockingFetchTileRawData(const QgsVectorTileDataProvider *provider, const QgsTileMatrixSet &tileMatrixSet, const QPointF &viewCenter, const QgsTileRange &range, int zoomLevel, QgsFeedback *feedback=nullptr, Qgis::RendererUsage usage=Qgis::RendererUsage::Unknown)
Returns raw tile data for the specified range of tiles. Blocks the caller until all tiles are fetched...
QgsVectorTileLoader(const QgsVectorTileDataProvider *provider, const QgsTileMatrixSet &tileMatrixSet, const QgsTileRange &range, int zoomLevel, const QPointF &viewCenter, QgsFeedback *feedback, Qgis::RendererUsage usage)
Constructs tile loader for doing asynchronous requests and starts network requests.
static void sortTilesByDistanceFromCenter(QVector< QgsTileXYZ > &tiles, QPointF center)
Orders tile requests according to the distance from view center (given in tile matrix coords).
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59