QGIS API Documentation 3.41.0-Master (af5edcb665c)
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#include "moc_qgsvectortileloader.cpp"
18#include "qgslogger.h"
19#include "qgsvectortileutils.h"
20#include "qgsapplication.h"
22#include "qgsfeedback.h"
24
25#include <QEventLoop>
26
27
28QgsVectorTileLoader::QgsVectorTileLoader( const QgsVectorTileDataProvider *provider, const QgsTileMatrixSet &tileMatrixSet, const QgsTileRange &range, int zoomLevel, const QPointF &viewCenter, QgsFeedback *feedback, Qgis::RendererUsage usage )
29 : mEventLoop( new QEventLoop )
30 , mFeedback( feedback )
31{
32 if ( feedback )
33 {
34 connect( feedback, &QgsFeedback::canceled, this, &QgsVectorTileLoader::canceled, Qt::QueuedConnection );
35
36 // rendering could have been canceled before we started to listen to canceled() signal
37 // so let's check before doing the download and maybe quit prematurely
38 if ( feedback->isCanceled() )
39 return;
40 }
41
42 QgsDebugMsgLevel( QStringLiteral( "Starting network loader" ), 2 );
43 QVector<QgsTileXYZ> tiles = tileMatrixSet.tilesInRange( range, zoomLevel );
45 for ( QgsTileXYZ id : std::as_const( tiles ) )
46 {
47 loadFromNetworkAsync( id, tileMatrixSet, provider, usage );
48 }
49}
50
52{
53 QgsDebugMsgLevel( QStringLiteral( "Terminating network loader" ), 2 );
54
55 if ( !mReplies.isEmpty() )
56 {
57 // this can happen when the loader is terminated without getting requests finalized
58 // (e.g. downloadBlocking() was not called)
59 canceled();
60 }
61}
62
64{
65 if ( mFeedback && mFeedback->isCanceled() )
66 {
67 QgsDebugMsgLevel( QStringLiteral( "downloadBlocking - not staring event loop - canceled" ), 2 );
68 return; // nothing to do
69 }
70
71 int repliesCount = std::accumulate( mReplies.constBegin(), mReplies.constEnd(), 0, []( int count, QList<QgsTileDownloadManagerReply *> replies ) {return count + replies.count();} );
72 Q_UNUSED( repliesCount )
73 QgsDebugMsgLevel( QStringLiteral( "Starting event loop with %1 requests" ).arg( repliesCount ), 2 );
74
75 mEventLoop->exec( QEventLoop::ExcludeUserInputEvents );
76
77 QgsDebugMsgLevel( QStringLiteral( "downloadBlocking finished" ), 2 );
78
79 Q_ASSERT( mReplies.isEmpty() );
80}
81
82void QgsVectorTileLoader::loadFromNetworkAsync( const QgsTileXYZ &id, const QgsTileMatrixSet &tileMatrixSet, const QgsVectorTileDataProvider *provider, Qgis::RendererUsage usage )
83{
84 const QList<QNetworkRequest> requests = provider->tileRequests( tileMatrixSet, id, usage );
85
86 for ( const QNetworkRequest &request : requests )
87 {
89 connect( reply, &QgsTileDownloadManagerReply::finished, this, &QgsVectorTileLoader::tileReplyFinished );
90 mReplies[id].append( reply );
91 }
92}
93
94void QgsVectorTileLoader::tileReplyFinished()
95{
96 QgsTileDownloadManagerReply *reply = qobject_cast<QgsTileDownloadManagerReply *>( sender() );
97
98 int reqX = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_COLUMN ) ).toInt();
99 int reqY = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_ROW ) ).toInt();
100 int reqZ = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_ZOOM ) ).toInt();
101 QString sourceId = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_SOURCE_ID ) ).toString();
102
103 QgsTileXYZ tileID( reqX, reqY, reqZ );
104
105 if ( reply->error() == QNetworkReply::NoError )
106 {
107 // TODO: handle redirections?
108
109 QgsDebugMsgLevel( QStringLiteral( "Tile download successful: " ) + tileID.toString(), 2 );
110 QByteArray rawData = reply->data();
111 mReplies[tileID].removeOne( reply );
112 mPendingRawData[tileID][sourceId] = rawData;
113 reply->deleteLater();
114
115 if ( mReplies[tileID].count() == 0 )
116 {
117 mReplies.remove( tileID );
118 emit tileRequestFinished( QgsVectorTileRawData( tileID, mPendingRawData.take( tileID ) ) );
119 }
120 }
121 else
122 {
123 if ( reply->error() == QNetworkReply::ContentAccessDenied )
124 {
125 if ( reply->data().isEmpty() )
126 mError = tr( "Access denied" );
127 else
128 mError = tr( "Access denied: %1" ).arg( QString( reply->data() ) );
129 }
130
131 QgsDebugError( QStringLiteral( "Tile download failed! " ) + reply->errorString() );
132 mReplies[tileID].removeOne( reply );
133 reply->deleteLater();
134
135 if ( mReplies[tileID].count() == 0 )
136 {
137 mReplies.remove( tileID );
139 }
140 }
141
142 if ( mReplies.isEmpty() )
143 {
144 // exist the event loop
145 QMetaObject::invokeMethod( mEventLoop.get(), "quit", Qt::QueuedConnection );
146 }
147}
148
149void QgsVectorTileLoader::canceled()
150{
151 int repliesCount = std::accumulate( mReplies.constBegin(), mReplies.constEnd(), 0, []( int count, QList<QgsTileDownloadManagerReply *> replies ) {return count + replies.count();} );
152 Q_UNUSED( repliesCount )
153 QgsDebugMsgLevel( QStringLiteral( "Canceling %1 pending requests" ).arg( repliesCount ), 2 );
154 QHash<QgsTileXYZ, QList<QgsTileDownloadManagerReply *>>::iterator it = mReplies.begin();
155 for ( ; it != mReplies.end(); ++it )
156 qDeleteAll( it.value() );
157 mReplies.clear();
158
159 // stop blocking download
160 mEventLoop->quit();
161
162}
163
165{
166 return mError;
167}
168
170
171QList<QgsVectorTileRawData> QgsVectorTileLoader::blockingFetchTileRawData( const QgsVectorTileDataProvider *provider, const QgsTileMatrixSet &tileMatrixSet, const QPointF &viewCenter, const QgsTileRange &range, int zoomLevel, QgsFeedback *feedback, Qgis::RendererUsage usage )
172{
173 if ( feedback && feedback->isCanceled() )
174 return {};
175
176 QVector<QgsTileXYZ> tiles = tileMatrixSet.tilesInRange( range, zoomLevel );
177
178 // if a tile matrix results in a HUGE number of tile requests, we skip the sort -- it can be expensive
179 if ( tiles.size() < 10000 )
181
182 return provider->readTiles( tileMatrixSet, tiles, feedback, usage );
183}
RendererUsage
Usage of the renderer.
Definition qgis.h:3259
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:53
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:252
QVector< QgsTileXYZ > tilesInRange(QgsTileRange range, int zoomLevel) const
Returns a list of tiles in the given tile range.
Definition qgstiles.cpp:428
Range of tiles in a tile matrix to be rendered.
Definition qgstiles.h:99
Stores coordinates of a tile in a tile matrix set.
Definition qgstiles.h:40
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.
Keeps track of raw tile data from one or more sources that need to be decoded.
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:39
#define QgsDebugError(str)
Definition qgslogger.h:38