Skip to content

Commit

Permalink
support multiple URLs/sources for vector tiles (#58155)
Browse files Browse the repository at this point in the history
* support multiple URLs/sources for vector tiles

* fix ++ op

* fix test

* address review

* review suggestions

* fix clazy warnings

* fix clazy warnings

* again
  • Loading branch information
3nids authored Sep 2, 2024
1 parent 1a974f5 commit 4d150a8
Show file tree
Hide file tree
Showing 10 changed files with 469 additions and 310 deletions.
8 changes: 6 additions & 2 deletions src/analysis/processing/qgsalgorithmdownloadvectortiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,14 @@ QVariantMap QgsDownloadVectorTilesAlgorithm::processAlgorithm( const QVariantMap
if ( feedback->isCanceled() )
break;

if ( !rawTile.data.isEmpty() )
// TODO: at the moment, it handles single source only of tiles
// takes the first one
const QByteArray data = rawTile.data.first();

if ( !data.isEmpty() )
{
QByteArray gzipTileData;
QgsZipUtils::encodeGzip( rawTile.data, gzipTileData );
QgsZipUtils::encodeGzip( data, gzipTileData );
int rowTMS = pow( 2, rawTile.id.zoomLevel() ) - rawTile.id.row() - 1;
writer->setTileData( rawTile.id.zoomLevel(), rawTile.id.column(), rowTMS, gzipTileData );
}
Expand Down
10 changes: 8 additions & 2 deletions src/core/vectortile/qgsvectortiledataprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
#include <QNetworkRequest>
#include <QImage>


int QgsVectorTileDataProvider::DATA_COLUMN = QNetworkRequest::User + 1;
int QgsVectorTileDataProvider::DATA_ROW = QNetworkRequest::User + 2;
int QgsVectorTileDataProvider::DATA_ZOOM = QNetworkRequest::User + 3;
int QgsVectorTileDataProvider::DATA_SOURCE_ID = QNetworkRequest::User + 4;

QgsVectorTileDataProvider::QgsVectorTileDataProvider(
const QString &uri,
const ProviderOptions &options,
Expand Down Expand Up @@ -69,11 +75,11 @@ bool QgsVectorTileDataProvider::supportsAsync() const
return false;
}

QNetworkRequest QgsVectorTileDataProvider::tileRequest( const QgsTileMatrixSet &, const QgsTileXYZ &, Qgis::RendererUsage ) const
QList<QNetworkRequest> QgsVectorTileDataProvider::tileRequests( const QgsTileMatrixSet &, const QgsTileXYZ &, Qgis::RendererUsage ) const
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS

return QNetworkRequest();
return QList<QNetworkRequest>();
}

QVariantMap QgsVectorTileDataProvider::styleDefinition() const
Expand Down
23 changes: 22 additions & 1 deletion src/core/vectortile/qgsvectortiledataprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class QgsVectorTileMatrixSet;
class QgsVectorTileDataProviderSharedData
{
public:

QgsVectorTileDataProviderSharedData();

/**
Expand Down Expand Up @@ -77,6 +78,15 @@ class CORE_EXPORT QgsVectorTileDataProvider : public QgsDataProvider

public:

//! Role to set column attribute in the request so it can be retrieved later
static int DATA_COLUMN;
//! Role to set row attribute in the request so it can be retrieved later
static int DATA_ROW;
//! Role to set zoom attribute in the request so it can be retrieved later
static int DATA_ZOOM;
//! Role to set source ID attribute in the request so it can be retrieved later
static int DATA_SOURCE_ID;

/**
* Constructor for QgsVectorTileDataProvider, with the specified \a uri.
*/
Expand Down Expand Up @@ -107,6 +117,17 @@ class CORE_EXPORT QgsVectorTileDataProvider : public QgsDataProvider
*/
virtual QString sourcePath() const = 0;


/**
* Returns the list of source paths for the data.
* \since QGIS 3.40
*/
virtual QgsStringMap sourcePaths() const
{
return { { QString(), sourcePath() } };
}


/**
* Returns a clone of the data provider.
*/
Expand Down Expand Up @@ -139,7 +160,7 @@ class CORE_EXPORT QgsVectorTileDataProvider : public QgsDataProvider
*
* The default implementation returns an invalid request.
*/
virtual QNetworkRequest tileRequest( const QgsTileMatrixSet &tileMatrixSet, const QgsTileXYZ &id, Qgis::RendererUsage usage ) const;
virtual QList<QNetworkRequest> tileRequests( const QgsTileMatrixSet &tileMatrixSet, const QgsTileXYZ &id, Qgis::RendererUsage usage ) const;

/**
* Returns the style definition for the provider, if available.
Expand Down
48 changes: 34 additions & 14 deletions src/core/vectortile/qgsvectortileloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ void QgsVectorTileLoader::downloadBlocking()
return; // nothing to do
}

QgsDebugMsgLevel( QStringLiteral( "Starting event loop with %1 requests" ).arg( mReplies.count() ), 2 );
int repliesCount = std::accumulate( mReplies.constBegin(), mReplies.constEnd(), 0, []( int count, QList<QgsTileDownloadManagerReply *> replies ) {return count + replies.count();} );
Q_UNUSED( repliesCount )
QgsDebugMsgLevel( QStringLiteral( "Starting event loop with %1 requests" ).arg( repliesCount ), 2 );

mEventLoop->exec( QEventLoop::ExcludeUserInputEvents );

Expand All @@ -78,20 +80,25 @@ void QgsVectorTileLoader::downloadBlocking()

void QgsVectorTileLoader::loadFromNetworkAsync( const QgsTileXYZ &id, const QgsTileMatrixSet &tileMatrixSet, const QgsVectorTileDataProvider *provider, Qgis::RendererUsage usage )
{
QNetworkRequest request = provider->tileRequest( tileMatrixSet, id, usage );
const QList<QNetworkRequest> requests = provider->tileRequests( tileMatrixSet, id, usage );

QgsTileDownloadManagerReply *reply = QgsApplication::tileDownloadManager()->get( request );
connect( reply, &QgsTileDownloadManagerReply::finished, this, &QgsVectorTileLoader::tileReplyFinished );
mReplies << reply;
for ( const QNetworkRequest &request : requests )
{
QgsTileDownloadManagerReply *reply = QgsApplication::tileDownloadManager()->get( request );
connect( reply, &QgsTileDownloadManagerReply::finished, this, &QgsVectorTileLoader::tileReplyFinished );
mReplies[id].append( reply );
}
}

void QgsVectorTileLoader::tileReplyFinished()
{
QgsTileDownloadManagerReply *reply = qobject_cast<QgsTileDownloadManagerReply *>( sender() );

int reqX = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QNetworkRequest::User + 1 ) ).toInt();
int reqY = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QNetworkRequest::User + 2 ) ).toInt();
int reqZ = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QNetworkRequest::User + 3 ) ).toInt();
int reqX = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_COLUMN ) ).toInt();
int reqY = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_ROW ) ).toInt();
int reqZ = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_ZOOM ) ).toInt();
QString sourceId = reply->request().attribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_SOURCE_ID ) ).toString();

QgsTileXYZ tileID( reqX, reqY, reqZ );

if ( reply->error() == QNetworkReply::NoError )
Expand All @@ -100,10 +107,15 @@ void QgsVectorTileLoader::tileReplyFinished()

QgsDebugMsgLevel( QStringLiteral( "Tile download successful: " ) + tileID.toString(), 2 );
QByteArray rawData = reply->data();
mReplies.removeOne( reply );
mReplies[tileID].removeOne( reply );
mPendingRawData[tileID][sourceId] = rawData;
reply->deleteLater();

emit tileRequestFinished( QgsVectorTileRawData( tileID, rawData ) );
if ( mReplies[tileID].count() == 0 )
{
mReplies.remove( tileID );
emit tileRequestFinished( QgsVectorTileRawData( tileID, mPendingRawData.take( tileID ) ) );
}
}
else
{
Expand All @@ -116,10 +128,14 @@ void QgsVectorTileLoader::tileReplyFinished()
}

QgsDebugError( QStringLiteral( "Tile download failed! " ) + reply->errorString() );
mReplies.removeOne( reply );
mReplies[tileID].removeOne( reply );
reply->deleteLater();

emit tileRequestFinished( QgsVectorTileRawData( tileID, QByteArray() ) );
if ( mReplies[tileID].count() == 0 )
{
mReplies.remove( tileID );
emit tileRequestFinished( QgsVectorTileRawData( tileID ) );
}
}

if ( mReplies.isEmpty() )
Expand All @@ -131,8 +147,12 @@ void QgsVectorTileLoader::tileReplyFinished()

void QgsVectorTileLoader::canceled()
{
QgsDebugMsgLevel( QStringLiteral( "Canceling %1 pending requests" ).arg( mReplies.count() ), 2 );
qDeleteAll( mReplies );
int repliesCount = std::accumulate( mReplies.constBegin(), mReplies.constEnd(), 0, []( int count, QList<QgsTileDownloadManagerReply *> replies ) {return count + replies.count();} );
Q_UNUSED( repliesCount )
QgsDebugMsgLevel( QStringLiteral( "Canceling %1 pending requests" ).arg( repliesCount ), 2 );
QHash<QgsTileXYZ, QList<QgsTileDownloadManagerReply *>>::iterator it = mReplies.begin();
for ( ; it != mReplies.end(); ++it )
qDeleteAll( it.value() );
mReplies.clear();

// stop blocking download
Expand Down
22 changes: 15 additions & 7 deletions src/core/vectortile/qgsvectortileloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,23 @@ class QByteArray;
class QNetworkReply;
class QEventLoop;


/**
* \ingroup core
* \brief Keeps track of raw tile data that need to be decoded
* \brief Keeps track of raw tile data from one or more sources that need to be decoded
*
* \since QGIS 3.14
*/
class CORE_EXPORT QgsVectorTileRawData
{
public:
//! Constructs a raw tile object
QgsVectorTileRawData( QgsTileXYZ tileID = QgsTileXYZ(), const QByteArray &raw = QByteArray() )
: id( tileID ), tileGeometryId( tileID ), data( raw ) {}
//! Constructs a raw tile object for single source
QgsVectorTileRawData( QgsTileXYZ tileID = QgsTileXYZ(), const QByteArray &data = QByteArray() )
: id( tileID ), tileGeometryId( tileID ), data( { { QString(), data } } ) {}

//! Constructs a raw tile object for one or more sources
QgsVectorTileRawData( QgsTileXYZ tileID, const QMap<QString, QByteArray> &data )
: id( tileID ), tileGeometryId( tileID ), data( data ) {}

//! Tile position in tile matrix set
QgsTileXYZ id;
Expand All @@ -54,8 +59,8 @@ class CORE_EXPORT QgsVectorTileRawData
*/
QgsTileXYZ tileGeometryId;

//! Raw tile data
QByteArray data;
//! Raw tile data by source ID
QMap<QString, QByteArray> data;
};


Expand Down Expand Up @@ -113,7 +118,10 @@ class CORE_EXPORT QgsVectorTileLoader : public QObject
QgsFeedback *mFeedback;

//! Running tile requests
QList<QgsTileDownloadManagerReply *> mReplies;
QHash<QgsTileXYZ, QList<QgsTileDownloadManagerReply *>> mReplies;

//! Raw data is stored until all sources are fetched
QHash<QgsTileXYZ, QMap<QString, QByteArray>> mPendingRawData;

QString mError;
};
Expand Down
Loading

0 comments on commit 4d150a8

Please sign in to comment.