Skip to content

Commit

Permalink
[vectortiles] Convert fill-translate property to simple fill offset
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Sep 9, 2020
1 parent 8e7db03 commit ca3d53d
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 1 deletion.
Expand Up @@ -217,6 +217,7 @@ or ``None`` if the style could not be converted successfully.
Color,
Numeric,
Opacity,
Point,
};

void parseLayers( const QVariantList &layers, QgsMapBoxGlStyleConversionContext *context = 0 );
Expand Down Expand Up @@ -333,6 +334,26 @@ It uses QGIS :py:func:`~QgsMapBoxGlStyleConverter.set_color_part` function to se
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.

.. warning::

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

static QgsProperty parseInterpolatePointByZoom( const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1, QPointF *defaultPoint /Out/ = 0 );
%Docstring
Interpolates a point/offset 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.parsePointStops` function.

.. warning::

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

static QString parsePointStops( double base, const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1 );
%Docstring
Takes values from stops and uses either :py:func:`~QgsMapBoxGlStyleConverter.scale_linear` or :py:func:`~QgsMapBoxGlStyleConverter.scale_exp` functions
to interpolate point/offset values.

.. warning::

This is private API only, and may change in future QGIS versions
Expand Down
145 changes: 144 additions & 1 deletion src/core/vectortile/qgsmapboxglstyleconverter.cpp
Expand Up @@ -273,7 +273,29 @@ bool QgsMapBoxGlStyleConverter::parseFillLayer( const QVariantMap &jsonLayer, Qg
}
}

// TODO: fill-translate
// fill-translate
QPointF fillTranslate;
if ( jsonPaint.contains( QStringLiteral( "fill-translate" ) ) )
{
const QVariant jsonFillTranslate = jsonPaint.value( QStringLiteral( "fill-translate" ) );
switch ( jsonFillTranslate.type() )
{

case QVariant::Map:
ddProperties.setProperty( QgsSymbolLayer::PropertyOffset, parseInterpolatePointByZoom( jsonFillTranslate.toMap(), context, context.pixelSizeConversionFactor(), &fillTranslate ) );
break;

case QVariant::List:
case QVariant::StringList:
fillTranslate = QPointF( jsonFillTranslate.toList().value( 0 ).toDouble() * context.pixelSizeConversionFactor(),
jsonFillTranslate.toList().value( 1 ).toDouble() * context.pixelSizeConversionFactor() );
break;

default:
context.pushWarning( QObject::tr( "Skipping non-implemented fill-translate expression" ) );
break;
}
}

std::unique_ptr< QgsSymbol > symbol( QgsSymbol::defaultSymbol( QgsWkbTypes::PolygonGeometry ) );
QgsSimpleFillSymbolLayer *fillSymbol = dynamic_cast< QgsSimpleFillSymbolLayer * >( symbol->symbolLayer( 0 ) );
Expand All @@ -282,6 +304,12 @@ bool QgsMapBoxGlStyleConverter::parseFillLayer( const QVariantMap &jsonLayer, Qg
symbol->setOutputUnit( context.targetUnit() );
fillSymbol->setOutputUnit( context.targetUnit() );

if ( !fillTranslate.isNull() )
{
fillSymbol->setOffset( fillTranslate );
}
fillSymbol->setOffsetUnit( context.targetUnit() );

if ( jsonPaint.contains( QStringLiteral( "fill-pattern" ) ) )
{
// get fill-pattern to set sprite
Expand Down Expand Up @@ -1344,6 +1372,118 @@ QString QgsMapBoxGlStyleConverter::parseOpacityStops( double base, const QVarian
return caseString;
}

QgsProperty QgsMapBoxGlStyleConverter::parseInterpolatePointByZoom( const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier, QPointF *defaultPoint )
{
const double base = json.value( QStringLiteral( "base" ), QStringLiteral( "1" ) ).toDouble();
const QVariantList stops = json.value( QStringLiteral( "stops" ) ).toList();
if ( stops.empty() )
return QgsProperty();

QString scaleExpression;
if ( stops.size() <= 2 )
{
if ( base == 1 )
{
scaleExpression = QStringLiteral( "array(scale_linear(@zoom_level, %1, %2, %3, %4)" ).arg( stops.value( 0 ).toList().value( 0 ).toString(),
stops.last().toList().value( 0 ).toString(),
stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toString(),
stops.last().toList().value( 1 ).toList().value( 0 ).toString() );
if ( multiplier != 1.0 )
scaleExpression += QStringLiteral( " * %2" ).arg( multiplier );

scaleExpression += QStringLiteral( ",scale_linear(@zoom_level, %1, %2, %3, %4)" ).arg( stops.value( 0 ).toList().value( 0 ).toString(),
stops.last().toList().value( 0 ).toString(),
stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toString(),
stops.last().toList().value( 1 ).toList().value( 1 ).toString() );
if ( multiplier != 1.0 )
scaleExpression += QStringLiteral( " * %2" ).arg( multiplier );
scaleExpression += ')';
}
else
{
scaleExpression = QStringLiteral( "array(%1,%2)" ).arg( interpolateExpression( stops.value( 0 ).toList().value( 0 ).toInt(),
stops.last().toList().value( 0 ).toInt(),
stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toInt(),
stops.last().toList().value( 1 ).toList().value( 0 ).toInt(), base, multiplier ),
interpolateExpression( stops.value( 0 ).toList().value( 0 ).toInt(),
stops.last().toList().value( 0 ).toInt(),
stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toInt(),
stops.last().toList().value( 1 ).toList().value( 1 ).toInt(), base, multiplier )
);
}
}
else
{
scaleExpression = parsePointStops( base, stops, context, multiplier );
}

if ( !stops.empty() && defaultPoint )
*defaultPoint = QPointF( stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble() * multiplier,
stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble() * multiplier );

return QgsProperty::fromExpression( scaleExpression );
}

QString QgsMapBoxGlStyleConverter::parsePointStops( double base, const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier )
{
QString caseString = QStringLiteral( "CASE " );

for ( int i = 0; i < stops.length() - 1; ++i )
{
// bottom zoom and value
const QVariant bz = stops.value( i ).toList().value( 0 );
const QVariant bv = stops.value( i ).toList().value( 1 );
if ( bz.type() != QVariant::List && bz.type() != QVariant::StringList )
{
context.pushWarning( QObject::tr( "Could not convert offset interpolation, skipping." ) );
return QString();
}

// top zoom and value
const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
if ( tz.type() != QVariant::List && tz.type() != QVariant::StringList )
{
context.pushWarning( QObject::tr( "Could not convert offset interpolation, skipping." ) );
return QString();
}

if ( base == 1 )
{
// base = 1 -> scale_linear
caseString += QStringLiteral( "WHEN @zoom_level > %1 AND @zoom_level <= %2 "
"THEN array(scale_linear(@zoom_level, %1, %2, %3, %4)" ).arg( bz.toString(),
tz.toString(),
bv.toList().value( 0 ).toString(),
tv.toList().value( 0 ).toString() );
if ( multiplier != 1.0 )
{
caseString += QStringLiteral( "* %1 " ).arg( multiplier );
}
caseString += QStringLiteral( ",scale_linear(@zoom_level, %1, %2, %3, %4)" ).arg( bz.toString(),
tz.toString(),
bv.toList().value( 1 ).toString(),
tv.toList().value( 1 ).toString() );
if ( multiplier != 1.0 )
{
caseString += QStringLiteral( "* %1 " ).arg( multiplier );
}
caseString += ')';
}
else
{
// base != 1 -> scale_exp
caseString += QStringLiteral( "WHEN @zoom_level > %1 AND @zoom_level <= %2 "
"THEN array(%3,%4)" ).arg( bz.toString(),
tz.toString(),
interpolateExpression( bz.toInt(), tz.toInt(), bv.toList().value( 0 ).toDouble(), tv.toList().value( 0 ).toDouble(), base, multiplier ),
interpolateExpression( bz.toInt(), tz.toInt(), bv.toList().value( 1 ).toDouble(), tv.toList().value( 1 ).toDouble(), base, multiplier ) );
}
}
caseString += QStringLiteral( "END" );
return caseString;
}

QString QgsMapBoxGlStyleConverter::parseStops( double base, const QVariantList &stops, double multiplier, QgsMapBoxGlStyleConversionContext &context )
{
QString caseString = QStringLiteral( "CASE " );
Expand Down Expand Up @@ -1445,6 +1585,9 @@ QgsProperty QgsMapBoxGlStyleConverter::parseInterpolateListByZoom( const QVarian

case PropertyType::Opacity:
return parseInterpolateOpacityByZoom( props );

case PropertyType::Point:
return parseInterpolatePointByZoom( props, context, multiplier );
}
return QgsProperty();
}
Expand Down
17 changes: 17 additions & 0 deletions src/core/vectortile/qgsmapboxglstyleconverter.h
Expand Up @@ -240,6 +240,7 @@ class CORE_EXPORT QgsMapBoxGlStyleConverter
Color, //!< Color property
Numeric, //!< Numeric property (e.g. line width, text size)
Opacity, //!< Opacity property
Point, //!< Point/offset property
};

/**
Expand Down Expand Up @@ -344,6 +345,22 @@ class CORE_EXPORT QgsMapBoxGlStyleConverter
*/
static QString parseOpacityStops( double base, const QVariantList &stops );

/**
* Interpolates a point/offset with either scale_linear() or scale_exp() (depending on base value).
* For \a json with intermediate stops it uses parsePointStops() function.
*
* \warning This is private API only, and may change in future QGIS versions
*/
static QgsProperty parseInterpolatePointByZoom( const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1, QPointF *defaultPoint SIP_OUT = nullptr );

/**
* Takes values from stops and uses either scale_linear() or scale_exp() functions
* to interpolate point/offset values.
*
* \warning This is private API only, and may change in future QGIS versions
*/
static QString parsePointStops( double base, const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1 );

/**
* Parses a list of interpolation stops
*
Expand Down

0 comments on commit ca3d53d

Please sign in to comment.