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.
This commit is contained in:
Nyall Dawson 2020-09-14 17:15:49 +10:00
parent 079f340e99
commit f1c0fe259f
9 changed files with 63 additions and 43 deletions

View File

@ -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." ) );

View File

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

View File

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

View File

@ -60,13 +60,17 @@ QgsFields QgsVectorTileUtils::makeQgisFields( QSet<QString> 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<int>( round( tileZoom2 ) );
return tileZoom2;
}
int QgsVectorTileUtils::scaleToZoomLevel( double mapScale, int sourceMinZoom, int sourceMaxZoom )
{
int tileZoom = static_cast<int>( floor( scaleToZoom( mapScale ) ) );
if ( tileZoom < sourceMinZoom )
tileZoom = sourceMinZoom;

View File

@ -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<QString> 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

View File

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

View File

@ -413,6 +413,7 @@ void QgsVectorTileBasicLabelingWidget::editStyleAtIndex( const QModelIndex &prox
QList<QgsExpressionContextScope> 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 );
}

View File

@ -416,6 +416,7 @@ void QgsVectorTileBasicRendererWidget::editStyleAtIndex( const QModelIndex &prox
QList<QgsExpressionContextScope> 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 );
}

View File

@ -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):