Fix interpolation with expression values, literal filter and geometry-type

This commit is contained in:
Marco Hugentobler 2021-08-26 10:58:07 +02:00
parent 4444402214
commit cf3de158a3
3 changed files with 68 additions and 39 deletions

View File

@ -347,7 +347,7 @@ Parses a numeric value which is interpolated by zoom range.
- defaultNumber: storage for a reasonable "default" number representing the overall property. - defaultNumber: storage for a reasonable "default" number representing the overall property.
%End %End
static QgsProperty parseInterpolateOpacityByZoom( const QVariantMap &json, int maxOpacity ); static QgsProperty parseInterpolateOpacityByZoom( const QVariantMap &json, int maxOpacity, QgsMapBoxGlStyleConversionContext &context );
%Docstring %Docstring
Interpolates opacity with either :py:func:`~QgsMapBoxGlStyleConverter.scale_linear` or :py:func:`~QgsMapBoxGlStyleConverter.scale_exp` (depending on base value). Interpolates opacity with either :py:func:`~QgsMapBoxGlStyleConverter.scale_linear` or :py:func:`~QgsMapBoxGlStyleConverter.scale_exp` (depending on base value).
For ``json`` with intermediate stops it uses :py:func:`~QgsMapBoxGlStyleConverter.parseOpacityStops` function. For ``json`` with intermediate stops it uses :py:func:`~QgsMapBoxGlStyleConverter.parseOpacityStops` function.
@ -358,7 +358,7 @@ It uses QGIS :py:func:`~QgsMapBoxGlStyleConverter.set_color_part` function to se
This is private API only, and may change in future QGIS versions This is private API only, and may change in future QGIS versions
%End %End
static QString parseOpacityStops( double base, const QVariantList &stops, int maxOpacity ); static QString parseOpacityStops( double base, const QVariantList &stops, int maxOpacity, QgsMapBoxGlStyleConversionContext &context );
%Docstring %Docstring
Takes values from stops and uses either :py:func:`~QgsMapBoxGlStyleConverter.scale_linear` or :py:func:`~QgsMapBoxGlStyleConverter.scale_exp` functions Takes values from stops and uses either :py:func:`~QgsMapBoxGlStyleConverter.scale_linear` or :py:func:`~QgsMapBoxGlStyleConverter.scale_exp` functions
to interpolate alpha component of color. to interpolate alpha component of color.
@ -487,7 +487,7 @@ Takes a QColor object and returns HSLA components in required format for QGIS :p
This is private API only, and may change in future QGIS versions This is private API only, and may change in future QGIS versions
%End %End
static QString interpolateExpression( double zoomMin, double zoomMax, double valueMin, double valueMax, double base, double multiplier = 1 ); static QString interpolateExpression( double zoomMin, double zoomMax, QVariant valueMin, QVariant valueMax, double base, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1 );
%Docstring %Docstring
Generates an interpolation for values between ``valueMin`` and ``valueMax``, scaled between the Generates an interpolation for values between ``valueMin`` and ``valueMax``, scaled between the
ranges ``zoomMin`` to ``zoomMax``. ranges ``zoomMin`` to ``zoomMax``.

View File

@ -268,8 +268,8 @@ bool QgsMapBoxGlStyleConverter::parseFillLayer( const QVariantMap &jsonLayer, Qg
} }
else else
{ {
ddProperties.setProperty( QgsSymbolLayer::PropertyFillColor, parseInterpolateOpacityByZoom( jsonFillOpacity.toMap(), fillColor.isValid() ? fillColor.alpha() : 255 ) ); ddProperties.setProperty( QgsSymbolLayer::PropertyFillColor, parseInterpolateOpacityByZoom( jsonFillOpacity.toMap(), fillColor.isValid() ? fillColor.alpha() : 255, context ) );
ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeColor, parseInterpolateOpacityByZoom( jsonFillOpacity.toMap(), fillOutlineColor.isValid() ? fillOutlineColor.alpha() : 255 ) ); ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeColor, parseInterpolateOpacityByZoom( jsonFillOpacity.toMap(), fillOutlineColor.isValid() ? fillOutlineColor.alpha() : 255, context ) );
ddRasterProperties.setProperty( QgsSymbolLayer::PropertyOpacity, parseInterpolateByZoom( jsonFillOpacity.toMap(), context, 100, &rasterOpacity ) ); ddRasterProperties.setProperty( QgsSymbolLayer::PropertyOpacity, parseInterpolateByZoom( jsonFillOpacity.toMap(), context, 100, &rasterOpacity ) );
} }
break; break;
@ -527,7 +527,7 @@ bool QgsMapBoxGlStyleConverter::parseLineLayer( const QVariantMap &jsonLayer, Qg
} }
else else
{ {
ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeColor, parseInterpolateOpacityByZoom( jsonLineOpacity.toMap(), lineColor.isValid() ? lineColor.alpha() : 255 ) ); ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeColor, parseInterpolateOpacityByZoom( jsonLineOpacity.toMap(), lineColor.isValid() ? lineColor.alpha() : 255, context ) );
} }
break; break;
@ -716,7 +716,7 @@ bool QgsMapBoxGlStyleConverter::parseCircleLayer( const QVariantMap &jsonLayer,
break; break;
case QVariant::Map: case QVariant::Map:
ddProperties.setProperty( QgsSymbolLayer::PropertyFillColor, parseInterpolateOpacityByZoom( jsonCircleOpacity.toMap(), circleFillColor.isValid() ? circleFillColor.alpha() : 255 ) ); ddProperties.setProperty( QgsSymbolLayer::PropertyFillColor, parseInterpolateOpacityByZoom( jsonCircleOpacity.toMap(), circleFillColor.isValid() ? circleFillColor.alpha() : 255, context ) );
break; break;
case QVariant::List: case QVariant::List:
@ -800,7 +800,7 @@ bool QgsMapBoxGlStyleConverter::parseCircleLayer( const QVariantMap &jsonLayer,
break; break;
case QVariant::Map: case QVariant::Map:
ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeColor, parseInterpolateOpacityByZoom( jsonCircleStrokeOpacity.toMap(), circleStrokeColor.isValid() ? circleStrokeColor.alpha() : 255 ) ); ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeColor, parseInterpolateOpacityByZoom( jsonCircleStrokeOpacity.toMap(), circleStrokeColor.isValid() ? circleStrokeColor.alpha() : 255, context ) );
break; break;
case QVariant::List: case QVariant::List:
@ -2027,10 +2027,10 @@ QgsProperty QgsMapBoxGlStyleConverter::parseInterpolateColorByZoom( const QVaria
caseString += QStringLiteral( "WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %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, "%3, %4, %5, %6) " ).arg( bz, tz,
interpolateExpression( bz.toDouble(), tz.toDouble(), bcHue, tcHue, base ), interpolateExpression( bz.toDouble(), tz.toDouble(), bcHue, tcHue, base, context ),
interpolateExpression( bz.toDouble(), tz.toDouble(), bcSat, tcSat, base ), interpolateExpression( bz.toDouble(), tz.toDouble(), bcSat, tcSat, base, context ),
interpolateExpression( bz.toDouble(), tz.toDouble(), bcLight, tcLight, base ), interpolateExpression( bz.toDouble(), tz.toDouble(), bcLight, tcLight, base, context ),
interpolateExpression( bz.toDouble(), tz.toDouble(), bcAlpha, tcAlpha, base ) ); interpolateExpression( bz.toDouble(), tz.toDouble(), bcAlpha, tcAlpha, base, context ) );
} }
// top color // top color
@ -2066,7 +2066,7 @@ QgsProperty QgsMapBoxGlStyleConverter::parseInterpolateByZoom( const QVariantMap
scaleExpression = interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(), scaleExpression = interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(),
stops.last().toList().value( 0 ).toDouble(), stops.last().toList().value( 0 ).toDouble(),
stops.value( 0 ).toList().value( 1 ).toDouble(), stops.value( 0 ).toList().value( 1 ).toDouble(),
stops.last().toList().value( 1 ).toDouble(), base, multiplier ); stops.last().toList().value( 1 ).toDouble(), base, context, multiplier );
} }
else else
{ {
@ -2079,7 +2079,7 @@ QgsProperty QgsMapBoxGlStyleConverter::parseInterpolateByZoom( const QVariantMap
return QgsProperty::fromExpression( scaleExpression ); return QgsProperty::fromExpression( scaleExpression );
} }
QgsProperty QgsMapBoxGlStyleConverter::parseInterpolateOpacityByZoom( const QVariantMap &json, int maxOpacity ) QgsProperty QgsMapBoxGlStyleConverter::parseInterpolateOpacityByZoom( const QVariantMap &json, int maxOpacity, QgsMapBoxGlStyleConversionContext &context )
{ {
const double base = json.value( QStringLiteral( "base" ), QStringLiteral( "1" ) ).toDouble(); const double base = json.value( QStringLiteral( "base" ), QStringLiteral( "1" ) ).toDouble();
const QVariantList stops = json.value( QStringLiteral( "stops" ) ).toList(); const QVariantList stops = json.value( QStringLiteral( "stops" ) ).toList();
@ -2093,16 +2093,16 @@ QgsProperty QgsMapBoxGlStyleConverter::parseInterpolateOpacityByZoom( const QVar
.arg( interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(), .arg( interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(),
stops.last().toList().value( 0 ).toDouble(), stops.last().toList().value( 0 ).toDouble(),
stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity, stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity,
stops.last().toList().value( 1 ).toDouble() * maxOpacity, base ) ); stops.last().toList().value( 1 ).toDouble() * maxOpacity, base, context ) );
} }
else else
{ {
scaleExpression = parseOpacityStops( base, stops, maxOpacity ); scaleExpression = parseOpacityStops( base, stops, maxOpacity, context );
} }
return QgsProperty::fromExpression( scaleExpression ); return QgsProperty::fromExpression( scaleExpression );
} }
QString QgsMapBoxGlStyleConverter::parseOpacityStops( double base, const QVariantList &stops, int maxOpacity ) QString QgsMapBoxGlStyleConverter::parseOpacityStops( double base, const QVariantList &stops, int maxOpacity, QgsMapBoxGlStyleConversionContext &context )
{ {
QString caseString = QStringLiteral( "CASE WHEN @vector_tile_zoom < %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( 0 ).toString() )
@ -2117,7 +2117,7 @@ QString QgsMapBoxGlStyleConverter::parseOpacityStops( double base, const QVarian
interpolateExpression( stops.value( i ).toList().value( 0 ).toDouble(), interpolateExpression( stops.value( i ).toList().value( 0 ).toDouble(),
stops.value( i + 1 ).toList().value( 0 ).toDouble(), stops.value( i + 1 ).toList().value( 0 ).toDouble(),
stops.value( i ).toList().value( 1 ).toDouble() * maxOpacity, stops.value( i ).toList().value( 1 ).toDouble() * maxOpacity,
stops.value( i + 1 ).toList().value( 1 ).toDouble() * maxOpacity, base ) ); stops.value( i + 1 ).toList().value( 1 ).toDouble() * maxOpacity, base, context ) );
} }
caseString += QStringLiteral( " WHEN @vector_tile_zoom >= %1 " caseString += QStringLiteral( " WHEN @vector_tile_zoom >= %1 "
@ -2140,11 +2140,11 @@ QgsProperty QgsMapBoxGlStyleConverter::parseInterpolatePointByZoom( const QVaria
scaleExpression = QStringLiteral( "array(%1,%2)" ).arg( interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(), scaleExpression = QStringLiteral( "array(%1,%2)" ).arg( interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(),
stops.last().toList().value( 0 ).toDouble(), stops.last().toList().value( 0 ).toDouble(),
stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble(), stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble(),
stops.last().toList().value( 1 ).toList().value( 0 ).toDouble(), base, multiplier ), stops.last().toList().value( 1 ).toList().value( 0 ).toDouble(), base, context, multiplier ),
interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(), interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(),
stops.last().toList().value( 0 ).toDouble(), stops.last().toList().value( 0 ).toDouble(),
stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble(), stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble(),
stops.last().toList().value( 1 ).toList().value( 1 ).toDouble(), base, multiplier ) stops.last().toList().value( 1 ).toList().value( 1 ).toDouble(), base, context, multiplier )
); );
} }
else else
@ -2198,8 +2198,8 @@ QString QgsMapBoxGlStyleConverter::parsePointStops( double base, const QVariantL
caseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 " caseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
"THEN array(%3,%4)" ).arg( bz.toString(), "THEN array(%3,%4)" ).arg( bz.toString(),
tz.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( 0 ).toDouble(), tv.toList().value( 0 ).toDouble(), base, context, multiplier ),
interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 1 ).toDouble(), tv.toList().value( 1 ).toDouble(), base, multiplier ) ); interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 1 ).toDouble(), tv.toList().value( 1 ).toDouble(), base, context, multiplier ) );
} }
caseString += QLatin1String( "END" ); caseString += QLatin1String( "END" );
return caseString; return caseString;
@ -2232,7 +2232,7 @@ QString QgsMapBoxGlStyleConverter::parseStops( double base, const QVariantList &
caseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 " caseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
"THEN %3 " ).arg( bz.toString(), "THEN %3 " ).arg( bz.toString(),
tz.toString(), tz.toString(),
interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toDouble(), tv.toDouble(), base, multiplier ) ); interpolateExpression( bz.toDouble(), tz.toDouble(), bv, tv, base, context, multiplier ) );
} }
const QVariant z = stops.last().toList().value( 0 ); const QVariant z = stops.last().toList().value( 0 );
@ -2435,7 +2435,7 @@ QgsProperty QgsMapBoxGlStyleConverter::parseInterpolateListByZoom( const QVarian
QVariantList stops; QVariantList stops;
for ( int i = 3; i < json.length(); i += 2 ) for ( int i = 3; i < json.length(); i += 2 )
{ {
stops.push_back( QVariantList() << json.value( i ).toString() << json.value( i + 1 ).toString() ); stops.push_back( QVariantList() << json.value( i ).toString() << json.value( i + 1 ) );
} }
QVariantMap props; QVariantMap props;
@ -2450,7 +2450,7 @@ QgsProperty QgsMapBoxGlStyleConverter::parseInterpolateListByZoom( const QVarian
return parseInterpolateByZoom( props, context, multiplier, defaultNumber ); return parseInterpolateByZoom( props, context, multiplier, defaultNumber );
case PropertyType::Opacity: case PropertyType::Opacity:
return parseInterpolateOpacityByZoom( props, maxOpacity ); return parseInterpolateOpacityByZoom( props, maxOpacity, context );
case PropertyType::Point: case PropertyType::Point:
return parseInterpolatePointByZoom( props, context, multiplier ); return parseInterpolatePointByZoom( props, context, multiplier );
@ -2477,26 +2477,44 @@ void QgsMapBoxGlStyleConverter::colorAsHslaComponents( const QColor &color, int
alpha = color.alpha(); alpha = color.alpha();
} }
QString QgsMapBoxGlStyleConverter::interpolateExpression( double zoomMin, double zoomMax, double valueMin, double valueMax, double base, double multiplier ) QString QgsMapBoxGlStyleConverter::interpolateExpression( double zoomMin, double zoomMax, QVariant valueMin, QVariant valueMax, double base, QgsMapBoxGlStyleConversionContext &context, double multiplier )
{ {
// special case! // special case!
if ( qgsDoubleNear( valueMin, valueMax ) ) if ( ( QMetaType::Type )valueMin.type() == QMetaType::Double && ( QMetaType::Type )valueMax.type() == QMetaType::Double )
return QString::number( valueMin * multiplier ); {
double min = valueMin.toDouble();
double max = valueMax.toDouble();
if ( qgsDoubleNear( min, max ) )
{
return QString::number( min * multiplier );
}
}
QString minValueExpr = valueMin.toString();
QString maxValueExpr = valueMax.toString();
if ( ( QMetaType::Type )valueMin.type() == QMetaType::QVariantList )
{
minValueExpr = parseExpression( valueMin.toList(), context );
}
if ( ( QMetaType::Type )valueMax.type() == QMetaType::QVariantList )
{
maxValueExpr = parseExpression( valueMax.toList(), context );
}
QString expression; QString expression;
if ( base == 1 ) if ( base == 1 )
{ {
expression = QStringLiteral( "scale_linear(@vector_tile_zoom,%1,%2,%3,%4)" ).arg( zoomMin ) expression = QStringLiteral( "scale_linear(@vector_tile_zoom,%1,%2,%3,%4)" ).arg( zoomMin )
.arg( zoomMax ) .arg( zoomMax )
.arg( valueMin ) .arg( minValueExpr )
.arg( valueMax ); .arg( maxValueExpr );
} }
else else
{ {
expression = QStringLiteral( "scale_exp(@vector_tile_zoom,%1,%2,%3,%4,%5)" ).arg( zoomMin ) expression = QStringLiteral( "scale_exp(@vector_tile_zoom,%1,%2,%3,%4,%5)" ).arg( zoomMin )
.arg( zoomMax ) .arg( zoomMax )
.arg( valueMin ) .arg( minValueExpr )
.arg( valueMax ) .arg( maxValueExpr )
.arg( base ); .arg( base );
} }
@ -2529,7 +2547,11 @@ Qt::PenJoinStyle QgsMapBoxGlStyleConverter::parseJoinStyle( const QString &style
QString QgsMapBoxGlStyleConverter::parseExpression( const QVariantList &expression, QgsMapBoxGlStyleConversionContext &context ) QString QgsMapBoxGlStyleConverter::parseExpression( const QVariantList &expression, QgsMapBoxGlStyleConversionContext &context )
{ {
QString op = expression.value( 0 ).toString(); QString op = expression.value( 0 ).toString();
if ( op == QLatin1String( "all" ) if ( op == QLatin1String( "literal" ) )
{
return expression.value( 1 ).toString();
}
else if ( op == QLatin1String( "all" )
|| op == QLatin1String( "any" ) || op == QLatin1String( "any" )
|| op == QLatin1String( "none" ) ) || op == QLatin1String( "none" ) )
{ {
@ -2931,7 +2953,14 @@ QString QgsMapBoxGlStyleConverter::parseKey( const QVariant &value )
if ( value.toList().size() > 1 ) if ( value.toList().size() > 1 )
return value.toList().at( 1 ).toString(); return value.toList().at( 1 ).toString();
else else
return value.toList().value( 0 ).toString(); {
QString valueString = value.toList().value( 0 ).toString();
if ( valueString == QLatin1String( "geometry-type" ) )
{
return QStringLiteral( "_geom_type" );
}
return valueString;
}
} }
return QgsExpression::quotedColumnRef( value.toString() ); return QgsExpression::quotedColumnRef( value.toString() );
} }

View File

@ -363,7 +363,7 @@ class CORE_EXPORT QgsMapBoxGlStyleConverter
* *
* \warning This is private API only, and may change in future QGIS versions * \warning This is private API only, and may change in future QGIS versions
*/ */
static QgsProperty parseInterpolateOpacityByZoom( const QVariantMap &json, int maxOpacity ); static QgsProperty parseInterpolateOpacityByZoom( const QVariantMap &json, int maxOpacity, QgsMapBoxGlStyleConversionContext &context );
/** /**
* Takes values from stops and uses either scale_linear() or scale_exp() functions * Takes values from stops and uses either scale_linear() or scale_exp() functions
@ -371,7 +371,7 @@ class CORE_EXPORT QgsMapBoxGlStyleConverter
* *
* \warning This is private API only, and may change in future QGIS versions * \warning This is private API only, and may change in future QGIS versions
*/ */
static QString parseOpacityStops( double base, const QVariantList &stops, int maxOpacity ); static QString parseOpacityStops( double base, const QVariantList &stops, int maxOpacity, QgsMapBoxGlStyleConversionContext &context );
/** /**
* Interpolates a point/offset with either scale_linear() or scale_exp() (depending on base value). * Interpolates a point/offset with either scale_linear() or scale_exp() (depending on base value).
@ -481,7 +481,7 @@ class CORE_EXPORT QgsMapBoxGlStyleConverter
* *
* \warning This is private API only, and may change in future QGIS versions * \warning This is private API only, and may change in future QGIS versions
*/ */
static QString interpolateExpression( double zoomMin, double zoomMax, double valueMin, double valueMax, double base, double multiplier = 1 ); static QString interpolateExpression( double zoomMin, double zoomMax, QVariant valueMin, QVariant valueMax, double base, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1 );
/** /**
* Converts a value to Qt::PenCapStyle enum from JSON value. * Converts a value to Qt::PenCapStyle enum from JSON value.