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