Skip to content

Commit

Permalink
implement step expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
3nids committed Jul 24, 2024
1 parent 1842ea5 commit 8efa115
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,16 @@ Parses and converts a value list (e.g. an interpolate list).
%Docstring
Parses and converts a match function value list.

.. warning::

This is private API only, and may change in future QGIS versions
%End

static QgsProperty parseStepList( const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1,
int maxOpacity = 255, QColor *defaultColor /Out/ = 0, double *defaultNumber /Out/ = 0 );
%Docstring
Parses and converts a match function value list.

.. warning::

This is private API only, and may change in future QGIS versions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,16 @@ Parses and converts a value list (e.g. an interpolate list).
%Docstring
Parses and converts a match function value list.

.. warning::

This is private API only, and may change in future QGIS versions
%End

static QgsProperty parseStepList( const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1,
int maxOpacity = 255, QColor *defaultColor /Out/ = 0, double *defaultNumber /Out/ = 0 );
%Docstring
Parses and converts a match function value list.

.. warning::

This is private API only, and may change in future QGIS versions
Expand Down
96 changes: 85 additions & 11 deletions src/core/vectortile/qgsmapboxglstyleconverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2664,6 +2664,10 @@ QgsProperty QgsMapBoxGlStyleConverter::parseValueList( const QVariantList &json,
{
return parseMatchList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
}
else if ( method == QLatin1String( "step" ) )
{
return parseStepList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
}
else
{
return QgsProperty::fromExpression( parseExpression( json, context ) );
Expand Down Expand Up @@ -2731,8 +2735,14 @@ QgsProperty QgsMapBoxGlStyleConverter::parseMatchList( const QVariantList &json,

}

caseString += QStringLiteral( "WHEN %1 IN (%2) THEN %3 " ).arg( attribute,
matchString.join( ',' ), valueString );
if ( matchString.count() == 1 )
{
caseString += QStringLiteral( "WHEN %1 IS %2 THEN %3 " ).arg( attribute, matchString.at( 0 ), valueString );
}
else
{
caseString += QStringLiteral( "WHEN %1 IN (%2) THEN %3 " ).arg( attribute, matchString.join( ',' ), valueString );
}
}


Expand Down Expand Up @@ -2780,6 +2790,74 @@ QgsProperty QgsMapBoxGlStyleConverter::parseMatchList( const QVariantList &json,
return QgsProperty::fromExpression( caseString );
}

QgsProperty QgsMapBoxGlStyleConverter::parseStepList( const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier, int maxOpacity, QColor *defaultColor, double *defaultNumber )
{
const QString expression = parseExpression( json.value( 1 ).toList(), context );
if ( expression.isEmpty() )
{
context.pushWarning( QObject::tr( "%1: Could not interpret match list" ).arg( context.layerId() ) );
return QgsProperty();
}

QString caseString = QStringLiteral( "CASE " );


for ( int i = json.length() - 2; i > 0; i -= 2 )
{
const QVariant stepValue = json.value( i + 1 );

QString valueString;
if ( stepValue.canConvert<QVariantList>() && ( stepValue.toList().count() != 2 || type != PropertyType::Point ) )
{
valueString = parseValueList( stepValue.toList(), type, context, multiplier, maxOpacity, defaultColor, defaultNumber ).expressionString();
}
else
{
switch ( type )
{
case PropertyType::Color:
{
const QColor color = parseColor( stepValue, context );
valueString = QgsExpression::quotedString( color.name() );
break;
}

case PropertyType::Numeric:
{
const double v = stepValue.toDouble() * multiplier;
valueString = QString::number( v );
break;
}

case PropertyType::Opacity:
{
const double v = stepValue.toDouble() * maxOpacity;
valueString = QString::number( v );
break;
}

case PropertyType::Point:
{
valueString = QStringLiteral( "array(%1,%2)" ).arg( stepValue.toList().value( 0 ).toDouble() * multiplier,
stepValue.toList().value( 0 ).toDouble() * multiplier );
break;
}
}
}

if ( i > 1 )
{
const QString stepKey = QgsExpression::quotedValue( json.value( i ) );
caseString += QStringLiteral( " WHEN %1 >= %2 THEN (%3) " ).arg( expression, stepKey, valueString );
}
else
{
caseString += QStringLiteral( "ELSE (%1) END" ).arg( valueString );
}
}
return QgsProperty::fromExpression( caseString );
}

QgsProperty QgsMapBoxGlStyleConverter::parseInterpolateListByZoom( const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier, int maxOpacity, QColor *defaultColor, double *defaultNumber )
{
if ( json.value( 0 ).toString() != QLatin1String( "interpolate" ) )
Expand Down Expand Up @@ -3351,14 +3429,10 @@ QString QgsMapBoxGlStyleConverter::retrieveSpriteAsBase64WithProperties( const Q
}
else if ( method == QLatin1String( "step" ) )
{
QString attribute = json.value( 1 ).toList().value( 0 ).toString();
if ( attribute == QStringLiteral( "zoom" ) )
{
attribute = QStringLiteral( "@vector_tile_zoom" );
}
else
const QString expression = parseExpression( json.value( 1 ).toList(), context );
if ( expression.isEmpty() )
{
context.pushWarning( QObject::tr( "%1: Could not interpret step list with attribute %2" ).arg( context.layerId(), attribute ) );
context.pushWarning( QObject::tr( "%1: Could not interpret step list" ).arg( context.layerId() ) );
break;
}

Expand All @@ -3372,8 +3446,8 @@ QString QgsMapBoxGlStyleConverter::retrieveSpriteAsBase64WithProperties( const Q
const QImage sprite = retrieveSprite( stepValue, context, spriteSize );
spritePath = prepareBase64( sprite );

spriteProperty += QStringLiteral( " WHEN %1 >= %2 THEN '%3' " ).arg( attribute, stepKey, spritePath );
spriteSizeProperty += QStringLiteral( " WHEN %1 >= %2 THEN %3 " ).arg( attribute ).arg( stepKey ).arg( spriteSize.width() );
spriteProperty += QStringLiteral( " WHEN %1 >= %2 THEN '%3' " ).arg( expression, stepKey, spritePath );
spriteSizeProperty += QStringLiteral( " WHEN %1 >= %2 THEN %3 " ).arg( expression ).arg( stepKey ).arg( spriteSize.width() );
}

const QImage sprite = retrieveSprite( json.at( 2 ).toString(), context, spriteSize );
Expand Down
8 changes: 8 additions & 0 deletions src/core/vectortile/qgsmapboxglstyleconverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,14 @@ class CORE_EXPORT QgsMapBoxGlStyleConverter
static QgsProperty parseMatchList( const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1,
int maxOpacity = 255, QColor *defaultColor SIP_OUT = nullptr, double *defaultNumber SIP_OUT = nullptr );

/**
* Parses and converts a match function value list.
*
* \warning This is private API only, and may change in future QGIS versions
*/
static QgsProperty parseStepList( const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1,
int maxOpacity = 255, QColor *defaultColor SIP_OUT = nullptr, double *defaultNumber SIP_OUT = nullptr );

/**
* Interpolates a list which starts with the interpolate function.
*
Expand Down
16 changes: 16 additions & 0 deletions tests/src/python/test_qgsmapboxglconverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,22 @@ def testParseMatchList(self):
self.assertEqual(res.asExpression(), 'CASE WHEN "luminosity" IN (-15) THEN \'#c8d2d5\' WHEN "luminosity" IN (-14) THEN \'#cbd5d8\' WHEN "luminosity" IN (-13) THEN \'#cfd7da\' WHEN "luminosity" IN (-12) THEN \'#d2dadd\' WHEN "luminosity" IN (-11) THEN \'#d5dde0\' WHEN "luminosity" IN (-10) THEN \'#d9e0e2\' WHEN "luminosity" IN (-9) THEN \'#dce3e5\' WHEN "luminosity" IN (-8) THEN \'#e0e6e7\' WHEN "luminosity" IN (-7) THEN \'#e3e8ea\' WHEN "luminosity" IN (-6) THEN \'#e7ebed\' WHEN "luminosity" IN (-5) THEN \'#eaeeef\' WHEN "luminosity" IN (-4) THEN \'#eef1f2\' WHEN "luminosity" IN (-3) THEN \'#f1f4f5\' WHEN "luminosity" IN (-2) THEN \'#f5f7f7\' WHEN "luminosity" IN (-1) THEN \'#f8f9fa\' ELSE \'#fcfcfc\' END')
self.assertTrue(qgsDoubleNear(default_number, 0.0))

def testParseStepList(self):
conversion_context = QgsMapBoxGlStyleConversionContext()
res, default_color, default_number = QgsMapBoxGlStyleConverter.parseStepList([
"step",
["zoom"],
0,
7, ["match", ["get", "capital"], [2, 4], 1, 0],
8, ["case", [">", 14, ["get", "rank"]], 1, 0],
9, ["case", [">", 15, ["get", "rank"]], 1, 0],
10, ["case", [">", 18, ["get", "rank"]], 1, 0],
11, ["case", [">", 28, ["get", "rank"]], 1, 0],
12, 1,
13, 0
], QgsMapBoxGlStyleConverter.PropertyType.Opacity, conversion_context, 1, 100)
self.assertEqual(res.asExpression(), 'CASE WHEN @vector_tile_zoom >= 13 THEN (0) WHEN @vector_tile_zoom >= 12 THEN (100) WHEN @vector_tile_zoom >= 11 THEN (CASE WHEN ("28" > "rank") THEN 1 ELSE 0 END) WHEN @vector_tile_zoom >= 10 THEN (CASE WHEN ("18" > "rank") THEN 1 ELSE 0 END) WHEN @vector_tile_zoom >= 9 THEN (CASE WHEN ("15" > "rank") THEN 1 ELSE 0 END) WHEN @vector_tile_zoom >= 8 THEN (CASE WHEN ("14" > "rank") THEN 1 ELSE 0 END) WHEN @vector_tile_zoom >= 7 THEN (CASE WHEN "capital" IN (2,4) THEN 100 ELSE 0 END) ELSE (0) END')

def testParseValueList(self):
conversion_context = QgsMapBoxGlStyleConversionContext()
res, default_color, default_number = QgsMapBoxGlStyleConverter.parseValueList([
Expand Down

0 comments on commit 8efa115

Please sign in to comment.