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();