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