Skip to content

Commit

Permalink
[mvt] improve conversion of sprites
Browse files Browse the repository at this point in the history
fixes some cases where the sprite was not defined (mainly when data defined) + support step definition
  • Loading branch information
3nids committed Jul 24, 2024
1 parent 398b151 commit b9d09a9
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -777,12 +777,22 @@ The ``context`` must have valid sprite definitions and images set via :py:func:`
prior to conversion.
%End

static QString retrieveSpriteAsBase64( const QVariant &value, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize, QString &spriteProperty, QString &spriteSizeProperty );
static QString retrieveSpriteAsBase64( const QVariant &value, QgsMapBoxGlStyleConversionContext &context );
%Docstring
Retrieves the sprite image with the specified ``name``, taken from the specified ``context`` as a base64 encoded value

The ``context`` must have valid sprite definitions and images set via :py:func:`QgsMapBoxGlStyleConversionContext.setSprites()`
prior to conversion.
%End

static QString retrieveSpriteAsBase64WithProperties( const QVariant &value, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize /Out/, QString &spriteProperty /Out/, QString &spriteSizeProperty /Out/ );
%Docstring
Retrieves the sprite image with the specified ``name``, taken from the specified ``context`` as a base64 encoded value

The ``context`` must have valid sprite definitions and images set via :py:func:`QgsMapBoxGlStyleConversionContext.setSprites()`
prior to conversion.

.. versionadded:: 3.40
%End

private:
Expand Down
3 changes: 2 additions & 1 deletion python/PyQt6/core/class_map.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8386,7 +8386,8 @@ QgsMapBoxGlStyleConverter.parseSymbolLayerAsRenderer: src/core/vectortile/qgsmap
QgsMapBoxGlStyleConverter.parseValueList: src/core/vectortile/qgsmapboxglstyleconverter.h#L662
QgsMapBoxGlStyleConverter.renderer: src/core/vectortile/qgsmapboxglstyleconverter.h#L425
QgsMapBoxGlStyleConverter.retrieveSprite: src/core/vectortile/qgsmapboxglstyleconverter.h#L751
QgsMapBoxGlStyleConverter.retrieveSpriteAsBase64: src/core/vectortile/qgsmapboxglstyleconverter.h#L759
QgsMapBoxGlStyleConverter.retrieveSpriteAsBase64: src/core/vectortile/qgsmapboxglstyleconverter.h#L765
QgsMapBoxGlStyleConverter.retrieveSpriteAsBase64WithProperties: src/core/vectortile/qgsmapboxglstyleconverter.h#L775
QgsMapBoxGlStyleConverter.warnings: src/core/vectortile/qgsmapboxglstyleconverter.h#L419
QgsMapBoxGlStyleConverter: src/core/vectortile/qgsmapboxglstyleconverter.h#L341
QgsMapBoxGlStyleRasterSource.attribution: src/core/vectortile/qgsmapboxglstyleconverter.h#L242
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -777,12 +777,22 @@ The ``context`` must have valid sprite definitions and images set via :py:func:`
prior to conversion.
%End

static QString retrieveSpriteAsBase64( const QVariant &value, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize, QString &spriteProperty, QString &spriteSizeProperty );
static QString retrieveSpriteAsBase64( const QVariant &value, QgsMapBoxGlStyleConversionContext &context );
%Docstring
Retrieves the sprite image with the specified ``name``, taken from the specified ``context`` as a base64 encoded value

The ``context`` must have valid sprite definitions and images set via :py:func:`QgsMapBoxGlStyleConversionContext.setSprites()`
prior to conversion.
%End

static QString retrieveSpriteAsBase64WithProperties( const QVariant &value, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize /Out/, QString &spriteProperty /Out/, QString &spriteSizeProperty /Out/ );
%Docstring
Retrieves the sprite image with the specified ``name``, taken from the specified ``context`` as a base64 encoded value

The ``context`` must have valid sprite definitions and images set via :py:func:`QgsMapBoxGlStyleConversionContext.setSprites()`
prior to conversion.

.. versionadded:: 3.40
%End

private:
Expand Down
3 changes: 2 additions & 1 deletion python/core/class_map.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8386,7 +8386,8 @@ QgsMapBoxGlStyleConverter.parseSymbolLayerAsRenderer: src/core/vectortile/qgsmap
QgsMapBoxGlStyleConverter.parseValueList: src/core/vectortile/qgsmapboxglstyleconverter.h#L662
QgsMapBoxGlStyleConverter.renderer: src/core/vectortile/qgsmapboxglstyleconverter.h#L425
QgsMapBoxGlStyleConverter.retrieveSprite: src/core/vectortile/qgsmapboxglstyleconverter.h#L751
QgsMapBoxGlStyleConverter.retrieveSpriteAsBase64: src/core/vectortile/qgsmapboxglstyleconverter.h#L759
QgsMapBoxGlStyleConverter.retrieveSpriteAsBase64: src/core/vectortile/qgsmapboxglstyleconverter.h#L765
QgsMapBoxGlStyleConverter.retrieveSpriteAsBase64WithProperties: src/core/vectortile/qgsmapboxglstyleconverter.h#L775
QgsMapBoxGlStyleConverter.warnings: src/core/vectortile/qgsmapboxglstyleconverter.h#L419
QgsMapBoxGlStyleConverter: src/core/vectortile/qgsmapboxglstyleconverter.h#L341
QgsMapBoxGlStyleRasterSource.attribution: src/core/vectortile/qgsmapboxglstyleconverter.h#L242
Expand Down
144 changes: 103 additions & 41 deletions src/core/vectortile/qgsmapboxglstyleconverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ bool QgsMapBoxGlStyleConverter::parseFillLayer( const QVariantMap &jsonLayer, Qg

QSize spriteSize;
QString spriteProperty, spriteSizeProperty;
const QString sprite = retrieveSpriteAsBase64( fillPatternJson, context, spriteSize, spriteProperty, spriteSizeProperty );
const QString sprite = retrieveSpriteAsBase64WithProperties( fillPatternJson, context, spriteSize, spriteProperty, spriteSizeProperty );
if ( !sprite.isEmpty() )
{
// when fill-pattern exists, set and insert QgsRasterFillSymbolLayer
Expand Down Expand Up @@ -496,7 +496,7 @@ bool QgsMapBoxGlStyleConverter::parseLineLayer( const QVariantMap &jsonLayer, Qg
{
QSize spriteSize;
QString spriteProperty, spriteSizeProperty;
rasterLineSprite = retrieveSpriteAsBase64( jsonLinePattern, context, spriteSize, spriteProperty, spriteSizeProperty );
rasterLineSprite = retrieveSpriteAsBase64WithProperties( jsonLinePattern, context, spriteSize, spriteProperty, spriteSizeProperty );
ddProperties.setProperty( QgsSymbolLayer::Property::File, QgsProperty::fromExpression( spriteProperty ) );
break;
}
Expand Down Expand Up @@ -1838,7 +1838,7 @@ void QgsMapBoxGlStyleConverter::parseSymbolLayer( const QVariantMap &jsonLayer,
{
QSize spriteSize;
QString spriteProperty, spriteSizeProperty;
const QString sprite = retrieveSpriteAsBase64( jsonLayout.value( QStringLiteral( "icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
const QString sprite = retrieveSpriteAsBase64WithProperties( jsonLayout.value( QStringLiteral( "icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
if ( !sprite.isEmpty() )
{
QgsRasterMarkerSymbolLayer *markerLayer = new QgsRasterMarkerSymbolLayer( );
Expand Down Expand Up @@ -1986,7 +1986,7 @@ bool QgsMapBoxGlStyleConverter::parseSymbolLayerAsRenderer( const QVariantMap &j
QgsRasterMarkerSymbolLayer *markerLayer = new QgsRasterMarkerSymbolLayer( );
QSize spriteSize;
QString spriteProperty, spriteSizeProperty;
const QString sprite = retrieveSpriteAsBase64( jsonLayout.value( QStringLiteral( "icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
const QString sprite = retrieveSpriteAsBase64WithProperties( jsonLayout.value( QStringLiteral( "icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
if ( !sprite.isNull() )
{
markerLayer->setPath( sprite );
Expand Down Expand Up @@ -2066,8 +2066,8 @@ bool QgsMapBoxGlStyleConverter::parseSymbolLayerAsRenderer( const QVariantMap &j

QSize spriteSize;
QString spriteProperty, spriteSizeProperty;
const QString sprite = retrieveSpriteAsBase64( jsonLayout.value( QStringLiteral( "icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
if ( !sprite.isEmpty() )
const QString sprite = retrieveSpriteAsBase64WithProperties( jsonLayout.value( QStringLiteral( "icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
if ( !sprite.isEmpty() || !spriteProperty.isEmpty() )
{
QgsRasterMarkerSymbolLayer *rasterMarker = new QgsRasterMarkerSymbolLayer( );
rasterMarker->setPath( sprite );
Expand Down Expand Up @@ -3150,7 +3150,7 @@ QImage QgsMapBoxGlStyleConverter::retrieveSprite( const QString &name, QgsMapBox
return sprite;
}

QString QgsMapBoxGlStyleConverter::retrieveSpriteAsBase64( const QVariant &value, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize, QString &spriteProperty, QString &spriteSizeProperty )
QString QgsMapBoxGlStyleConverter::retrieveSpriteAsBase64WithProperties( const QVariant &value, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize, QString &spriteProperty, QString &spriteSizeProperty )
{
QString spritePath;

Expand Down Expand Up @@ -3280,53 +3280,115 @@ QString QgsMapBoxGlStyleConverter::retrieveSpriteAsBase64( const QVariant &value
{
const QVariantList json = value.toList();
const QString method = json.value( 0 ).toString();
if ( method != QLatin1String( "match" ) )
{
context.pushWarning( QObject::tr( "%1: Could not interpret sprite value list with method %2" ).arg( context.layerId(), method ) );
break;
}

const QString attribute = parseExpression( json.value( 1 ).toList(), context );
if ( attribute.isEmpty() )
if ( method == QLatin1String( "match" ) )
{
context.pushWarning( QObject::tr( "%1: Could not interpret match list" ).arg( context.layerId() ) );
break;
}
const QString attribute = parseExpression( json.value( 1 ).toList(), context );
if ( attribute.isEmpty() )
{
context.pushWarning( QObject::tr( "%1: Could not interpret match list" ).arg( context.layerId() ) );
break;
}

spriteProperty = QStringLiteral( "CASE " );
spriteSizeProperty = QStringLiteral( "CASE " );
spriteProperty = QStringLiteral( "CASE" );
spriteSizeProperty = QStringLiteral( "CASE" );

for ( int i = 2; i < json.length() - 1; i += 2 )
{
const QVariantList keys = json.value( i ).toList();
for ( int i = 2; i < json.length() - 1; i += 2 )
{
const QVariant matchKey = json.value( i );
const QVariant matchValue = json.value( i + 1 );
QString matchString;
switch ( matchKey.userType() )
{
case QMetaType::Type::QVariantList:
case QMetaType::Type::QStringList:
{
const QVariantList keys = matchKey.toList();
QStringList matchStringList;
for ( const QVariant &key : keys )
{
matchStringList << QgsExpression::quotedValue( key );
}
matchString = matchStringList.join( ',' );
break;
}

case QMetaType::Type::Bool:
case QMetaType::Type::QString:
case QMetaType::Type::Int:
case QMetaType::Type::LongLong:
case QMetaType::Type::Double:
{
matchString = QgsExpression::quotedValue( matchKey );
break;
}

default:
context.pushWarning( QObject::tr( "%1: Skipping unsupported sprite type (%2)." ).arg( context.layerId(), QMetaType::typeName( static_cast<QMetaType::Type>( value.userType() ) ) ) );
break;

}

const QImage sprite = retrieveSprite( matchValue.toString(), context, spriteSize );
spritePath = prepareBase64( sprite );

spriteProperty += QStringLiteral( " WHEN %1 IN (%2) "
"THEN '%3'" ).arg( attribute,
matchString,
spritePath );

spriteSizeProperty += QStringLiteral( " WHEN %1 IN (%2) "
"THEN %3" ).arg( attribute,
matchString ).arg( spriteSize.width() );
}

const QImage sprite = retrieveSprite( json.constLast().toString(), context, spriteSize );
spritePath = prepareBase64( sprite );

QStringList matchString;
for ( const QVariant &key : keys )
spriteProperty += QStringLiteral( " ELSE '%1' END" ).arg( spritePath );
spriteSizeProperty += QStringLiteral( " ELSE %3 END" ).arg( spriteSize.width() );
break;
}
else if ( method == QLatin1String( "step" ) )
{
QString attribute = json.value( 1 ).toList().value( 0 ).toString();
if ( attribute == QStringLiteral( "zoom" ) )
{
matchString << QgsExpression::quotedValue( key );
attribute = QStringLiteral( "@vector_tile_zoom" );
}
else
{
context.pushWarning( QObject::tr( "%1: Could not interpret step list with attribute %2" ).arg( context.layerId(), attribute ) );
break;
}

spriteProperty = QStringLiteral( "CASE" );
spriteSizeProperty = QStringLiteral( "CASE" );
for ( int i = json.length() - 2; i > 2; i -= 2 )
{
const QString stepKey = QgsExpression::quotedValue( json.value( i ) );
const QString stepValue = json.value( i + 1 ).toString();

const QImage sprite = retrieveSprite( stepValue, context, spriteSize );
spritePath = prepareBase64( sprite );

const QVariant value = json.value( i + 1 );
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() );
}

const QImage sprite = retrieveSprite( value.toString(), context, spriteSize );
const QImage sprite = retrieveSprite( json.at( 2 ).toString(), context, spriteSize );
spritePath = prepareBase64( sprite );

spriteProperty += QStringLiteral( " WHEN %1 IN (%2) "
"THEN '%3' " ).arg( attribute,
matchString.join( ',' ),
spritePath );
spriteProperty += QStringLiteral( "ELSE '%1' END" ).arg( spritePath );
spriteSizeProperty += QStringLiteral( "ELSE %3 END" ).arg( spriteSize.width() );
break;

spriteSizeProperty += QStringLiteral( " WHEN %1 IN (%2) "
"THEN %3 " ).arg( attribute,
matchString.join( ',' ) ).arg( spriteSize.width() );
}

const QImage sprite = retrieveSprite( json.constLast().toString(), context, spriteSize );
spritePath = prepareBase64( sprite );

spriteProperty += QStringLiteral( "ELSE %1 END" ).arg( spritePath );
spriteSizeProperty += QStringLiteral( "ELSE %3 END" ).arg( spriteSize.width() );
break;
else
{
context.pushWarning( QObject::tr( "%1: Could not interpret sprite value list with method %2" ).arg( context.layerId(), method ) );
break;
}
}

default:
Expand Down
18 changes: 17 additions & 1 deletion src/core/vectortile/qgsmapboxglstyleconverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,23 @@ class CORE_EXPORT QgsMapBoxGlStyleConverter
* The \a context must have valid sprite definitions and images set via QgsMapBoxGlStyleConversionContext::setSprites()
* prior to conversion.
*/
static QString retrieveSpriteAsBase64( const QVariant &value, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize, QString &spriteProperty, QString &spriteSizeProperty );
static QString retrieveSpriteAsBase64( const QVariant &value, QgsMapBoxGlStyleConversionContext &context )
{
QSize spriteSize;
QString spriteProperty;
QString spriteSizeProperty;
return retrieveSpriteAsBase64WithProperties( value, context, spriteSize, spriteProperty, spriteSizeProperty );
}

/**
* Retrieves the sprite image with the specified \a name, taken from the specified \a context as a base64 encoded value
*
* The \a context must have valid sprite definitions and images set via QgsMapBoxGlStyleConversionContext::setSprites()
* prior to conversion.
*
* \since QGIS 3.40
*/
static QString retrieveSpriteAsBase64WithProperties( const QVariant &value, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize SIP_OUT, QString &spriteProperty SIP_OUT, QString &spriteSizeProperty SIP_OUT );

private:

Expand Down
Loading

0 comments on commit b9d09a9

Please sign in to comment.