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