mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-07 00:03:52 -05:00
Handle stops in text-field when converting mapbox styles
This commit is contained in:
parent
825482db2a
commit
54a979c600
@ -391,7 +391,6 @@ For ``json`` with intermediate stops it uses :py:func:`~QgsMapBoxGlStyleConverte
|
||||
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
|
||||
@ -436,6 +435,16 @@ Parses a list of interpolation stops containing string values.
|
||||
%End
|
||||
|
||||
|
||||
static QString parseLabelStops( const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context );
|
||||
%Docstring
|
||||
Parses a list of interpolation stops containing label values.
|
||||
|
||||
:param stops: definition of interpolation stops
|
||||
:param context: conversion context
|
||||
|
||||
:return: converted expression
|
||||
%End
|
||||
|
||||
static QgsProperty parseValueList( const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier = 1,
|
||||
int maxOpacity = 255, QColor *defaultColor /Out/ = 0, double *defaultNumber /Out/ = 0 );
|
||||
%Docstring
|
||||
|
||||
@ -1427,52 +1427,6 @@ void QgsMapBoxGlStyleConverter::parseSymbolLayer( const QVariantMap &jsonLayer,
|
||||
}
|
||||
|
||||
// convert field name
|
||||
|
||||
auto processLabelField = []( const QString & string, bool & isExpression )->QString
|
||||
{
|
||||
// {field_name} is permitted in string -- if multiple fields are present, convert them to an expression
|
||||
// but if single field is covered in {}, return it directly
|
||||
const QRegularExpression singleFieldRx( QStringLiteral( "^{([^}]+)}$" ) );
|
||||
const QRegularExpressionMatch match = singleFieldRx.match( string );
|
||||
if ( match.hasMatch() )
|
||||
{
|
||||
isExpression = false;
|
||||
return match.captured( 1 );
|
||||
}
|
||||
|
||||
const QRegularExpression multiFieldRx( QStringLiteral( "(?={[^}]+})" ) );
|
||||
const QStringList parts = string.split( multiFieldRx );
|
||||
if ( parts.size() > 1 )
|
||||
{
|
||||
isExpression = true;
|
||||
|
||||
QStringList res;
|
||||
for ( const QString &part : parts )
|
||||
{
|
||||
if ( part.isEmpty() )
|
||||
continue;
|
||||
|
||||
if ( !part.contains( '{' ) )
|
||||
{
|
||||
res << QgsExpression::quotedValue( part );
|
||||
continue;
|
||||
}
|
||||
|
||||
// part will start at a {field} reference
|
||||
const QStringList split = part.split( '}' );
|
||||
res << QgsExpression::quotedColumnRef( split.at( 0 ).mid( 1 ) );
|
||||
if ( !split.at( 1 ).isEmpty() )
|
||||
res << QgsExpression::quotedValue( split.at( 1 ) );
|
||||
}
|
||||
return QStringLiteral( "concat(%1)" ).arg( res.join( ',' ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
isExpression = false;
|
||||
return string;
|
||||
}
|
||||
};
|
||||
|
||||
if ( jsonLayout.contains( QStringLiteral( "text-field" ) ) )
|
||||
{
|
||||
const QVariant jsonTextField = jsonLayout.value( QStringLiteral( "text-field" ) );
|
||||
@ -1524,6 +1478,21 @@ void QgsMapBoxGlStyleConverter::parseSymbolLayer( const QVariantMap &jsonLayer,
|
||||
break;
|
||||
}
|
||||
|
||||
case QVariant::Map:
|
||||
{
|
||||
const QVariantList stops = jsonTextField.toMap().value( QStringLiteral( "stops" ) ).toList();
|
||||
if ( !stops.empty() )
|
||||
{
|
||||
labelSettings.fieldName = parseLabelStops( stops, context );
|
||||
labelSettings.isExpression = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
context.pushWarning( QObject::tr( "%1: Skipping unsupported text-field dictionary" ).arg( context.layerId() ) );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
context.pushWarning( QObject::tr( "%1: Skipping unsupported text-field type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonTextField.type() ) ) );
|
||||
break;
|
||||
@ -2508,6 +2477,70 @@ QString QgsMapBoxGlStyleConverter::parseStringStops( const QVariantList &stops,
|
||||
return caseString;
|
||||
}
|
||||
|
||||
QString QgsMapBoxGlStyleConverter::parseLabelStops( const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context )
|
||||
{
|
||||
QString caseString = QStringLiteral( "CASE " );
|
||||
|
||||
bool isExpression = false;
|
||||
for ( int i = 0; i < stops.length() - 1; ++i )
|
||||
{
|
||||
// bottom zoom and value
|
||||
const QVariant bz = stops.value( i ).toList().value( 0 );
|
||||
if ( bz.type() == QVariant::List || bz.type() == QVariant::StringList )
|
||||
{
|
||||
context.pushWarning( QObject::tr( "%1: Lists in label interpolation function are not supported, skipping." ).arg( context.layerId() ) );
|
||||
return QString();
|
||||
}
|
||||
|
||||
// top zoom
|
||||
const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
|
||||
if ( tz.type() == QVariant::List || tz.type() == QVariant::StringList )
|
||||
{
|
||||
context.pushWarning( QObject::tr( "%1: Lists in label interpolation function are not supported, skipping." ).arg( context.layerId() ) );
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
|
||||
if ( fieldPart.isEmpty() )
|
||||
fieldPart = QStringLiteral( "''" );
|
||||
else if ( !isExpression )
|
||||
fieldPart = QgsExpression::quotedColumnRef( fieldPart );
|
||||
|
||||
caseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom < %2 "
|
||||
"THEN %3 " ).arg( bz.toString(),
|
||||
tz.toString(),
|
||||
fieldPart ) ;
|
||||
}
|
||||
|
||||
{
|
||||
const QVariant bz = stops.constLast().toList().value( 0 );
|
||||
if ( bz.type() == QVariant::List || bz.type() == QVariant::StringList )
|
||||
{
|
||||
context.pushWarning( QObject::tr( "%1: Lists in label interpolation function are not supported, skipping." ).arg( context.layerId() ) );
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
|
||||
if ( fieldPart.isEmpty() )
|
||||
fieldPart = QStringLiteral( "''" );
|
||||
else if ( !isExpression )
|
||||
fieldPart = QgsExpression::quotedColumnRef( fieldPart );
|
||||
|
||||
caseString += QStringLiteral( "WHEN @vector_tile_zoom >= %1 "
|
||||
"THEN %3 " ).arg( bz.toString(),
|
||||
fieldPart ) ;
|
||||
}
|
||||
|
||||
QString defaultPart = processLabelField( stops.constFirst().toList().value( 1 ).toString(), isExpression );
|
||||
if ( defaultPart.isEmpty() )
|
||||
defaultPart = QStringLiteral( "''" );
|
||||
else if ( !isExpression )
|
||||
defaultPart = QgsExpression::quotedColumnRef( defaultPart );
|
||||
caseString += QStringLiteral( "ELSE %1 END" ).arg( defaultPart );
|
||||
|
||||
return caseString;
|
||||
}
|
||||
|
||||
QgsProperty QgsMapBoxGlStyleConverter::parseValueList( const QVariantList &json, QgsMapBoxGlStyleConverter::PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier, int maxOpacity, QColor *defaultColor, double *defaultNumber )
|
||||
{
|
||||
const QString method = json.value( 0 ).toString();
|
||||
@ -3249,6 +3282,51 @@ QString QgsMapBoxGlStyleConverter::parseKey( const QVariant &value, QgsMapBoxGlS
|
||||
return QgsExpression::quotedColumnRef( value.toString() );
|
||||
}
|
||||
|
||||
QString QgsMapBoxGlStyleConverter::processLabelField( const QString &string, bool &isExpression )
|
||||
{
|
||||
// {field_name} is permitted in string -- if multiple fields are present, convert them to an expression
|
||||
// but if single field is covered in {}, return it directly
|
||||
const QRegularExpression singleFieldRx( QStringLiteral( "^{([^}]+)}$" ) );
|
||||
const QRegularExpressionMatch match = singleFieldRx.match( string );
|
||||
if ( match.hasMatch() )
|
||||
{
|
||||
isExpression = false;
|
||||
return match.captured( 1 );
|
||||
}
|
||||
|
||||
const QRegularExpression multiFieldRx( QStringLiteral( "(?={[^}]+})" ) );
|
||||
const QStringList parts = string.split( multiFieldRx );
|
||||
if ( parts.size() > 1 )
|
||||
{
|
||||
isExpression = true;
|
||||
|
||||
QStringList res;
|
||||
for ( const QString &part : parts )
|
||||
{
|
||||
if ( part.isEmpty() )
|
||||
continue;
|
||||
|
||||
if ( !part.contains( '{' ) )
|
||||
{
|
||||
res << QgsExpression::quotedValue( part );
|
||||
continue;
|
||||
}
|
||||
|
||||
// part will start at a {field} reference
|
||||
const QStringList split = part.split( '}' );
|
||||
res << QgsExpression::quotedColumnRef( split.at( 0 ).mid( 1 ) );
|
||||
if ( !split.at( 1 ).isEmpty() )
|
||||
res << QgsExpression::quotedValue( split.at( 1 ) );
|
||||
}
|
||||
return QStringLiteral( "concat(%1)" ).arg( res.join( ',' ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
isExpression = false;
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
||||
QgsVectorTileRenderer *QgsMapBoxGlStyleConverter::renderer() const
|
||||
{
|
||||
return mRenderer ? mRenderer->clone() : nullptr;
|
||||
|
||||
@ -393,7 +393,6 @@ class CORE_EXPORT QgsMapBoxGlStyleConverter
|
||||
const QVariantMap &conversionMap,
|
||||
QString *defaultString SIP_OUT = nullptr );
|
||||
|
||||
|
||||
/**
|
||||
* Takes values from stops and uses either scale_linear() or scale_exp() functions
|
||||
* to interpolate point/offset values.
|
||||
@ -434,6 +433,16 @@ class CORE_EXPORT QgsMapBoxGlStyleConverter
|
||||
QString *defaultString SIP_OUT = nullptr );
|
||||
|
||||
|
||||
/**
|
||||
* Parses a list of interpolation stops containing label values.
|
||||
*
|
||||
* \param stops definition of interpolation stops
|
||||
* \param context conversion context
|
||||
*
|
||||
* \returns converted expression
|
||||
*/
|
||||
static QString parseLabelStops( const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context );
|
||||
|
||||
/**
|
||||
* Parses and converts a value list (e.g. an interpolate list).
|
||||
*
|
||||
@ -548,6 +557,8 @@ class CORE_EXPORT QgsMapBoxGlStyleConverter
|
||||
|
||||
static QString parseKey( const QVariant &value, QgsMapBoxGlStyleConversionContext &context );
|
||||
|
||||
static QString processLabelField( const QString &string, bool &isExpression );
|
||||
|
||||
/**
|
||||
* Checks if interpolation bottom/top values are numeric values
|
||||
* \param bottomVariant bottom value
|
||||
|
||||
@ -874,6 +874,34 @@ class TestQgsMapBoxGlStyleConverter(unittest.TestCase):
|
||||
self.assertFalse(labeling_style.labelSettings().isExpression)
|
||||
self.assertEqual(labeling_style.labelSettings().fieldName, 'substance')
|
||||
|
||||
def testLabelWithStops(self):
|
||||
context = QgsMapBoxGlStyleConversionContext()
|
||||
style = {
|
||||
"layout": {
|
||||
"visibility": "visible",
|
||||
"text-field": {
|
||||
"stops": [
|
||||
[
|
||||
6,
|
||||
""
|
||||
],
|
||||
[
|
||||
15,
|
||||
"my {class} and {stuff}"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"paint": {
|
||||
"text-color": "rgba(47, 47, 47, 1)",
|
||||
},
|
||||
"type": "symbol"
|
||||
}
|
||||
rendererStyle, has_renderer, labeling_style, has_labeling = QgsMapBoxGlStyleConverter.parseSymbolLayer(style, context)
|
||||
self.assertTrue(has_labeling)
|
||||
self.assertTrue(labeling_style.labelSettings().isExpression)
|
||||
self.assertEqual(labeling_style.labelSettings().fieldName, 'CASE WHEN @vector_tile_zoom > 6 AND @vector_tile_zoom < 15 THEN concat(\'my \',"class",\' and \',"stuff") WHEN @vector_tile_zoom >= 15 THEN concat(\'my \',"class",\' and \',"stuff") ELSE \'\' END')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user