From b39761f8f0ed57831ec68ee1f53de1b42c99a2df Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 2 Mar 2022 15:20:43 +1000 Subject: [PATCH] Ensure that non-EPSG:3857 vector tiles layers can be rendered in the correct place This fixes the rendering of vector tiles layers which are constructed in any non web mercator CRS. (Unfortunately the required information is not recorded in mbtiles packages, so it's necessary to set the correct parameters via API calls when trying to load non-3857 mbtiles.) --- python/core/auto_generated/qgstiles.sip.in | 4 +- .../vectortile/qgsvectortilelayer.sip.in | 7 + .../vectortile/qgsvectortilestructure.sip.in | 197 ++++++++++++++++ python/core/core_auto.sip | 1 + src/core/CMakeLists.txt | 2 + src/core/qgstiles.h | 6 +- src/core/vectortile/qgsvectortilelayer.cpp | 60 ++++- src/core/vectortile/qgsvectortilelayer.h | 18 +- .../vectortile/qgsvectortilelayerrenderer.cpp | 40 ++-- .../vectortile/qgsvectortilelayerrenderer.h | 8 +- .../vectortile/qgsvectortilemvtdecoder.cpp | 22 +- src/core/vectortile/qgsvectortilemvtdecoder.h | 9 +- .../vectortile/qgsvectortilestructure.cpp | 46 ++++ src/core/vectortile/qgsvectortilestructure.h | 215 ++++++++++++++++++ src/core/vectortile/qgsvectortileutils.cpp | 20 +- src/core/vectortile/qgsvectortileutils.h | 22 +- src/gui/qgsmaptoolidentify.cpp | 8 +- tests/src/core/testqgsvectortilewriter.cpp | 10 +- 18 files changed, 619 insertions(+), 76 deletions(-) create mode 100644 python/core/auto_generated/vectortile/qgsvectortilestructure.sip.in create mode 100644 src/core/vectortile/qgsvectortilestructure.cpp create mode 100644 src/core/vectortile/qgsvectortilestructure.h diff --git a/python/core/auto_generated/qgstiles.sip.in b/python/core/auto_generated/qgstiles.sip.in index b4af8ac1471..cd98fce2614 100644 --- a/python/core/auto_generated/qgstiles.sip.in +++ b/python/core/auto_generated/qgstiles.sip.in @@ -117,7 +117,9 @@ Returns a tile matrix for the usual web mercator const QgsPointXY &z0TopLeftPoint, double z0Dimension, int z0MatrixWidth = 1, int z0MatrixHeight = 1 ); %Docstring -Returns a tile matrix for a specific CRS, top left point, zoom level 0 dimension in CRS units +Returns a tile matrix for a specific CRS, top left point, zoom level 0 dimension in CRS units. + +The ``z0Dimension`` argument must specify the dimension (width or height, in map units) of the root tiles in zoom level 0. %End static QgsTileMatrix fromTileMatrix( const int &zoomLevel, const QgsTileMatrix &tileMatrix ); diff --git a/python/core/auto_generated/vectortile/qgsvectortilelayer.sip.in b/python/core/auto_generated/vectortile/qgsvectortilelayer.sip.in index 762e83a87af..474ee070daa 100644 --- a/python/core/auto_generated/vectortile/qgsvectortilelayer.sip.in +++ b/python/core/auto_generated/vectortile/qgsvectortilelayer.sip.in @@ -130,6 +130,13 @@ Constructs a new vector tile layer + QgsVectorTileStructure &tileStructure(); +%Docstring +Returns the vector tile structure. + +.. versionadded:: 3.22.6 +%End + QString sourceType() const; %Docstring Returns type of the data source diff --git a/python/core/auto_generated/vectortile/qgsvectortilestructure.sip.in b/python/core/auto_generated/vectortile/qgsvectortilestructure.sip.in new file mode 100644 index 00000000000..2fb4ddf9ee7 --- /dev/null +++ b/python/core/auto_generated/vectortile/qgsvectortilestructure.sip.in @@ -0,0 +1,197 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/vectortile/qgsvectortilestructure.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + +class QgsVectorTileStructure +{ +%Docstring(signature="appended") +Encapsulates properties of a vector tile structure, including tile origins and scaling information. + +.. versionadded:: 3.22.6 +%End + +%TypeHeaderCode +#include "qgsvectortilestructure.h" +%End + public: + + static QgsVectorTileStructure fromWebMercator(); +%Docstring +Returns a vector tile structure corresponding to the standard web mercator/GoogleCRS84Quad setup. +%End + + double z0xMin() const; +%Docstring +Returns the minimum x coordinate for zoom level 0. + +This property is used when scaling raw vector tile coordinates. + +.. seealso:: :py:func:`setZ0xMin` +%End + + void setZ0xMin( double x ); +%Docstring +Sets the minimum ``x`` coordinate for zoom level 0. + +This property is used when scaling raw vector tile coordinates. + +.. seealso:: :py:func:`z0xMin` +%End + + double z0xMax() const; +%Docstring +Returns the maximum x coordinate for zoom level 0. + +This property is used when scaling raw vector tile coordinates. + +.. seealso:: :py:func:`setZ0xMax` +%End + + void setZ0xMax( double x ); +%Docstring +Sets the maximum ``x`` coordinate for zoom level 0. + +This property is used when scaling raw vector tile coordinates. + +.. seealso:: :py:func:`z0xMin` +%End + + double z0yMin() const; +%Docstring +Returns the minimum y coordinate for zoom level 0. + +This property is used when scaling raw vector tile coordinates. + +.. seealso:: :py:func:`setZ0xMin` +%End + + void setZ0yMin( double y ); +%Docstring +Sets the minimum ``y`` coordinate for zoom level 0. + +This property is used when scaling raw vector tile coordinates. + +.. seealso:: :py:func:`z0yMin` +%End + + double z0yMax() const; +%Docstring +Returns the maximum y coordinate for zoom level 0. + +This property is used when scaling raw vector tile coordinates. + +.. seealso:: :py:func:`setZ0yMax` +%End + + void setZ0yMax( double y ); +%Docstring +Sets the maximum ``y`` coordinate for zoom level 0. + +This property is used when scaling raw vector tile coordinates. + +.. seealso:: :py:func:`z0yMin` +%End + + double z0Dimension() const; +%Docstring +Returns the dimension (width or height, in map units) of the root tiles in zoom level 0. + +.. seealso:: :py:func:`setZ0Dimension` +%End + + void setZ0Dimension( double dimension ); +%Docstring +Sets the ``dimension`` (width or height, in map units) of the root tiles in zoom level 0. + +.. seealso:: :py:func:`z0Dimension` +%End + + double z0Scale() const; +%Docstring +Returns the scale denominator corresponding to root tiles in zoom level 0. + +.. seealso:: :py:func:`setZ0Dimension` +%End + + void setZ0Scale( double scale ); +%Docstring +Sets the ``scale`` denominator of the root tiles in zoom level 0. + +.. seealso:: :py:func:`z0Scale` +%End + + QgsCoordinateReferenceSystem crs() const; +%Docstring +Returns the coordinate reference system associated with the tiles. + +.. seealso:: :py:func:`setCrs` +%End + + void setCrs( const QgsCoordinateReferenceSystem &crs ); +%Docstring +Sets the coordinate reference system associated with the tiles. + +.. seealso:: :py:func:`crs` +%End + + int minimumZoom() const; +%Docstring +Returns the minimum zoom level for tiles (where negative = unconstrained). + +.. seealso:: :py:func:`setMinimumZoom` +%End + + void setMinimumZoom( int zoom ); +%Docstring +Sets the minimum ``zoom`` level for tiles (where negative = unconstrained). + +.. seealso:: :py:func:`minimumZoom` +%End + + int maximumZoom() const; +%Docstring +Returns the maximum zoom level for tiles (where negative = unconstrained). + +.. seealso:: :py:func:`setMaximumZoom` +%End + + void setMaximumZoom( int zoom ); +%Docstring +Sets the maximum ``zoom`` level for tiles (where negative = unconstrained). + +.. seealso:: :py:func:`maximumZoom` +%End + + QgsTileMatrix tileMatrix( int zoom ) const; +%Docstring +Returns the tile matrix corresponding to the specified ``zoom``. +%End + + double scaleToZoom( double scale ) const; +%Docstring +Finds zoom level given a map ``scale`` denominator. +%End + + int scaleToZoomLevel( double scale ) const; +%Docstring +Finds the best fitting (integer) zoom level given a map ``scale`` denominator. + +Values are constrained to the zoom levels between :py:func:`~QgsVectorTileStructure.minimumZoom` and :py:func:`~QgsVectorTileStructure.maximumZoom`. +%End + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/vectortile/qgsvectortilestructure.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index eca22c99378..06384159f17 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -692,6 +692,7 @@ %Include auto_generated/vectortile/qgsvectortilelabeling.sip %Include auto_generated/vectortile/qgsvectortilelayer.sip %Include auto_generated/vectortile/qgsvectortilerenderer.sip +%Include auto_generated/vectortile/qgsvectortilestructure.sip %Include auto_generated/vectortile/qgsvectortilewriter.sip %Include auto_generated/gps/qgsqtlocationconnection.sip %Include auto_generated/gps/qgsgpsconnectionregistry.sip diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1c183546ab8..16fb6abea24 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -845,6 +845,7 @@ set(QGIS_CORE_SRCS vectortile/qgsvectortilemvtencoder.cpp vectortile/qgsvectortilemvtutils.cpp vectortile/qgsvectortileprovidermetadata.cpp + vectortile/qgsvectortilestructure.cpp vectortile/qgsvectortileutils.cpp vectortile/qgsvectortilewriter.cpp @@ -1788,6 +1789,7 @@ set(QGIS_CORE_HDRS vectortile/qgsvectortilemvtutils.h vectortile/qgsvectortileprovidermetadata.h vectortile/qgsvectortilerenderer.h + vectortile/qgsvectortilestructure.h vectortile/qgsvectortileutils.h vectortile/qgsvectortilewriter.h ) diff --git a/src/core/qgstiles.h b/src/core/qgstiles.h index b82987feffc..fa091076561 100644 --- a/src/core/qgstiles.h +++ b/src/core/qgstiles.h @@ -107,7 +107,11 @@ class CORE_EXPORT QgsTileMatrix //! Returns a tile matrix for the usual web mercator static QgsTileMatrix fromWebMercator( int zoomLevel ); - //! Returns a tile matrix for a specific CRS, top left point, zoom level 0 dimension in CRS units + /** + * Returns a tile matrix for a specific CRS, top left point, zoom level 0 dimension in CRS units. + * + * The \a z0Dimension argument must specify the dimension (width or height, in map units) of the root tiles in zoom level 0. + */ static QgsTileMatrix fromCustomDef( int zoomLevel, const QgsCoordinateReferenceSystem &crs, const QgsPointXY &z0TopLeftPoint, double z0Dimension, int z0MatrixWidth = 1, int z0MatrixHeight = 1 ); diff --git a/src/core/vectortile/qgsvectortilelayer.cpp b/src/core/vectortile/qgsvectortilelayer.cpp index 19496f58c0d..212ecfe8ea8 100644 --- a/src/core/vectortile/qgsvectortilelayer.cpp +++ b/src/core/vectortile/qgsvectortilelayer.cpp @@ -40,6 +40,8 @@ QgsVectorTileLayer::QgsVectorTileLayer( const QString &uri, const QString &baseN : QgsMapLayer( QgsMapLayerType::VectorTileLayer, baseName ) , mTransformContext( options.transformContext ) { + mTileStructure = QgsVectorTileStructure::fromWebMercator(); + mDataSource = uri; setValid( loadDataSource() ); @@ -80,13 +82,13 @@ bool QgsVectorTileLayer::loadDataSource() } // online tiles - mSourceMinZoom = 0; - mSourceMaxZoom = 14; + mTileStructure.setMinimumZoom( 0 ); + mTileStructure.setMaximumZoom( 14 ); if ( dsUri.hasParam( QStringLiteral( "zmin" ) ) ) - mSourceMinZoom = dsUri.param( QStringLiteral( "zmin" ) ).toInt(); + mTileStructure.setMinimumZoom( dsUri.param( QStringLiteral( "zmin" ) ).toInt() ); if ( dsUri.hasParam( QStringLiteral( "zmax" ) ) ) - mSourceMaxZoom = dsUri.param( QStringLiteral( "zmax" ) ).toInt(); + mTileStructure.setMaximumZoom( dsUri.param( QStringLiteral( "zmax" ) ).toInt() ); setExtent( QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 ) ); } @@ -111,10 +113,10 @@ bool QgsVectorTileLayer::loadDataSource() const int minZoom = reader.metadataValue( QStringLiteral( "minzoom" ) ).toInt( &minZoomOk ); const int maxZoom = reader.metadataValue( QStringLiteral( "maxzoom" ) ).toInt( &maxZoomOk ); if ( minZoomOk ) - mSourceMinZoom = minZoom; + mTileStructure.setMinimumZoom( minZoom ); if ( maxZoomOk ) - mSourceMaxZoom = maxZoom; - QgsDebugMsgLevel( QStringLiteral( "zoom range: %1 - %2" ).arg( mSourceMinZoom ).arg( mSourceMaxZoom ), 2 ); + mTileStructure.setMaximumZoom( maxZoom ); + QgsDebugMsgLevel( QStringLiteral( "zoom range: %1 - %2" ).arg( mTileStructure.minimumZoom() ).arg( mTileStructure.maximumZoom() ), 2 ); QgsRectangle r = reader.extent(); QgsCoordinateTransform ct( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), @@ -190,14 +192,14 @@ bool QgsVectorTileLayer::setupArcgisVectorTileServiceConnection( const QString & // if hardcoded zoom limits aren't specified, take them from the server if ( !dataSourceUri.hasParam( QStringLiteral( "zmin" ) ) ) - mSourceMinZoom = 0; + mTileStructure.setMinimumZoom( 0 ); else - mSourceMinZoom = dataSourceUri.param( QStringLiteral( "zmin" ) ).toInt(); + mTileStructure.setMinimumZoom( dataSourceUri.param( QStringLiteral( "zmin" ) ).toInt() ); if ( !dataSourceUri.hasParam( QStringLiteral( "zmax" ) ) ) - mSourceMaxZoom = mArcgisLayerConfiguration.value( QStringLiteral( "maxzoom" ) ).toInt(); + mTileStructure.setMaximumZoom( mArcgisLayerConfiguration.value( QStringLiteral( "maxzoom" ) ).toInt() ); else - mSourceMaxZoom = dataSourceUri.param( QStringLiteral( "zmax" ) ).toInt(); + mTileStructure.setMaximumZoom( dataSourceUri.param( QStringLiteral( "zmax" ) ).toInt() ); setExtent( QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 ) ); @@ -234,6 +236,26 @@ bool QgsVectorTileLayer::readXml( const QDomNode &layerNode, QgsReadWriteContext { setValid( loadDataSource() ); + const QDomElement structureElement = layerNode.firstChildElement( QStringLiteral( "tileStructure" ) ); + if ( !structureElement.isNull() ) + { + QgsVectorTileStructure structure; + structure.setZ0xMin( structureElement.attribute( QStringLiteral( "z0xMin" ) ).toDouble() ); + structure.setZ0xMax( structureElement.attribute( QStringLiteral( "z0xMax" ) ).toDouble() ); + structure.setZ0yMin( structureElement.attribute( QStringLiteral( "z0yMin" ) ).toDouble() ); + structure.setZ0yMax( structureElement.attribute( QStringLiteral( "z0yMax" ) ).toDouble() ); + structure.setZ0Dimension( structureElement.attribute( QStringLiteral( "z0Dimension" ) ).toDouble() ); + structure.setZ0Scale( structureElement.attribute( QStringLiteral( "z0Scale" ) ).toDouble() ); + structure.setMinimumZoom( structureElement.attribute( QStringLiteral( "minZoom" ) ).toInt() ); + structure.setMaximumZoom( structureElement.attribute( QStringLiteral( "maxZoom" ) ).toInt() ); + + QgsCoordinateReferenceSystem crs; + crs.readXml( structureElement ); + structure.setCrs( crs ); + + mTileStructure = structure; + } + QString errorMsg; if ( !readSymbology( layerNode, errorMsg, context ) ) return false; @@ -247,6 +269,20 @@ bool QgsVectorTileLayer::writeXml( QDomNode &layerNode, QDomDocument &doc, const QDomElement mapLayerNode = layerNode.toElement(); mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( QgsMapLayerType::VectorTileLayer ) ); + QDomElement structureElement = doc.createElement( QStringLiteral( "tileStructure" ) ); + structureElement.setAttribute( QStringLiteral( "z0xMin" ), qgsDoubleToString( mTileStructure.z0xMin() ) ); + structureElement.setAttribute( QStringLiteral( "z0xMax" ), qgsDoubleToString( mTileStructure.z0xMax() ) ); + structureElement.setAttribute( QStringLiteral( "z0yMin" ), qgsDoubleToString( mTileStructure.z0yMin() ) ); + structureElement.setAttribute( QStringLiteral( "z0yMax" ), qgsDoubleToString( mTileStructure.z0yMax() ) ); + structureElement.setAttribute( QStringLiteral( "z0Dimension" ), qgsDoubleToString( mTileStructure.z0Dimension() ) ); + structureElement.setAttribute( QStringLiteral( "z0Scale" ), qgsDoubleToString( mTileStructure.z0Scale() ) ); + structureElement.setAttribute( QStringLiteral( "minZoom" ), mTileStructure.minimumZoom() ); + structureElement.setAttribute( QStringLiteral( "maxZoom" ), mTileStructure.maximumZoom() ); + mTileStructure.crs().writeXml( structureElement, doc ); + mapLayerNode.appendChild( structureElement ); + + mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( QgsMapLayerType::VectorTileLayer ) ); + // add provider node if ( mDataProvider ) { @@ -702,7 +738,7 @@ QString QgsVectorTileLayer::htmlMetadata() const QByteArray QgsVectorTileLayer::getRawTile( QgsTileXYZ tileID ) { - const QgsTileMatrix tileMatrix = QgsTileMatrix::fromWebMercator( tileID.zoomLevel() ); + const QgsTileMatrix tileMatrix = mTileStructure.tileMatrix( tileID.zoomLevel() ); const QgsTileRange tileRange( tileID.column(), tileID.column(), tileID.row(), tileID.row() ); QgsDataSourceUri dsUri; diff --git a/src/core/vectortile/qgsvectortilelayer.h b/src/core/vectortile/qgsvectortilelayer.h index b6a108cebe5..bf82dc8387a 100644 --- a/src/core/vectortile/qgsvectortilelayer.h +++ b/src/core/vectortile/qgsvectortilelayer.h @@ -20,6 +20,7 @@ #include "qgis_sip.h" #include "qgsmaplayer.h" +#include "qgsvectortilestructure.h" class QgsVectorTileLabeling; class QgsVectorTileRenderer; @@ -155,15 +156,22 @@ class CORE_EXPORT QgsVectorTileLayer : public QgsMapLayer // new methods + /** + * Returns the vector tile structure. + * + * \since QGIS 3.22.6 + */ + QgsVectorTileStructure &tileStructure() { return mTileStructure; } + //! Returns type of the data source QString sourceType() const { return mSourceType; } //! Returns URL/path of the data source (syntax different to each data source type) QString sourcePath() const { return mSourcePath; } //! Returns minimum zoom level at which source has any valid tiles (negative = unconstrained) - int sourceMinZoom() const { return mSourceMinZoom; } + int sourceMinZoom() const { return mTileStructure.minimumZoom(); } //! Returns maximum zoom level at which source has any valid tiles (negative = unconstrained) - int sourceMaxZoom() const { return mSourceMaxZoom; } + int sourceMaxZoom() const { return mTileStructure.maximumZoom(); } /** * Fetches raw tile data for the give tile coordinates. If failed to fetch tile data, @@ -203,10 +211,8 @@ class CORE_EXPORT QgsVectorTileLayer : public QgsMapLayer QString mSourceType; //! URL/Path of the data source QString mSourcePath; - //! Minimum zoom level at which source has any valid tiles (negative = unconstrained) - int mSourceMinZoom = -1; - //! Maximum zoom level at which source has any valid tiles (negative = unconstrained) - int mSourceMaxZoom = -1; + + QgsVectorTileStructure mTileStructure; //! Renderer assigned to the layer to draw map std::unique_ptr mRenderer; diff --git a/src/core/vectortile/qgsvectortilelayerrenderer.cpp b/src/core/vectortile/qgsvectortilelayerrenderer.cpp index 34b6504fd2c..00e6726d15d 100644 --- a/src/core/vectortile/qgsvectortilelayerrenderer.cpp +++ b/src/core/vectortile/qgsvectortilelayerrenderer.cpp @@ -35,12 +35,11 @@ QgsVectorTileLayerRenderer::QgsVectorTileLayerRenderer( QgsVectorTileLayer *laye : QgsMapLayerRenderer( layer->id(), &context ) , mSourceType( layer->sourceType() ) , mSourcePath( layer->sourcePath() ) - , mSourceMinZoom( layer->sourceMinZoom() ) - , mSourceMaxZoom( layer->sourceMaxZoom() ) , mRenderer( layer->renderer()->clone() ) , mDrawTileBoundaries( layer->isTileBorderRenderingEnabled() ) , mFeedback( new QgsFeedback ) , mLayerOpacity( layer->opacity() ) + , mTileStructure( layer->tileStructure() ) { QgsDataSourceUri dsUri; @@ -86,10 +85,10 @@ bool QgsVectorTileLayerRenderer::render() QgsDebugMsgLevel( QStringLiteral( "Vector tiles rendering extent: " ) + ctx.extent().toString( -1 ), 2 ); QgsDebugMsgLevel( QStringLiteral( "Vector tiles map scale 1 : %1" ).arg( ctx.rendererScale() ), 2 ); - mTileZoom = QgsVectorTileUtils::scaleToZoomLevel( ctx.rendererScale(), mSourceMinZoom, mSourceMaxZoom ); + mTileZoom = mTileStructure.scaleToZoomLevel( ctx.rendererScale() ); QgsDebugMsgLevel( QStringLiteral( "Vector tiles zoom level: %1" ).arg( mTileZoom ), 2 ); - mTileMatrix = QgsTileMatrix::fromWebMercator( mTileZoom ); + mTileMatrix = QgsTileMatrix::fromCustomDef( mTileZoom, mTileStructure.crs(), QgsPointXY( mTileStructure.z0xMin(), mTileStructure.z0yMax() ), mTileStructure.z0Dimension() ); mTileRange = mTileMatrix.tileRangeFromExtent( ctx.extent() ); QgsDebugMsgLevel( QStringLiteral( "Vector tiles range X: %1 - %2 Y: %3 - %4" ) @@ -150,7 +149,7 @@ bool QgsVectorTileLayerRenderer::render() // add @zoom_level variable which can be used in styling QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "Tiles" ) ); // will be deleted by popper scope->setVariable( QStringLiteral( "zoom_level" ), mTileZoom, true ); - scope->setVariable( QStringLiteral( "vector_tile_zoom" ), QgsVectorTileUtils::scaleToZoom( ctx.rendererScale() ), true ); + scope->setVariable( QStringLiteral( "vector_tile_zoom" ), mTileStructure.scaleToZoom( ctx.rendererScale() ), true ); const QgsExpressionContextScopePopper popper( ctx.expressionContext(), scope ); mRenderer->startRender( *renderContext(), mTileZoom, mTileRange ); @@ -188,7 +187,7 @@ bool QgsVectorTileLayerRenderer::render() if ( !isAsync ) { - for ( const QgsVectorTileRawData &rawTile : rawTiles ) + for ( const QgsVectorTileRawData &rawTile : std::as_const( rawTiles ) ) { if ( ctx.renderingStopped() ) break; @@ -229,7 +228,7 @@ void QgsVectorTileLayerRenderer::decodeAndDrawTile( const QgsVectorTileRawData & tLoad.start(); // currently only MVT encoding supported - QgsVectorTileMVTDecoder decoder; + QgsVectorTileMVTDecoder decoder( mTileStructure ); if ( !decoder.decode( rawTile.id, rawTile.data ) ) { QgsDebugMsgLevel( QStringLiteral( "Failed to parse raw tile data! " ) + rawTile.id.toString(), 2 ); @@ -262,17 +261,19 @@ void QgsVectorTileLayerRenderer::decodeAndDrawTile( const QgsVectorTileRawData & if ( ctx.renderingStopped() ) return; - // set up clipping so that rendering does not go behind tile's extent - const QgsScopedQPainterState savePainterState( ctx.painter() ); - // we have to intersect with any existing painter clip regions, or we risk overwriting valid clip - // regions setup outside of the vector tile renderer (e.g. layout map clip region) - ctx.painter()->setClipRegion( QRegion( tile.tilePolygon() ), Qt::IntersectClip ); + { + // set up clipping so that rendering does not go behind tile's extent + const QgsScopedQPainterState savePainterState( ctx.painter() ); + // we have to intersect with any existing painter clip regions, or we risk overwriting valid clip + // regions setup outside of the vector tile renderer (e.g. layout map clip region) + ctx.painter()->setClipRegion( QRegion( tile.tilePolygon() ), Qt::IntersectClip ); - QElapsedTimer tDraw; - tDraw.start(); + QElapsedTimer tDraw; + tDraw.start(); - mRenderer->renderTile( tile, ctx ); - mTotalDrawTime += tDraw.elapsed(); + mRenderer->renderTile( tile, ctx ); + mTotalDrawTime += tDraw.elapsed(); + } if ( mLabelProvider ) mLabelProvider->registerTileFeatures( tile, ctx ); @@ -284,7 +285,14 @@ void QgsVectorTileLayerRenderer::decodeAndDrawTile( const QgsVectorTileRawData & QPen pen( Qt::red ); pen.setWidth( 3 ); + QBrush brush( QColor( 255, 0, 0, 40 ), Qt::BrushStyle::Dense3Pattern ); + ctx.painter()->setPen( pen ); + ctx.painter()->setBrush( brush ); ctx.painter()->drawPolygon( tile.tilePolygon() ); +#if 0 + ctx.painter()->setBrush( QBrush( QColor( 255, 0, 0 ) ) ); + ctx.painter()->drawText( tile.tilePolygon().boundingRect().center(), tile.id().toString() ); +#endif } } diff --git a/src/core/vectortile/qgsvectortilelayerrenderer.h b/src/core/vectortile/qgsvectortilelayerrenderer.h index 3c9d044837c..46e7804180a 100644 --- a/src/core/vectortile/qgsvectortilelayerrenderer.h +++ b/src/core/vectortile/qgsvectortilelayerrenderer.h @@ -27,6 +27,7 @@ class QgsVectorTileLabelProvider; #include "qgsvectortilerenderer.h" #include "qgsmapclippingregion.h" #include "qgshttpheaders.h" +#include "qgsvectortilestructure.h" /** * \ingroup core @@ -62,10 +63,6 @@ class QgsVectorTileLayerRenderer : public QgsMapLayerRenderer QString mAuthCfg; QgsHttpHeaders mHeaders; - //! Minimum zoom level at which source has any valid tiles (negative = unconstrained) - int mSourceMinZoom = -1; - //! Maximum zoom level at which source has any valid tiles (negative = unconstrained) - int mSourceMaxZoom = -1; //! Tile renderer object to do rendering of individual tiles std::unique_ptr mRenderer; @@ -101,6 +98,9 @@ class QgsVectorTileLayerRenderer : public QgsMapLayerRenderer QList< QgsMapClippingRegion > mClippingRegions; double mLayerOpacity = 1.0; + + QgsVectorTileStructure mTileStructure; + }; diff --git a/src/core/vectortile/qgsvectortilemvtdecoder.cpp b/src/core/vectortile/qgsvectortilemvtdecoder.cpp index c5bda3a0a21..da84c7255c3 100644 --- a/src/core/vectortile/qgsvectortilemvtdecoder.cpp +++ b/src/core/vectortile/qgsvectortilemvtdecoder.cpp @@ -31,7 +31,9 @@ #include -QgsVectorTileMVTDecoder::QgsVectorTileMVTDecoder() = default; +QgsVectorTileMVTDecoder::QgsVectorTileMVTDecoder( const QgsVectorTileStructure &structure ) + : mStructure( structure ) +{} QgsVectorTileMVTDecoder::~QgsVectorTileMVTDecoder() = default; @@ -55,7 +57,9 @@ bool QgsVectorTileMVTDecoder::decode( QgsTileXYZ tileID, const QByteArray &rawTi QStringList QgsVectorTileMVTDecoder::layers() const { QStringList layerNames; - for ( int layerNum = 0; layerNum < tile.layers_size(); layerNum++ ) + const int layerSize = tile.layers_size(); + layerNames.reserve( layerSize ); + for ( int layerNum = 0; layerNum < layerSize; layerNum++ ) { const ::vector_tile::Tile_Layer &layer = tile.layers( layerNum ); const QString layerName = layer.name().c_str(); @@ -71,7 +75,9 @@ QStringList QgsVectorTileMVTDecoder::layerFieldNames( const QString &layerName ) const ::vector_tile::Tile_Layer &layer = tile.layers( mLayerNameToIndex[layerName] ); QStringList fieldNames; - for ( int i = 0; i < layer.keys_size(); ++i ) + const int size = layer.keys_size(); + fieldNames.reserve( size ); + for ( int i = 0; i < size; ++i ) { const QString fieldName = layer.keys( i ).c_str(); fieldNames << fieldName; @@ -84,12 +90,10 @@ QgsVectorTileFeatures QgsVectorTileMVTDecoder::layerFeatures( const QMap( pow( 2, mTileID.zoomLevel() ) ); // assuming we won't ever go over 30 zoom levels - double z0xMin = -20037508.3427892, z0yMin = -20037508.3427892; - double z0xMax = 20037508.3427892, z0yMax = 20037508.3427892; - const double tileDX = ( z0xMax - z0xMin ) / numTiles; - const double tileDY = ( z0yMax - z0yMin ) / numTiles; - const double tileXMin = z0xMin + mTileID.column() * tileDX; - const double tileYMax = z0yMax - mTileID.row() * tileDY; + const double tileDX = ( mStructure.z0xMax() - mStructure.z0xMin() ) / numTiles; + const double tileDY = ( mStructure.z0yMax() - mStructure.z0yMin() ) / numTiles; + const double tileXMin = mStructure.z0xMin() + mTileID.column() * tileDX; + const double tileYMax = mStructure.z0yMax() - mTileID.row() * tileDY; for ( int layerNum = 0; layerNum < tile.layers_size(); layerNum++ ) { diff --git a/src/core/vectortile/qgsvectortilemvtdecoder.h b/src/core/vectortile/qgsvectortilemvtdecoder.h index 59e556eec9a..6c03e84f2c0 100644 --- a/src/core/vectortile/qgsvectortilemvtdecoder.h +++ b/src/core/vectortile/qgsvectortilemvtdecoder.h @@ -26,6 +26,8 @@ class QgsFeature; #include "vector_tile.pb.h" #include "qgsvectortilerenderer.h" +#include "qgsvectortilestructure.h" + /** * \ingroup core @@ -36,7 +38,11 @@ class QgsFeature; class CORE_EXPORT QgsVectorTileMVTDecoder { public: - QgsVectorTileMVTDecoder(); + + /** + * Constructor for QgsVectorTileMVTDecoder, using the specified tile \a structure. + */ + QgsVectorTileMVTDecoder( const QgsVectorTileStructure &structure ); ~QgsVectorTileMVTDecoder(); //! Tries to decode raw tile data, returns true on success @@ -59,6 +65,7 @@ class CORE_EXPORT QgsVectorTileMVTDecoder private: vector_tile::Tile tile; QgsTileXYZ mTileID; + QgsVectorTileStructure mStructure; QMap mLayerNameToIndex; }; diff --git a/src/core/vectortile/qgsvectortilestructure.cpp b/src/core/vectortile/qgsvectortilestructure.cpp new file mode 100644 index 00000000000..5e2f6962760 --- /dev/null +++ b/src/core/vectortile/qgsvectortilestructure.cpp @@ -0,0 +1,46 @@ +/*************************************************************************** + qgsvectortilestructure.cpp + -------------------------------------- + Date : March 2022 + Copyright : (C) 2022 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsvectortilestructure.h" +#include "qgstiles.h" +#include "qgsvectortileutils.h" + +QgsVectorTileStructure QgsVectorTileStructure::fromWebMercator() +{ + QgsVectorTileStructure res; + res.setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ) ); + res.setZ0xMin( -20037508.3427892 ); + res.setZ0xMax( 20037508.3427892 ); + res.setZ0yMin( -20037508.3427892 ); + res.setZ0yMax( 20037508.3427892 ); + res.setZ0Dimension( 2 * 20037508.3427892 ); + res.setZ0Scale( 559082264.0287178 ); + return res; +} + +QgsTileMatrix QgsVectorTileStructure::tileMatrix( int zoom ) const +{ + return QgsTileMatrix::fromCustomDef( zoom, mCrs, QgsPointXY( mZ0xMin, mZ0yMax ), mZ0Dimension ); +} + +double QgsVectorTileStructure::scaleToZoom( double scale ) const +{ + return QgsVectorTileUtils::scaleToZoom( scale, mZ0Scale ); +} + +int QgsVectorTileStructure::scaleToZoomLevel( double scale ) const +{ + return QgsVectorTileUtils::scaleToZoomLevel( scale, mMinZoom, mMaxZoom, mZ0Scale ); +} diff --git a/src/core/vectortile/qgsvectortilestructure.h b/src/core/vectortile/qgsvectortilestructure.h new file mode 100644 index 00000000000..b385285b4a0 --- /dev/null +++ b/src/core/vectortile/qgsvectortilestructure.h @@ -0,0 +1,215 @@ +/*************************************************************************** + qgsvectortilestructure.h + -------------------------------------- + Date : March 2022 + Copyright : (C) 2022 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSVECTORTILESTRUCTURE_H +#define QGSVECTORTILESTRUCTURE_H + +#include "qgis_core.h" +#include "qgis_sip.h" +#include "qgscoordinatereferencesystem.h" + +class QgsTileMatrix; + +/** + * Encapsulates properties of a vector tile structure, including tile origins and scaling information. + * \ingroup core + * \since QGIS 3.22.6 + */ +class CORE_EXPORT QgsVectorTileStructure +{ + public: + + /** + * Returns a vector tile structure corresponding to the standard web mercator/GoogleCRS84Quad setup. + */ + static QgsVectorTileStructure fromWebMercator(); + + /** + * Returns the minimum x coordinate for zoom level 0. + * + * This property is used when scaling raw vector tile coordinates. + * + * \see setZ0xMin() + */ + double z0xMin() const { return mZ0xMin; } + + /** + * Sets the minimum \a x coordinate for zoom level 0. + * + * This property is used when scaling raw vector tile coordinates. + * + * \see z0xMin() + */ + void setZ0xMin( double x ) { mZ0xMin = x; } + + /** + * Returns the maximum x coordinate for zoom level 0. + * + * This property is used when scaling raw vector tile coordinates. + * + * \see setZ0xMax() + */ + double z0xMax() const { return mZ0xMax; } + + /** + * Sets the maximum \a x coordinate for zoom level 0. + * + * This property is used when scaling raw vector tile coordinates. + * + * \see z0xMin() + */ + void setZ0xMax( double x ) { mZ0xMax = x; } + + /** + * Returns the minimum y coordinate for zoom level 0. + * + * This property is used when scaling raw vector tile coordinates. + * + * \see setZ0xMin() + */ + double z0yMin() const { return mZ0yMin; } + + /** + * Sets the minimum \a y coordinate for zoom level 0. + * + * This property is used when scaling raw vector tile coordinates. + * + * \see z0yMin() + */ + void setZ0yMin( double y ) { mZ0yMin = y; } + + /** + * Returns the maximum y coordinate for zoom level 0. + * + * This property is used when scaling raw vector tile coordinates. + * + * \see setZ0yMax() + */ + double z0yMax() const { return mZ0yMax; } + + /** + * Sets the maximum \a y coordinate for zoom level 0. + * + * This property is used when scaling raw vector tile coordinates. + * + * \see z0yMin() + */ + void setZ0yMax( double y ) { mZ0yMax = y; } + + /** + * Returns the dimension (width or height, in map units) of the root tiles in zoom level 0. + * + * \see setZ0Dimension() + */ + double z0Dimension() const { return mZ0Dimension; } + + /** + * Sets the \a dimension (width or height, in map units) of the root tiles in zoom level 0. + * + * \see z0Dimension() + */ + void setZ0Dimension( double dimension ) { mZ0Dimension = dimension;} + + /** + * Returns the scale denominator corresponding to root tiles in zoom level 0. + * + * \see setZ0Dimension() + */ + double z0Scale() const { return mZ0Scale; } + + /** + * Sets the \a scale denominator of the root tiles in zoom level 0. + * + * \see z0Scale() + */ + void setZ0Scale( double scale ) { mZ0Scale = scale;} + + /** + * Returns the coordinate reference system associated with the tiles. + * + * \see setCrs() + */ + QgsCoordinateReferenceSystem crs() const { return mCrs; } + + /** + * Sets the coordinate reference system associated with the tiles. + * + * \see crs() + */ + void setCrs( const QgsCoordinateReferenceSystem &crs ) { mCrs = crs; } + + /** + * Returns the minimum zoom level for tiles (where negative = unconstrained). + * + * \see setMinimumZoom() + */ + int minimumZoom() const { return mMinZoom; } + + /** + * Sets the minimum \a zoom level for tiles (where negative = unconstrained). + * + * \see minimumZoom() + */ + void setMinimumZoom( int zoom ) { mMinZoom = zoom; } + + /** + * Returns the maximum zoom level for tiles (where negative = unconstrained). + * + * \see setMaximumZoom() + */ + int maximumZoom() const { return mMaxZoom; } + + /** + * Sets the maximum \a zoom level for tiles (where negative = unconstrained). + * + * \see maximumZoom() + */ + void setMaximumZoom( int zoom ) { mMaxZoom = zoom; } + + /** + * Returns the tile matrix corresponding to the specified \a zoom. + */ + QgsTileMatrix tileMatrix( int zoom ) const; + + /** + * Finds zoom level given a map \a scale denominator. + */ + double scaleToZoom( double scale ) const; + + /** + * Finds the best fitting (integer) zoom level given a map \a scale denominator. + * + * Values are constrained to the zoom levels between minimumZoom() and maximumZoom(). + */ + int scaleToZoomLevel( double scale ) const; + + private: + + double mZ0xMin = 0; + double mZ0xMax = 0; + double mZ0yMin = 0; + double mZ0yMax = 0; + + double mZ0Dimension = 0; + double mZ0Scale = 0; + + int mMinZoom = -1; + int mMaxZoom = -1; + + QgsCoordinateReferenceSystem mCrs; + +}; + +#endif // QGSVECTORTILESTRUCTURE_H diff --git a/src/core/vectortile/qgsvectortileutils.cpp b/src/core/vectortile/qgsvectortileutils.cpp index cffcb4d4fac..06cc47ccc88 100644 --- a/src/core/vectortile/qgsvectortileutils.cpp +++ b/src/core/vectortile/qgsvectortileutils.cpp @@ -48,7 +48,7 @@ QPolygon QgsVectorTileUtils::tilePolygon( QgsTileXYZ id, const QgsCoordinateTran return path; } -QgsFields QgsVectorTileUtils::makeQgisFields( QSet flds ) +QgsFields QgsVectorTileUtils::makeQgisFields( const QSet &flds ) { QgsFields fields; QStringList fieldsSorted = qgis::setToList( flds ); @@ -60,17 +60,17 @@ QgsFields QgsVectorTileUtils::makeQgisFields( QSet flds ) return fields; } -double QgsVectorTileUtils::scaleToZoom( double mapScale ) +double QgsVectorTileUtils::scaleToZoom( double mapScale, double z0Scale ) { - double s0 = 559082264.0287178; // scale denominator at zoom level 0 of GoogleCRS84Quad + double s0 = z0Scale; double tileZoom2 = log( s0 / mapScale ) / log( 2 ); tileZoom2 -= 1; // TODO: it seems that map scale is double (is that because of high-dpi screen?) return tileZoom2; } -int QgsVectorTileUtils::scaleToZoomLevel( double mapScale, int sourceMinZoom, int sourceMaxZoom ) +int QgsVectorTileUtils::scaleToZoomLevel( double mapScale, int sourceMinZoom, int sourceMaxZoom, double z0Scale ) { - int tileZoom = static_cast( round( scaleToZoom( mapScale ) ) ); + int tileZoom = static_cast( round( scaleToZoom( mapScale, z0Scale ) ) ); if ( tileZoom < sourceMinZoom ) tileZoom = sourceMinZoom; @@ -82,7 +82,7 @@ int QgsVectorTileUtils::scaleToZoomLevel( double mapScale, int sourceMinZoom, in QgsVectorLayer *QgsVectorTileUtils::makeVectorLayerForTile( QgsVectorTileLayer *mvt, QgsTileXYZ tileID, const QString &layerName ) { - QgsVectorTileMVTDecoder decoder; + QgsVectorTileMVTDecoder decoder( mvt->tileStructure() ); decoder.decode( tileID, mvt->getRawTile( tileID ) ); QSet fieldNames = qgis::listToSet( decoder.layerFieldNames( layerName ) ); fieldNames << QStringLiteral( "_geom_type" ); @@ -112,7 +112,7 @@ QgsVectorLayer *QgsVectorTileUtils::makeVectorLayerForTile( QgsVectorTileLayer * vl->dataProvider()->addAttributes( fields.toList() ); vl->updateFields(); bool res = vl->dataProvider()->addFeatures( featuresList ); - Q_UNUSED( res ); + Q_UNUSED( res ) Q_ASSERT( res ); Q_ASSERT( featuresList.count() == vl->featureCount() ); vl->updateExtents(); @@ -149,7 +149,7 @@ bool QgsVectorTileUtils::checkXYZUrlTemplate( const QString &url ) struct LessThanTileRequest { QPointF center; //!< Center in tile matrix (!) coordinates - bool operator()( const QgsTileXYZ &req1, const QgsTileXYZ &req2 ) + bool operator()( QgsTileXYZ req1, QgsTileXYZ req2 ) { QPointF p1( req1.column() + 0.5, req1.row() + 0.5 ); QPointF p2( req2.column() + 0.5, req2.row() + 0.5 ); @@ -160,7 +160,7 @@ struct LessThanTileRequest } }; -QVector QgsVectorTileUtils::tilesInRange( const QgsTileRange &range, int zoomLevel ) +QVector QgsVectorTileUtils::tilesInRange( QgsTileRange range, int zoomLevel ) { QVector tiles; for ( int tileRow = range.startRow(); tileRow <= range.endRow(); ++tileRow ) @@ -173,7 +173,7 @@ QVector QgsVectorTileUtils::tilesInRange( const QgsTileRange &range, return tiles; } -void QgsVectorTileUtils::sortTilesByDistanceFromCenter( QVector &tiles, const QPointF ¢er ) +void QgsVectorTileUtils::sortTilesByDistanceFromCenter( QVector &tiles, QPointF center ) { LessThanTileRequest cmp; cmp.center = center; diff --git a/src/core/vectortile/qgsvectortileutils.h b/src/core/vectortile/qgsvectortileutils.h index 8710abb54dd..74d8c3a1b07 100644 --- a/src/core/vectortile/qgsvectortileutils.h +++ b/src/core/vectortile/qgsvectortileutils.h @@ -47,9 +47,9 @@ class CORE_EXPORT QgsVectorTileUtils public: //! Returns a list of tiles in the given tile range - static QVector tilesInRange( const QgsTileRange &range, int zoomLevel ); + static QVector tilesInRange( QgsTileRange range, int zoomLevel ); //! Orders tile requests according to the distance from view center (given in tile matrix coords) - static void sortTilesByDistanceFromCenter( QVector &tiles, const QPointF ¢er ); + static void sortTilesByDistanceFromCenter( QVector &tiles, QPointF center ); /** * Returns polygon (made by four corners of the tile) in screen coordinates @@ -59,17 +59,25 @@ class CORE_EXPORT QgsVectorTileUtils static QPolygon tilePolygon( QgsTileXYZ id, const QgsCoordinateTransform &ct, const QgsTileMatrix &tm, const QgsMapToPixel &mtp ); //! Returns QgsFields instance based on the set of field names - static QgsFields makeQgisFields( QSet flds ); + static QgsFields makeQgisFields( const QSet &flds ); /** - * Finds zoom level (assuming GoogleCRS84Quad tile matrix set) given map scale denominator. + * Finds zoom level given map scale denominator. + * + * The \a z0Scale argument gives the scale denominator at zoom level 0, where the default + * value corresponds to GoogleCRS84Quad tile matrix set * * \since QGIS 3.16 */ - static double scaleToZoom( double mapScale ); + static double scaleToZoom( double mapScale, double z0Scale = 559082264.0287178 ); - //! Finds best fitting zoom level (assuming GoogleCRS84Quad tile matrix set) given map scale denominator and allowed zoom level range - static int scaleToZoomLevel( double mapScale, int sourceMinZoom, int sourceMaxZoom ); + /** + * Finds the best fitting zoom level given a map scale denominator and allowed zoom level range. + * + * The \a z0Scale argument gives the scale denominator at zoom level 0, where the default + * value corresponds to GoogleCRS84Quad tile matrix set. + */ + static int scaleToZoomLevel( double mapScale, int sourceMinZoom, int sourceMaxZoom, double z0Scale = 559082264.0287178 ); //! Returns a temporary vector layer for given sub-layer of tile in vector tile layer static QgsVectorLayer *makeVectorLayerForTile( QgsVectorTileLayer *mvt, QgsTileXYZ tileID, const QString &layerName ); //! Returns formatted tile URL string replacing {x}, {y}, {z} placeholders (or {-y} instead of {y} for TMS convention) diff --git a/src/gui/qgsmaptoolidentify.cpp b/src/gui/qgsmaptoolidentify.cpp index d48c39f60a2..ec6d3989e1e 100644 --- a/src/gui/qgsmaptoolidentify.cpp +++ b/src/gui/qgsmaptoolidentify.cpp @@ -447,9 +447,9 @@ bool QgsMapToolIdentify::identifyVectorTileLayer( QListscale(), layer->sourceMinZoom(), layer->sourceMaxZoom() ); - const QgsTileMatrix tileMatrix = QgsTileMatrix::fromWebMercator( tileZoom ); - QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( r ); + const int tileZoom = layer->tileStructure().scaleToZoomLevel( mCanvas->scale() ); + const QgsTileMatrix tileMatrix = layer->tileStructure().tileMatrix( tileZoom ); + const QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( r ); for ( int row = tileRange.startRow(); row <= tileRange.endRow(); ++row ) { @@ -460,7 +460,7 @@ bool QgsMapToolIdentify::identifyVectorTileLayer( QListtileStructure() ); if ( !decoder.decode( tileID, data ) ) continue; // failed to decode diff --git a/tests/src/core/testqgsvectortilewriter.cpp b/tests/src/core/testqgsvectortilewriter.cpp index 4bcdb0a25ee..7670c7cc9f3 100644 --- a/tests/src/core/testqgsvectortilewriter.cpp +++ b/tests/src/core/testqgsvectortilewriter.cpp @@ -116,7 +116,7 @@ void TestQgsVectorTileWriter::test_basic() QgsVectorTileLayer *vtLayer = new QgsVectorTileLayer( ds.encodedUri(), "output" ); const QByteArray tile0 = vtLayer->getRawTile( QgsTileXYZ( 0, 0, 0 ) ); - QgsVectorTileMVTDecoder decoder; + QgsVectorTileMVTDecoder decoder( QgsVectorTileStructure::fromWebMercator() ); const bool resDecode0 = decoder.decode( QgsTileXYZ( 0, 0, 0 ), tile0 ); QVERIFY( resDecode0 ); const QStringList layerNames = decoder.layers(); @@ -185,7 +185,7 @@ void TestQgsVectorTileWriter::test_mbtiles() QgsVectorTileLayer *vtLayer = new QgsVectorTileLayer( ds.encodedUri(), "output" ); const QByteArray tile0 = vtLayer->getRawTile( QgsTileXYZ( 0, 0, 0 ) ); - QgsVectorTileMVTDecoder decoder; + QgsVectorTileMVTDecoder decoder( QgsVectorTileStructure::fromWebMercator() ); const bool resDecode0 = decoder.decode( QgsTileXYZ( 0, 0, 0 ), tile0 ); QVERIFY( resDecode0 ); const QStringList layerNames = decoder.layers(); @@ -301,7 +301,7 @@ void TestQgsVectorTileWriter::test_filtering() QgsVectorTileLayer *vtLayer = new QgsVectorTileLayer( ds.encodedUri(), "output" ); const QByteArray tile0 = vtLayer->getRawTile( QgsTileXYZ( 0, 0, 0 ) ); - QgsVectorTileMVTDecoder decoder; + QgsVectorTileMVTDecoder decoder( QgsVectorTileStructure::fromWebMercator() ); const bool resDecode0 = decoder.decode( QgsTileXYZ( 0, 0, 0 ), tile0 ); QVERIFY( resDecode0 ); const QStringList layerNames = decoder.layers(); @@ -364,7 +364,7 @@ void TestQgsVectorTileWriter::test_z0TileMatrix3857() QgsVectorTileLayer *vtLayer = new QgsVectorTileLayer( ds.encodedUri(), "output" ); const QByteArray tile0 = vtLayer->getRawTile( QgsTileXYZ( 0, 0, 0 ) ); - QgsVectorTileMVTDecoder decoder; + QgsVectorTileMVTDecoder decoder( QgsVectorTileStructure::fromWebMercator() ); const bool resDecode0 = decoder.decode( QgsTileXYZ( 0, 0, 0 ), tile0 ); QVERIFY( resDecode0 ); const QStringList layerNames = decoder.layers(); @@ -450,7 +450,7 @@ void TestQgsVectorTileWriter::test_z0TileMatrix2154() QgsVectorTileLayer *vtLayer = new QgsVectorTileLayer( ds.encodedUri(), "output" ); const QByteArray tile0 = vtLayer->getRawTile( QgsTileXYZ( 0, 0, 0 ) ); - QgsVectorTileMVTDecoder decoder; + QgsVectorTileMVTDecoder decoder( QgsVectorTileStructure::fromWebMercator() ); const bool resDecode0 = decoder.decode( QgsTileXYZ( 0, 0, 0 ), tile0 ); QVERIFY( resDecode0 ); const QStringList layerNames = decoder.layers();