From f1c0fe259f4934b8b1aa5500afc556cbc6b7e249 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 14 Sep 2020 17:15:49 +1000 Subject: [PATCH] Fix calculation of zoom level for vector tiles Previously the code was calculating the exact zoom level (a double value) and then rounding this to an integer in order to determine which rules should be applied. This appears to violate the vector tile styling specifications, which are designed to "round down" the zoom level, so that styling rules like: - layer zoom range: 12-22 - interpolated expression: case when @zoom_level > 12 and @zoom_level <= 14 then 1 when @zoom_level > 14 and @zoom_level < 18 then 2 when @zoom_level >=18 then 4 end work correctly when the exact zoom level is just less than 12, e.g. 11.8 So now we use floor when converting a zoom level to int so that the styling rules work correctly. Additionally, this adds a new @vector_tile_zoom expression variable which contains the original double value of the calculated tile zoom (not the integer one used for layer visibility). Many mapbox GL styling rules rely on non-integer zoom levels for interpolation, e.g. Case when @vector_tile_zoom >= 11.2 then 4 ... This change allows for smooth interpolation between zoom levels which matches the web map appearance, instead of "jumpy" fixed level interpolation we previously had. --- src/core/expression/qgsexpression.cpp | 1 + .../vectortile/qgsmapboxglstyleconverter.cpp | 40 +++++++++---------- .../vectortile/qgsvectortilelayerrenderer.cpp | 1 + src/core/vectortile/qgsvectortileutils.cpp | 10 +++-- src/core/vectortile/qgsvectortileutils.h | 8 ++++ src/gui/symbology/qgssymbollayerwidget.cpp | 4 ++ .../qgsvectortilebasiclabelingwidget.cpp | 1 + .../qgsvectortilebasicrendererwidget.cpp | 1 + tests/src/python/test_qgsmapboxglconverter.py | 40 +++++++++---------- 9 files changed, 63 insertions(+), 43 deletions(-) diff --git a/src/core/expression/qgsexpression.cpp b/src/core/expression/qgsexpression.cpp index d5996dcc482..85e86f32066 100644 --- a/src/core/expression/qgsexpression.cpp +++ b/src/core/expression/qgsexpression.cpp @@ -790,6 +790,7 @@ void QgsExpression::initVariableHelp() // vector tile layer variables sVariableHelpTexts()->insert( QStringLiteral( "zoom_level" ), QCoreApplication::translate( "variable_help", "Zoom level of the tile that is being rendered (derived from the current map scale). Normally in interval [0, 20]." ) ); + sVariableHelpTexts()->insert( QStringLiteral( "vector_tile_zoom" ), QCoreApplication::translate( "variable_help", "Exact zoom level of the tile that is being rendered (derived from the current map scale). Normally in interval [0, 20]. Unlike @zoom_level, this variable is a floating point value which can be used to interpolated values between two integer zoom levels." ) ); sVariableHelpTexts()->insert( QStringLiteral( "row_number" ), QCoreApplication::translate( "variable_help", "Stores the number of the current row." ) ); sVariableHelpTexts()->insert( QStringLiteral( "grid_number" ), QCoreApplication::translate( "variable_help", "Current grid annotation value." ) ); diff --git a/src/core/vectortile/qgsmapboxglstyleconverter.cpp b/src/core/vectortile/qgsmapboxglstyleconverter.cpp index d9ed74c14a0..39216b0514d 100644 --- a/src/core/vectortile/qgsmapboxglstyleconverter.cpp +++ b/src/core/vectortile/qgsmapboxglstyleconverter.cpp @@ -820,11 +820,11 @@ void QgsMapBoxGlStyleConverter::parseSymbolLayer( const QVariantMap &jsonLayer, if ( splitFontFamily( bv, fontFamily, fontStyle ) ) { - familyCaseString += QStringLiteral( "WHEN @zoom_level > %1 AND @zoom_level <= %2 " + familyCaseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 " "THEN %3 " ).arg( bz.toString(), tz.toString(), QgsExpression::quotedValue( fontFamily ) ); - styleCaseString += QStringLiteral( "WHEN @zoom_level > %1 AND @zoom_level <= %2 " + styleCaseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 " "THEN %3 " ).arg( bz.toString(), tz.toString(), QgsExpression::quotedValue( fontStyle ) ); @@ -1763,7 +1763,7 @@ QgsProperty QgsMapBoxGlStyleConverter::parseInterpolateColorByZoom( const QVaria int tcAlpha; colorAsHslaComponents( topColor, tcHue, tcSat, tcLight, tcAlpha ); - caseString += QStringLiteral( "WHEN @zoom_level >= %1 AND @zoom_level < %2 THEN color_hsla(" + caseString += QStringLiteral( "WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla(" "%3, %4, %5, %6) " ).arg( bz, tz, interpolateExpression( bz.toDouble(), tz.toDouble(), bcHue, tcHue, base ), interpolateExpression( bz.toDouble(), tz.toDouble(), bcSat, tcSat, base ), @@ -1780,7 +1780,7 @@ QgsProperty QgsMapBoxGlStyleConverter::parseInterpolateColorByZoom( const QVaria int tcAlpha; colorAsHslaComponents( topColor, tcHue, tcSat, tcLight, tcAlpha ); - caseString += QStringLiteral( "WHEN @zoom_level >= %1 THEN color_hsla(%2, %3, %4, %5) " + caseString += QStringLiteral( "WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) " "ELSE color_hsla(%2, %3, %4, %5) END" ).arg( tz ) .arg( tcHue ).arg( tcSat ).arg( tcLight ).arg( tcAlpha ); @@ -1842,13 +1842,13 @@ QgsProperty QgsMapBoxGlStyleConverter::parseInterpolateOpacityByZoom( const QVar QString QgsMapBoxGlStyleConverter::parseOpacityStops( double base, const QVariantList &stops, int maxOpacity ) { - QString caseString = QStringLiteral( "CASE WHEN @zoom_level < %1 THEN set_color_part(@symbol_color, 'alpha', %2)" ) + QString caseString = QStringLiteral( "CASE WHEN @vector_tile_zoom < %1 THEN set_color_part(@symbol_color, 'alpha', %2)" ) .arg( stops.value( 0 ).toList().value( 0 ).toString() ) .arg( stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity ); for ( int i = 0; i < stops.size() - 1; ++i ) { - caseString += QStringLiteral( " WHEN @zoom_level >= %1 AND @zoom_level < %2 " + caseString += QStringLiteral( " WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 " "THEN set_color_part(@symbol_color, 'alpha', %3)" ) .arg( stops.value( i ).toList().value( 0 ).toString(), stops.value( i + 1 ).toList().value( 0 ).toString(), @@ -1858,7 +1858,7 @@ QString QgsMapBoxGlStyleConverter::parseOpacityStops( double base, const QVarian stops.value( i + 1 ).toList().value( 1 ).toDouble() * maxOpacity, base ) ); } - caseString += QStringLiteral( " WHEN @zoom_level >= %1 " + caseString += QStringLiteral( " WHEN @vector_tile_zoom >= %1 " "THEN set_color_part(@symbol_color, 'alpha', %2) END" ) .arg( stops.last().toList().value( 0 ).toString() ) .arg( stops.last().toList().value( 1 ).toDouble() * maxOpacity ); @@ -1933,8 +1933,8 @@ QString QgsMapBoxGlStyleConverter::parsePointStops( double base, const QVariantL return QString(); } - caseString += QStringLiteral( "WHEN @zoom_level > %1 AND @zoom_level <= %2 " - "THEN array(%3,%4) " ).arg( bz.toString(), + caseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 " + "THEN array(%3,%4)" ).arg( bz.toString(), tz.toString(), interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 0 ).toDouble(), tv.toList().value( 0 ).toDouble(), base, multiplier ), interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 1 ).toDouble(), tv.toList().value( 1 ).toDouble(), base, multiplier ) ); @@ -1967,7 +1967,7 @@ QString QgsMapBoxGlStyleConverter::parseStops( double base, const QVariantList & return QString(); } - caseString += QStringLiteral( "WHEN @zoom_level > %1 AND @zoom_level <= %2 " + caseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 " "THEN %3 " ).arg( bz.toString(), tz.toString(), interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toDouble(), tv.toDouble(), base, multiplier ) ); @@ -1975,7 +1975,7 @@ QString QgsMapBoxGlStyleConverter::parseStops( double base, const QVariantList & const QVariant z = stops.last().toList().value( 0 ); const QVariant v = stops.last().toList().value( 1 ); - caseString += QStringLiteral( "WHEN @zoom_level > %1 " + caseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 " "THEN %2 END" ).arg( z.toString() ).arg( v.toDouble() * multiplier ); return caseString; } @@ -2003,7 +2003,7 @@ QString QgsMapBoxGlStyleConverter::parseStringStops( const QVariantList &stops, return QString(); } - caseString += QStringLiteral( "WHEN @zoom_level > %1 AND @zoom_level <= %2 " + caseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 " "THEN %3 " ).arg( bz.toString(), tz.toString(), QgsExpression::quotedValue( conversionMap.value( bv, bv ) ) ); @@ -2224,14 +2224,14 @@ QString QgsMapBoxGlStyleConverter::interpolateExpression( double zoomMin, double QString expression; if ( base == 1 ) { - expression = QStringLiteral( "scale_linear(@zoom_level,%1,%2,%3,%4)" ).arg( zoomMin ) + expression = QStringLiteral( "scale_linear(@vector_tile_zoom,%1,%2,%3,%4)" ).arg( zoomMin ) .arg( zoomMax ) .arg( valueMin ) .arg( valueMax ); } else { - expression = QStringLiteral( "scale_exp(@zoom_level,%1,%2,%3,%4,%5)" ).arg( zoomMin ) + expression = QStringLiteral( "scale_exp(@vector_tile_zoom,%1,%2,%3,%4,%5)" ).arg( zoomMin ) .arg( zoomMax ) .arg( valueMin ) .arg( valueMax ) @@ -2539,10 +2539,10 @@ QString QgsMapBoxGlStyleConverter::retrieveSpriteAsBase64( const QVariant &value sprite = retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, spriteSize ); spritePath = prepareBase64( sprite ); - spriteProperty = QStringLiteral( "CASE WHEN @zoom_level < %1 THEN '%2'" ) + spriteProperty = QStringLiteral( "CASE WHEN @vector_tile_zoom < %1 THEN '%2'" ) .arg( stops.value( 0 ).toList().value( 0 ).toString() ) .arg( spritePath ); - spriteSizeProperty = QStringLiteral( "CASE WHEN @zoom_level < %1 THEN %2" ) + spriteSizeProperty = QStringLiteral( "CASE WHEN @vector_tile_zoom < %1 THEN %2" ) .arg( stops.value( 0 ).toList().value( 0 ).toString() ) .arg( spriteSize.width() ); @@ -2552,12 +2552,12 @@ QString QgsMapBoxGlStyleConverter::retrieveSpriteAsBase64( const QVariant &value sprite = retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, size ); path = prepareBase64( sprite ); - spriteProperty += QStringLiteral( " WHEN @zoom_level >= %1 AND @zoom_level < %2 " + spriteProperty += QStringLiteral( " WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 " "THEN '%3'" ) .arg( stops.value( i ).toList().value( 0 ).toString(), stops.value( i + 1 ).toList().value( 0 ).toString(), path ); - spriteSizeProperty += QStringLiteral( " WHEN @zoom_level >= %1 AND @zoom_level < %2 " + spriteSizeProperty += QStringLiteral( " WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 " "THEN %3" ) .arg( stops.value( i ).toList().value( 0 ).toString(), stops.value( i + 1 ).toList().value( 0 ).toString() ) @@ -2566,11 +2566,11 @@ QString QgsMapBoxGlStyleConverter::retrieveSpriteAsBase64( const QVariant &value sprite = retrieveSprite( stops.last().toList().value( 1 ).toString(), context, size ); path = prepareBase64( sprite ); - spriteProperty += QStringLiteral( " WHEN @zoom_level >= %1 " + spriteProperty += QStringLiteral( " WHEN @vector_tile_zoom >= %1 " "THEN '%2' END" ) .arg( stops.last().toList().value( 0 ).toString() ) .arg( path ); - spriteSizeProperty += QStringLiteral( " WHEN @zoom_level >= %1 " + spriteSizeProperty += QStringLiteral( " WHEN @vector_tile_zoom >= %1 " "THEN %2 END" ) .arg( stops.last().toList().value( 0 ).toString() ) .arg( size.width() ); diff --git a/src/core/vectortile/qgsvectortilelayerrenderer.cpp b/src/core/vectortile/qgsvectortilelayerrenderer.cpp index 45a04d0b26e..98e94c574c9 100644 --- a/src/core/vectortile/qgsvectortilelayerrenderer.cpp +++ b/src/core/vectortile/qgsvectortilelayerrenderer.cpp @@ -132,6 +132,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( "zoom_level", mTileZoom, true ); + scope->setVariable( "vector_tile_zoom", QgsVectorTileUtils::scaleToZoom( ctx.rendererScale() ), true ); QgsExpressionContextScopePopper popper( ctx.expressionContext(), scope ); mRenderer->startRender( *renderContext(), mTileZoom, mTileRange ); diff --git a/src/core/vectortile/qgsvectortileutils.cpp b/src/core/vectortile/qgsvectortileutils.cpp index b855bb1b3d9..d16fc79b487 100644 --- a/src/core/vectortile/qgsvectortileutils.cpp +++ b/src/core/vectortile/qgsvectortileutils.cpp @@ -60,13 +60,17 @@ QgsFields QgsVectorTileUtils::makeQgisFields( QSet flds ) return fields; } - -int QgsVectorTileUtils::scaleToZoomLevel( double mapScale, int sourceMinZoom, int sourceMaxZoom ) +double QgsVectorTileUtils::scaleToZoom( double mapScale ) { double s0 = 559082264.0287178; // scale denominator at zoom level 0 of GoogleCRS84Quad double tileZoom2 = log( s0 / mapScale ) / log( 2 ); tileZoom2 -= 1; // TODO: it seems that map scale is double (is that because of high-dpi screen?) - int tileZoom = static_cast( round( tileZoom2 ) ); + return tileZoom2; +} + +int QgsVectorTileUtils::scaleToZoomLevel( double mapScale, int sourceMinZoom, int sourceMaxZoom ) +{ + int tileZoom = static_cast( floor( scaleToZoom( mapScale ) ) ); if ( tileZoom < sourceMinZoom ) tileZoom = sourceMinZoom; diff --git a/src/core/vectortile/qgsvectortileutils.h b/src/core/vectortile/qgsvectortileutils.h index 263c7cc7b72..ef3227eadad 100644 --- a/src/core/vectortile/qgsvectortileutils.h +++ b/src/core/vectortile/qgsvectortileutils.h @@ -55,6 +55,14 @@ 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 ); + + /** + * Finds zoom level (assuming GoogleCRS84Quad tile matrix set) given map scale denominator. + * + * \since QGIS 3.16 + */ + static double scaleToZoom( double mapScale ); + //! 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 ); //! Returns a temporary vector layer for given sub-layer of tile in vector tile layer diff --git a/src/gui/symbology/qgssymbollayerwidget.cpp b/src/gui/symbology/qgssymbollayerwidget.cpp index cb10cb7a8d8..ff0eb016ba7 100644 --- a/src/gui/symbology/qgssymbollayerwidget.cpp +++ b/src/gui/symbology/qgssymbollayerwidget.cpp @@ -105,6 +105,10 @@ QgsExpressionContext QgsSymbolLayerWidget::createExpressionContext() const { highlights << QStringLiteral( "zoom_level" ); } + if ( expContext.hasVariable( QStringLiteral( "vector_tile_zoom" ) ) ) + { + highlights << QStringLiteral( "vector_tile_zoom" ); + } expContext.setHighlightedVariables( highlights ); diff --git a/src/gui/vectortile/qgsvectortilebasiclabelingwidget.cpp b/src/gui/vectortile/qgsvectortilebasiclabelingwidget.cpp index bd891c339c9..6bb26644d3a 100644 --- a/src/gui/vectortile/qgsvectortilebasiclabelingwidget.cpp +++ b/src/gui/vectortile/qgsvectortilebasiclabelingwidget.cpp @@ -413,6 +413,7 @@ void QgsVectorTileBasicLabelingWidget::editStyleAtIndex( const QModelIndex &prox QList scopes = context.additionalExpressionContextScopes(); QgsExpressionContextScope tileScope; tileScope.setVariable( "zoom_level", zoom, true ); + tileScope.setVariable( "vector_tile_zoom", QgsVectorTileUtils::scaleToZoom( mMapCanvas->scale() ), true ); scopes << tileScope; context.setAdditionalExpressionContextScopes( scopes ); } diff --git a/src/gui/vectortile/qgsvectortilebasicrendererwidget.cpp b/src/gui/vectortile/qgsvectortilebasicrendererwidget.cpp index d62b6dcd3c8..59c34c33c71 100644 --- a/src/gui/vectortile/qgsvectortilebasicrendererwidget.cpp +++ b/src/gui/vectortile/qgsvectortilebasicrendererwidget.cpp @@ -416,6 +416,7 @@ void QgsVectorTileBasicRendererWidget::editStyleAtIndex( const QModelIndex &prox QList scopes = context.additionalExpressionContextScopes(); QgsExpressionContextScope tileScope; tileScope.setVariable( "zoom_level", zoom, true ); + tileScope.setVariable( "vector_tile_zoom", QgsVectorTileUtils::scaleToZoom( mMapCanvas->scale() ), true ); scopes << tileScope; context.setAdditionalExpressionContextScopes( scopes ); } diff --git a/tests/src/python/test_qgsmapboxglconverter.py b/tests/src/python/test_qgsmapboxglconverter.py index 6127dbb9213..2ee20ab2fe9 100644 --- a/tests/src/python/test_qgsmapboxglconverter.py +++ b/tests/src/python/test_qgsmapboxglconverter.py @@ -37,11 +37,11 @@ class TestQgsMapBoxGlStyleConverter(unittest.TestCase): def testInterpolateExpression(self): self.assertEqual(QgsMapBoxGlStyleConverter.interpolateExpression(5, 13, 27, 29, 1), - 'scale_linear(@zoom_level,5,13,27,29)') + 'scale_linear(@vector_tile_zoom,5,13,27,29)') self.assertEqual(QgsMapBoxGlStyleConverter.interpolateExpression(5, 13, 27, 29, 1.5), - 'scale_exp(@zoom_level,5,13,27,29,1.5)') + 'scale_exp(@vector_tile_zoom,5,13,27,29,1.5)') self.assertEqual(QgsMapBoxGlStyleConverter.interpolateExpression(5, 13, 27, 29, 1.5), - 'scale_exp(@zoom_level,5,13,27,29,1.5)') + 'scale_exp(@vector_tile_zoom,5,13,27,29,1.5)') # same values, return nice and simple expression! self.assertEqual(QgsMapBoxGlStyleConverter.interpolateExpression(5, 13, 27, 27, 1.5), @@ -64,7 +64,7 @@ class TestQgsMapBoxGlStyleConverter(unittest.TestCase): }, conversion_context) self.assertEqual(props.expressionString(), - 'CASE WHEN @zoom_level >= 0 AND @zoom_level < 150 THEN color_hsla(scale_linear(@zoom_level,0,150,59,352), scale_linear(@zoom_level,0,150,81,59), scale_linear(@zoom_level,0,150,70,44), 255) WHEN @zoom_level >= 150 AND @zoom_level < 250 THEN color_hsla(scale_linear(@zoom_level,150,250,352,0), scale_linear(@zoom_level,150,250,59,72), scale_linear(@zoom_level,150,250,44,63), 255) WHEN @zoom_level >= 250 THEN color_hsla(0, 72, 63, 255) ELSE color_hsla(0, 72, 63, 255) END') + 'CASE WHEN @vector_tile_zoom >= 0 AND @vector_tile_zoom < 150 THEN color_hsla(scale_linear(@vector_tile_zoom,0,150,59,352), scale_linear(@vector_tile_zoom,0,150,81,59), scale_linear(@vector_tile_zoom,0,150,70,44), 255) WHEN @vector_tile_zoom >= 150 AND @vector_tile_zoom < 250 THEN color_hsla(scale_linear(@vector_tile_zoom,150,250,352,0), scale_linear(@vector_tile_zoom,150,250,59,72), scale_linear(@vector_tile_zoom,150,250,44,63), 255) WHEN @vector_tile_zoom >= 250 THEN color_hsla(0, 72, 63, 255) ELSE color_hsla(0, 72, 63, 255) END') self.assertEqual(default_col.name(), '#f1f075') props, default_col = QgsMapBoxGlStyleConverter.parseInterpolateColorByZoom({'base': 2, 'stops': [[0, '#f1f075'], @@ -73,19 +73,19 @@ class TestQgsMapBoxGlStyleConverter(unittest.TestCase): }, conversion_context) self.assertEqual(props.expressionString(), - 'CASE WHEN @zoom_level >= 0 AND @zoom_level < 150 THEN color_hsla(scale_exp(@zoom_level,0,150,59,352,2), scale_exp(@zoom_level,0,150,81,59,2), scale_exp(@zoom_level,0,150,70,44,2), 255) WHEN @zoom_level >= 150 AND @zoom_level < 250 THEN color_hsla(scale_exp(@zoom_level,150,250,352,0,2), scale_exp(@zoom_level,150,250,59,72,2), scale_exp(@zoom_level,150,250,44,63,2), 255) WHEN @zoom_level >= 250 THEN color_hsla(0, 72, 63, 255) ELSE color_hsla(0, 72, 63, 255) END') + 'CASE WHEN @vector_tile_zoom >= 0 AND @vector_tile_zoom < 150 THEN color_hsla(scale_exp(@vector_tile_zoom,0,150,59,352,2), scale_exp(@vector_tile_zoom,0,150,81,59,2), scale_exp(@vector_tile_zoom,0,150,70,44,2), 255) WHEN @vector_tile_zoom >= 150 AND @vector_tile_zoom < 250 THEN color_hsla(scale_exp(@vector_tile_zoom,150,250,352,0,2), scale_exp(@vector_tile_zoom,150,250,59,72,2), scale_exp(@vector_tile_zoom,150,250,44,63,2), 255) WHEN @vector_tile_zoom >= 250 THEN color_hsla(0, 72, 63, 255) ELSE color_hsla(0, 72, 63, 255) END') self.assertEqual(default_col.name(), '#f1f075') def testParseStops(self): conversion_context = QgsMapBoxGlStyleConversionContext() self.assertEqual(QgsMapBoxGlStyleConverter.parseStops(1, [[1, 10], [2, 20], [5, 100]], 1, conversion_context), - 'CASE WHEN @zoom_level > 1 AND @zoom_level <= 2 THEN scale_linear(@zoom_level,1,2,10,20) WHEN @zoom_level > 2 AND @zoom_level <= 5 THEN scale_linear(@zoom_level,2,5,20,100) WHEN @zoom_level > 5 THEN 100 END') + 'CASE WHEN @vector_tile_zoom > 1 AND @vector_tile_zoom <= 2 THEN scale_linear(@vector_tile_zoom,1,2,10,20) WHEN @vector_tile_zoom > 2 AND @vector_tile_zoom <= 5 THEN scale_linear(@vector_tile_zoom,2,5,20,100) WHEN @vector_tile_zoom > 5 THEN 100 END') self.assertEqual(QgsMapBoxGlStyleConverter.parseStops(1.5, [[1, 10], [2, 20], [5, 100]], 1, conversion_context), - 'CASE WHEN @zoom_level > 1 AND @zoom_level <= 2 THEN scale_exp(@zoom_level,1,2,10,20,1.5) WHEN @zoom_level > 2 AND @zoom_level <= 5 THEN scale_exp(@zoom_level,2,5,20,100,1.5) WHEN @zoom_level > 5 THEN 100 END') + 'CASE WHEN @vector_tile_zoom > 1 AND @vector_tile_zoom <= 2 THEN scale_exp(@vector_tile_zoom,1,2,10,20,1.5) WHEN @vector_tile_zoom > 2 AND @vector_tile_zoom <= 5 THEN scale_exp(@vector_tile_zoom,2,5,20,100,1.5) WHEN @vector_tile_zoom > 5 THEN 100 END') self.assertEqual(QgsMapBoxGlStyleConverter.parseStops(1, [[1, 10], [2, 20], [5, 100]], 8, conversion_context), - 'CASE WHEN @zoom_level > 1 AND @zoom_level <= 2 THEN scale_linear(@zoom_level,1,2,10,20) * 8 WHEN @zoom_level > 2 AND @zoom_level <= 5 THEN scale_linear(@zoom_level,2,5,20,100) * 8 WHEN @zoom_level > 5 THEN 800 END') + 'CASE WHEN @vector_tile_zoom > 1 AND @vector_tile_zoom <= 2 THEN scale_linear(@vector_tile_zoom,1,2,10,20) * 8 WHEN @vector_tile_zoom > 2 AND @vector_tile_zoom <= 5 THEN scale_linear(@vector_tile_zoom,2,5,20,100) * 8 WHEN @vector_tile_zoom > 5 THEN 800 END') self.assertEqual(QgsMapBoxGlStyleConverter.parseStops(1.5, [[1, 10], [2, 20], [5, 100]], 8, conversion_context), - 'CASE WHEN @zoom_level > 1 AND @zoom_level <= 2 THEN scale_exp(@zoom_level,1,2,10,20,1.5) * 8 WHEN @zoom_level > 2 AND @zoom_level <= 5 THEN scale_exp(@zoom_level,2,5,20,100,1.5) * 8 WHEN @zoom_level > 5 THEN 800 END') + 'CASE WHEN @vector_tile_zoom > 1 AND @vector_tile_zoom <= 2 THEN scale_exp(@vector_tile_zoom,1,2,10,20,1.5) * 8 WHEN @vector_tile_zoom > 2 AND @vector_tile_zoom <= 5 THEN scale_exp(@vector_tile_zoom,2,5,20,100,1.5) * 8 WHEN @vector_tile_zoom > 5 THEN 800 END') def testParseMatchList(self): conversion_context = QgsMapBoxGlStyleConversionContext() @@ -154,7 +154,7 @@ class TestQgsMapBoxGlStyleConverter(unittest.TestCase): 0.6 ], QgsMapBoxGlStyleConverter.Numeric, conversion_context, 2.5, 200) self.assertEqual(res.asExpression(), - 'CASE WHEN @zoom_level > 10 AND @zoom_level <= 15 THEN scale_linear(@zoom_level,10,15,0.1,0.3) * 2.5 WHEN @zoom_level > 15 AND @zoom_level <= 18 THEN scale_linear(@zoom_level,15,18,0.3,0.6) * 2.5 WHEN @zoom_level > 18 THEN 1.5 END') + 'CASE WHEN @vector_tile_zoom > 10 AND @vector_tile_zoom <= 15 THEN scale_linear(@vector_tile_zoom,10,15,0.1,0.3) * 2.5 WHEN @vector_tile_zoom > 15 AND @vector_tile_zoom <= 18 THEN scale_linear(@vector_tile_zoom,15,18,0.3,0.6) * 2.5 WHEN @vector_tile_zoom > 18 THEN 1.5 END') self.assertEqual(default_number, 0.25) def testInterpolateByZoom(self): @@ -165,21 +165,21 @@ class TestQgsMapBoxGlStyleConverter(unittest.TestCase): [250, 22]] }, conversion_context) self.assertEqual(prop.expressionString(), - 'CASE WHEN @zoom_level > 0 AND @zoom_level <= 150 THEN scale_linear(@zoom_level,0,150,11,15) WHEN @zoom_level > 150 AND @zoom_level <= 250 THEN scale_linear(@zoom_level,150,250,15,22) WHEN @zoom_level > 250 THEN 22 END') + 'CASE WHEN @vector_tile_zoom > 0 AND @vector_tile_zoom <= 150 THEN scale_linear(@vector_tile_zoom,0,150,11,15) WHEN @vector_tile_zoom > 150 AND @vector_tile_zoom <= 250 THEN scale_linear(@vector_tile_zoom,150,250,15,22) WHEN @vector_tile_zoom > 250 THEN 22 END') self.assertEqual(default_val, 11.0) prop, default_val = QgsMapBoxGlStyleConverter.parseInterpolateByZoom({'base': 1, 'stops': [[0, 11], [150, 15]] }, conversion_context) self.assertEqual(prop.expressionString(), - 'scale_linear(@zoom_level,0,150,11,15)') + 'scale_linear(@vector_tile_zoom,0,150,11,15)') self.assertEqual(default_val, 11.0) prop, default_val = QgsMapBoxGlStyleConverter.parseInterpolateByZoom({'base': 2, 'stops': [[0, 11], [150, 15]] }, conversion_context) self.assertEqual(prop.expressionString(), - 'scale_exp(@zoom_level,0,150,11,15,2)') + 'scale_exp(@vector_tile_zoom,0,150,11,15,2)') self.assertEqual(default_val, 11.0) prop, default_val = QgsMapBoxGlStyleConverter.parseInterpolateByZoom({'base': 2, @@ -187,7 +187,7 @@ class TestQgsMapBoxGlStyleConverter(unittest.TestCase): [150, 15]] }, conversion_context, multiplier=5) self.assertEqual(prop.expressionString(), - 'scale_exp(@zoom_level,0,150,11,15,2) * 5') + 'scale_exp(@vector_tile_zoom,0,150,11,15,2) * 5') self.assertEqual(default_val, 55.0) def testInterpolateOpacityByZoom(self): @@ -196,23 +196,23 @@ class TestQgsMapBoxGlStyleConverter(unittest.TestCase): [150, 0.15], [250, 0.2]] }, 255).expressionString(), - "CASE WHEN @zoom_level < 0 THEN set_color_part(@symbol_color, 'alpha', 25.5) WHEN @zoom_level >= 0 AND @zoom_level < 150 THEN set_color_part(@symbol_color, 'alpha', scale_linear(@zoom_level,0,150,25.5,38.25)) WHEN @zoom_level >= 150 AND @zoom_level < 250 THEN set_color_part(@symbol_color, 'alpha', scale_linear(@zoom_level,150,250,38.25,51)) WHEN @zoom_level >= 250 THEN set_color_part(@symbol_color, 'alpha', 51) END") + "CASE WHEN @vector_tile_zoom < 0 THEN set_color_part(@symbol_color, 'alpha', 25.5) WHEN @vector_tile_zoom >= 0 AND @vector_tile_zoom < 150 THEN set_color_part(@symbol_color, 'alpha', scale_linear(@vector_tile_zoom,0,150,25.5,38.25)) WHEN @vector_tile_zoom >= 150 AND @vector_tile_zoom < 250 THEN set_color_part(@symbol_color, 'alpha', scale_linear(@vector_tile_zoom,150,250,38.25,51)) WHEN @vector_tile_zoom >= 250 THEN set_color_part(@symbol_color, 'alpha', 51) END") self.assertEqual(QgsMapBoxGlStyleConverter.parseInterpolateOpacityByZoom({'base': 1, 'stops': [[0, 0.1], [150, 0.15], [250, 0.2]] }, 100).expressionString(), - "CASE WHEN @zoom_level < 0 THEN set_color_part(@symbol_color, 'alpha', 10) WHEN @zoom_level >= 0 AND @zoom_level < 150 THEN set_color_part(@symbol_color, 'alpha', scale_linear(@zoom_level,0,150,10,15)) WHEN @zoom_level >= 150 AND @zoom_level < 250 THEN set_color_part(@symbol_color, 'alpha', scale_linear(@zoom_level,150,250,15,20)) WHEN @zoom_level >= 250 THEN set_color_part(@symbol_color, 'alpha', 20) END") + "CASE WHEN @vector_tile_zoom < 0 THEN set_color_part(@symbol_color, 'alpha', 10) WHEN @vector_tile_zoom >= 0 AND @vector_tile_zoom < 150 THEN set_color_part(@symbol_color, 'alpha', scale_linear(@vector_tile_zoom,0,150,10,15)) WHEN @vector_tile_zoom >= 150 AND @vector_tile_zoom < 250 THEN set_color_part(@symbol_color, 'alpha', scale_linear(@vector_tile_zoom,150,250,15,20)) WHEN @vector_tile_zoom >= 250 THEN set_color_part(@symbol_color, 'alpha', 20) END") self.assertEqual(QgsMapBoxGlStyleConverter.parseInterpolateOpacityByZoom({'base': 1, 'stops': [[0, 0.1], [150, 0.15]] }, 255).expressionString(), - "set_color_part(@symbol_color, 'alpha', scale_linear(@zoom_level,0,150,25.5,38.25))") + "set_color_part(@symbol_color, 'alpha', scale_linear(@vector_tile_zoom,0,150,25.5,38.25))") self.assertEqual(QgsMapBoxGlStyleConverter.parseInterpolateOpacityByZoom({'base': 2, 'stops': [[0, 0.1], [150, 0.15]] }, 255).expressionString(), - "set_color_part(@symbol_color, 'alpha', scale_exp(@zoom_level,0,150,25.5,38.25,2))") + "set_color_part(@symbol_color, 'alpha', scale_exp(@vector_tile_zoom,0,150,25.5,38.25,2))") self.assertEqual(QgsMapBoxGlStyleConverter.parseInterpolateOpacityByZoom({'base': 2, 'stops': [[0, 0.1], [150, 0.1]] @@ -233,7 +233,7 @@ class TestQgsMapBoxGlStyleConverter(unittest.TestCase): 0.6 ], QgsMapBoxGlStyleConverter.Opacity, conversion_context, 2) self.assertEqual(prop.expressionString(), - "CASE WHEN @zoom_level < 10 THEN set_color_part(@symbol_color, 'alpha', 25.5) WHEN @zoom_level >= 10 AND @zoom_level < 15 THEN set_color_part(@symbol_color, 'alpha', scale_linear(@zoom_level,10,15,25.5,76.5)) WHEN @zoom_level >= 15 AND @zoom_level < 18 THEN set_color_part(@symbol_color, 'alpha', scale_linear(@zoom_level,15,18,76.5,153)) WHEN @zoom_level >= 18 THEN set_color_part(@symbol_color, 'alpha', 153) END") + "CASE WHEN @vector_tile_zoom < 10 THEN set_color_part(@symbol_color, 'alpha', 25.5) WHEN @vector_tile_zoom >= 10 AND @vector_tile_zoom < 15 THEN set_color_part(@symbol_color, 'alpha', scale_linear(@vector_tile_zoom,10,15,25.5,76.5)) WHEN @vector_tile_zoom >= 15 AND @vector_tile_zoom < 18 THEN set_color_part(@symbol_color, 'alpha', scale_linear(@vector_tile_zoom,15,18,76.5,153)) WHEN @vector_tile_zoom >= 18 THEN set_color_part(@symbol_color, 'alpha', 153) END") prop, default_color, default_val = QgsMapBoxGlStyleConverter.parseInterpolateListByZoom([ "interpolate", @@ -247,7 +247,7 @@ class TestQgsMapBoxGlStyleConverter(unittest.TestCase): 0.6 ], QgsMapBoxGlStyleConverter.Numeric, conversion_context, 2) self.assertEqual(prop.expressionString(), - "CASE WHEN @zoom_level > 10 AND @zoom_level <= 15 THEN scale_linear(@zoom_level,10,15,0.1,0.3) * 2 WHEN @zoom_level > 15 AND @zoom_level <= 18 THEN scale_linear(@zoom_level,15,18,0.3,0.6) * 2 WHEN @zoom_level > 18 THEN 1.2 END") + "CASE WHEN @vector_tile_zoom > 10 AND @vector_tile_zoom <= 15 THEN scale_linear(@vector_tile_zoom,10,15,0.1,0.3) * 2 WHEN @vector_tile_zoom > 15 AND @vector_tile_zoom <= 18 THEN scale_linear(@vector_tile_zoom,15,18,0.3,0.6) * 2 WHEN @vector_tile_zoom > 18 THEN 1.2 END") self.assertEqual(default_val, 0.2) def testParseExpression(self):