QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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 QgsDebugMsgLevel( QStringLiteral( "Starting event loop with %1 requests" ).arg( mReplies.count() ), 2 );
71
72 mEventLoop->exec( QEventLoop::ExcludeUserInputEvents );
73
74 QgsDebugMsgLevel( QStringLiteral( "downloadBlocking finished" ), 2 );
75
76 Q_ASSERT( mReplies.isEmpty() );
77}
78
79void QgsVectorTileLoader::loadFromNetworkAsync( const QgsTileXYZ &id, const QgsTileMatrixSet &tileMatrixSet, const QgsVectorTileDataProvider *provider, Qgis::RendererUsage usage )
80{
81 QNetworkRequest request = provider->tileRequest( tileMatrixSet, id, usage );
82
84 connect( reply, &QgsTileDownloadManagerReply::finished, this, &QgsVectorTileLoader::tileReplyFinished );
85 mReplies << reply;
86}
87
88void QgsVectorTileLoader::tileReplyFinished()
89{
90 QgsTileDownloadManagerReply *reply = qobject_cast<QgsTileDownloadManagerReply *>( sender() );
91
92 int reqX = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QNetworkRequest::User + 1 ) ).toInt();
93 int reqY = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QNetworkRequest::User + 2 ) ).toInt();
94 int reqZ = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QNetworkRequest::User + 3 ) ).toInt();
95 QgsTileXYZ tileID( reqX, reqY, reqZ );
96
97 if ( reply->error() == QNetworkReply::NoError )
98 {
99 // TODO: handle redirections?
100
101 QgsDebugMsgLevel( QStringLiteral( "Tile download successful: " ) + tileID.toString(), 2 );
102 QByteArray rawData = reply->data();
103 mReplies.removeOne( reply );
104 reply->deleteLater();
105
106 emit tileRequestFinished( QgsVectorTileRawData( tileID, rawData ) );
107 }
108 else
109 {
110 if ( reply->error() == QNetworkReply::ContentAccessDenied )
111 {
112 if ( reply->data().isEmpty() )
113 mError = tr( "Access denied" );
114 else
115 mError = tr( "Access denied: %1" ).arg( QString( reply->data() ) );
116 }
117
118 QgsDebugError( QStringLiteral( "Tile download failed! " ) + reply->errorString() );
119 mReplies.removeOne( reply );
120 reply->deleteLater();
121
122 emit tileRequestFinished( QgsVectorTileRawData( tileID, QByteArray() ) );
123 }
124
125 if ( mReplies.isEmpty() )
126 {
127 // exist the event loop
128 QMetaObject::invokeMethod( mEventLoop.get(), "quit", Qt::QueuedConnection );
129 }
130}
131
132void QgsVectorTileLoader::canceled()
133{
134 QgsDebugMsgLevel( QStringLiteral( "Canceling %1 pending requests" ).arg( mReplies.count() ), 2 );
135 qDeleteAll( mReplies );
136 mReplies.clear();
137
138 // stop blocking download
139 mEventLoop->quit();
140
141}
142
144{
145 return mError;
146}
147
149
150QList<QgsVectorTileRawData> QgsVectorTileLoader::blockingFetchTileRawData( const QgsVectorTileDataProvider *provider, const QgsTileMatrixSet &tileMatrixSet, const QPointF &viewCenter, const QgsTileRange &range, int zoomLevel, QgsFeedback *feedback, Qgis::RendererUsage usage )
151{
152 if ( feedback && feedback->isCanceled() )
153 return {};
154
155 QVector<QgsTileXYZ> tiles = tileMatrixSet.tilesInRange( range, zoomLevel );
156
157 // if a tile matrix results in a HUGE number of tile requests, we skip the sort -- it can be expensive
158 if ( tiles.size() < 10000 )
160
161 return provider->readTiles( tileMatrixSet, tiles, feedback, usage );
162}
RendererUsage
Usage of the renderer.
Definition: qgis.h:2803
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:250
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:97
Stores coordinates of a tile in a tile matrix set.
Definition: qgstiles.h:38
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.
virtual QNetworkRequest tileRequest(const QgsTileMatrixSet &tileMatrixSet, const QgsTileXYZ &id, Qgis::RendererUsage usage) const
Returns a network request for a tile.
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 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