41 #include <QRegularExpression>
51 if ( style.contains( QStringLiteral(
"layers" ) ) )
53 parseLayers( style.value( QStringLiteral(
"layers" ) ).toList(), context );
57 mError = QObject::tr(
"Could not find layers list in JSON" );
72 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
75 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
76 context = tmpContext.get();
79 QList<QgsVectorTileBasicRendererStyle> rendererStyles;
80 QList<QgsVectorTileBasicLabelingStyle> labelingStyles;
83 bool hasRendererBackgroundStyle =
false;
85 for (
const QVariant &layer : layers )
87 const QVariantMap jsonLayer = layer.toMap();
89 const QString layerType = jsonLayer.value( QStringLiteral(
"type" ) ).toString();
90 if ( layerType == QLatin1String(
"background" ) )
92 hasRendererBackgroundStyle =
parseFillLayer( jsonLayer, rendererBackgroundStyle, *context,
true );
93 if ( hasRendererBackgroundStyle )
103 const QString styleId = jsonLayer.value( QStringLiteral(
"id" ) ).toString();
105 const QString layerName = jsonLayer.value( QStringLiteral(
"source-layer" ) ).toString();
107 const int minZoom = jsonLayer.value( QStringLiteral(
"minzoom" ), QStringLiteral(
"-1" ) ).toInt();
108 const int maxZoom = jsonLayer.value( QStringLiteral(
"maxzoom" ), QStringLiteral(
"-1" ) ).toInt();
110 const bool enabled = jsonLayer.value( QStringLiteral(
"visibility" ) ).toString() != QLatin1String(
"none" );
112 QString filterExpression;
113 if ( jsonLayer.contains( QStringLiteral(
"filter" ) ) )
115 filterExpression =
parseExpression( jsonLayer.value( QStringLiteral(
"filter" ) ).toList(), *context );
121 bool hasRendererStyle =
false;
122 bool hasLabelingStyle =
false;
123 if ( layerType == QLatin1String(
"fill" ) )
125 hasRendererStyle =
parseFillLayer( jsonLayer, rendererStyle, *context );
127 else if ( layerType == QLatin1String(
"line" ) )
129 hasRendererStyle =
parseLineLayer( jsonLayer, rendererStyle, *context );
131 else if ( layerType == QLatin1String(
"circle" ) )
135 else if ( layerType == QLatin1String(
"symbol" ) )
137 parseSymbolLayer( jsonLayer, rendererStyle, hasRendererStyle, labelingStyle, hasLabelingStyle, *context );
141 mWarnings << QObject::tr(
"%1: Skipping unknown layer type %2" ).arg( context->
layerId(), layerType );
146 if ( hasRendererStyle )
154 rendererStyles.append( rendererStyle );
157 if ( hasLabelingStyle )
165 labelingStyles.append( labelingStyle );
168 mWarnings.append( context->
warnings() );
172 if ( hasRendererBackgroundStyle )
173 rendererStyles.prepend( rendererBackgroundStyle );
175 mRenderer = std::make_unique< QgsVectorTileBasicRenderer >();
177 renderer->setStyles( rendererStyles );
179 mLabeling = std::make_unique< QgsVectorTileBasicLabeling >();
181 labeling->setStyles( labelingStyles );
186 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
191 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsFillSymbol >() );
195 if ( jsonPaint.contains( isBackgroundStyle ? QStringLiteral(
"background-color" ) : QStringLiteral(
"fill-color" ) ) )
197 const QVariant jsonFillColor = jsonPaint.value( isBackgroundStyle ? QStringLiteral(
"background-color" ) : QStringLiteral(
"fill-color" ) );
198 switch ( jsonFillColor.type() )
205 case QVariant::StringList:
209 case QVariant::String:
210 fillColor =
parseColor( jsonFillColor.toString(), context );
223 fillColor = QColor( 0, 0, 0 );
226 QColor fillOutlineColor;
227 if ( !isBackgroundStyle )
229 if ( !jsonPaint.contains( QStringLiteral(
"fill-outline-color" ) ) )
231 if ( fillColor.isValid() )
232 fillOutlineColor = fillColor;
240 const QVariant jsonFillOutlineColor = jsonPaint.value( QStringLiteral(
"fill-outline-color" ) );
241 switch ( jsonFillOutlineColor.type() )
248 case QVariant::StringList:
252 case QVariant::String:
253 fillOutlineColor =
parseColor( jsonFillOutlineColor.toString(), context );
263 double fillOpacity = -1.0;
264 double rasterOpacity = -1.0;
265 if ( jsonPaint.contains( isBackgroundStyle ? QStringLiteral(
"background-opacity" ) : QStringLiteral(
"fill-opacity" ) ) )
267 const QVariant jsonFillOpacity = jsonPaint.value( isBackgroundStyle ? QStringLiteral(
"background-opacity" ) : QStringLiteral(
"fill-opacity" ) );
268 switch ( jsonFillOpacity.type() )
271 case QVariant::LongLong:
272 case QVariant::Double:
273 fillOpacity = jsonFillOpacity.toDouble();
274 rasterOpacity = fillOpacity;
291 case QVariant::StringList:
311 QPointF fillTranslate;
312 if ( jsonPaint.contains( QStringLiteral(
"fill-translate" ) ) )
314 const QVariant jsonFillTranslate = jsonPaint.value( QStringLiteral(
"fill-translate" ) );
315 switch ( jsonFillTranslate.type() )
323 case QVariant::StringList:
335 Q_ASSERT( fillSymbol );
338 symbol->setOutputUnit( context.
targetUnit() );
341 if ( !fillTranslate.isNull() )
347 if ( jsonPaint.contains( isBackgroundStyle ? QStringLiteral(
"background-pattern" ) : QStringLiteral(
"fill-pattern" ) ) )
351 const QVariant fillPatternJson = jsonPaint.value( isBackgroundStyle ? QStringLiteral(
"background-pattern" ) : QStringLiteral(
"fill-pattern" ) );
354 fillColor = QColor();
355 fillOutlineColor = QColor();
362 QString spriteProperty, spriteSizeProperty;
363 const QString sprite =
retrieveSpriteAsBase64( fillPatternJson, context, spriteSize, spriteProperty, spriteSizeProperty );
364 if ( !sprite.isEmpty() )
369 rasterFill->
setWidth( spriteSize.width() );
373 if ( rasterOpacity >= 0 )
378 if ( !spriteProperty.isEmpty() )
385 symbol->appendSymbolLayer( rasterFill );
391 if ( fillOpacity != -1 )
393 symbol->setOpacity( fillOpacity );
396 if ( fillOutlineColor.isValid() )
405 if ( fillColor.isValid() )
421 if ( !jsonLayer.contains( QStringLiteral(
"paint" ) ) )
423 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
428 QString rasterLineSprite;
430 const QVariantMap jsonPaint = jsonLayer.
value( QStringLiteral(
"paint" ) ).toMap();
431 if ( jsonPaint.contains( QStringLiteral(
"line-pattern" ) ) )
433 const QVariant jsonLinePattern = jsonPaint.value( QStringLiteral(
"line-pattern" ) );
434 switch ( jsonLinePattern.type() )
437 case QVariant::String:
440 QString spriteProperty, spriteSizeProperty;
441 rasterLineSprite =
retrieveSpriteAsBase64( jsonLinePattern, context, spriteSize, spriteProperty, spriteSizeProperty );
447 case QVariant::StringList:
452 if ( rasterLineSprite.isEmpty() )
455 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-pattern property" ).arg( context.
layerId() ) );
462 if ( jsonPaint.contains( QStringLiteral(
"line-color" ) ) )
464 const QVariant jsonLineColor = jsonPaint.value( QStringLiteral(
"line-color" ) );
465 switch ( jsonLineColor.type() )
473 case QVariant::StringList:
478 case QVariant::String:
479 lineColor =
parseColor( jsonLineColor.toString(), context );
490 lineColor = QColor( 0, 0, 0 );
494 double lineWidth = 1.0;
496 if ( jsonPaint.contains( QStringLiteral(
"line-width" ) ) )
498 const QVariant jsonLineWidth = jsonPaint.
value( QStringLiteral(
"line-width" ) );
499 switch ( jsonLineWidth.type() )
502 case QVariant::LongLong:
503 case QVariant::Double:
514 case QVariant::StringList:
525 double lineOffset = 0.0;
526 if ( jsonPaint.contains( QStringLiteral(
"line-offset" ) ) )
528 const QVariant jsonLineOffset = jsonPaint.value( QStringLiteral(
"line-offset" ) );
529 switch ( jsonLineOffset.type() )
532 case QVariant::LongLong:
533 case QVariant::Double:
543 case QVariant::StringList:
553 double lineOpacity = -1.0;
554 if ( jsonPaint.contains( QStringLiteral(
"line-opacity" ) ) )
556 const QVariant jsonLineOpacity = jsonPaint.value( QStringLiteral(
"line-opacity" ) );
557 switch ( jsonLineOpacity.type() )
560 case QVariant::LongLong:
561 case QVariant::Double:
562 lineOpacity = jsonLineOpacity.toDouble();
568 context.
pushWarning( QObject::tr(
"%1: Could not set opacity of layer, opacity already defined in stroke color" ).arg( context.
layerId() ) );
577 case QVariant::StringList:
580 context.
pushWarning( QObject::tr(
"%1: Could not set opacity of layer, opacity already defined in stroke color" ).arg( context.
layerId() ) );
594 QVector< double > dashVector;
595 if ( jsonPaint.contains( QStringLiteral(
"line-dasharray" ) ) )
597 const QVariant jsonLineDashArray = jsonPaint.value( QStringLiteral(
"line-dasharray" ) );
598 switch ( jsonLineDashArray.type() )
602 QString arrayExpression;
605 arrayExpression = QStringLiteral(
"array_to_string(array_foreach(%1,@element * (%2)), ';')" )
606 .arg(
parseArrayStops( jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList(), context, 1 ),
611 arrayExpression = QStringLiteral(
"array_to_string(%1, ';')" ).arg(
parseArrayStops( jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList(), context, lineWidth ) );
615 const QVariantList dashSource = jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList().first().toList().value( 1 ).toList();
616 for (
const QVariant &v : dashSource )
618 dashVector << v.toDouble() * lineWidth;
624 case QVariant::StringList:
626 const QVariantList dashSource = jsonLineDashArray.toList();
628 QVector< double > rawDashVectorSizes;
629 rawDashVectorSizes.reserve( dashSource.size() );
630 for (
const QVariant &v : dashSource )
632 rawDashVectorSizes << v.toDouble();
636 if ( rawDashVectorSizes.size() == 1 )
639 rawDashVectorSizes.clear();
641 else if ( rawDashVectorSizes.size() % 2 == 1 )
645 rawDashVectorSizes[0] = rawDashVectorSizes[0] + rawDashVectorSizes[rawDashVectorSizes.size() - 1];
646 rawDashVectorSizes.resize( rawDashVectorSizes.size() - 1 );
649 if ( !rawDashVectorSizes.isEmpty() && ( !lineWidthProperty.
asExpression().isEmpty() ) )
651 QStringList dashArrayStringParts;
652 dashArrayStringParts.reserve( rawDashVectorSizes.size() );
653 for (
double v : std::as_const( rawDashVectorSizes ) )
658 QString arrayExpression = QStringLiteral(
"array_to_string(array_foreach(array(%1),@element * (%2)), ';')" )
659 .arg( dashArrayStringParts.join(
',' ),
665 for (
double v : std::as_const( rawDashVectorSizes ) )
667 dashVector << v *lineWidth;
679 Qt::PenCapStyle penCapStyle = Qt::FlatCap;
680 Qt::PenJoinStyle penJoinStyle = Qt::MiterJoin;
681 if ( jsonLayer.contains( QStringLiteral(
"layout" ) ) )
683 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
684 if ( jsonLayout.contains( QStringLiteral(
"line-cap" ) ) )
686 penCapStyle =
parseCapStyle( jsonLayout.value( QStringLiteral(
"line-cap" ) ).toString() );
688 if ( jsonLayout.contains( QStringLiteral(
"line-join" ) ) )
690 penJoinStyle =
parseJoinStyle( jsonLayout.value( QStringLiteral(
"line-join" ) ).toString() );
694 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsLineSymbol >() );
695 symbol->setOutputUnit( context.
targetUnit() );
697 if ( !rasterLineSprite.isEmpty() )
707 if ( lineOpacity != -1 )
709 symbol->setOpacity( lineOpacity );
711 if ( lineWidth != -1 )
715 symbol->changeSymbolLayer( 0, lineSymbol );
720 Q_ASSERT( lineSymbol );
730 if ( lineOpacity != -1 )
732 symbol->setOpacity( lineOpacity );
734 if ( lineColor.isValid() )
738 if ( lineWidth != -1 )
742 if ( !dashVector.empty() )
756 if ( !jsonLayer.contains( QStringLiteral(
"paint" ) ) )
758 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
762 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
766 QColor circleFillColor;
767 if ( jsonPaint.contains( QStringLiteral(
"circle-color" ) ) )
769 const QVariant jsonCircleColor = jsonPaint.
value( QStringLiteral(
"circle-color" ) );
770 switch ( jsonCircleColor.type() )
777 case QVariant::StringList:
781 case QVariant::String:
782 circleFillColor =
parseColor( jsonCircleColor.toString(), context );
793 circleFillColor = QColor( 0, 0, 0 );
797 double circleDiameter = 10.0;
798 if ( jsonPaint.contains( QStringLiteral(
"circle-radius" ) ) )
800 const QVariant jsonCircleRadius = jsonPaint.value( QStringLiteral(
"circle-radius" ) );
801 switch ( jsonCircleRadius.type() )
804 case QVariant::LongLong:
805 case QVariant::Double:
815 case QVariant::StringList:
825 double circleOpacity = -1.0;
826 if ( jsonPaint.contains( QStringLiteral(
"circle-opacity" ) ) )
828 const QVariant jsonCircleOpacity = jsonPaint.value( QStringLiteral(
"circle-opacity" ) );
829 switch ( jsonCircleOpacity.type() )
832 case QVariant::LongLong:
833 case QVariant::Double:
834 circleOpacity = jsonCircleOpacity.toDouble();
842 case QVariant::StringList:
851 if ( ( circleOpacity != -1 ) && circleFillColor.isValid() )
853 circleFillColor.setAlphaF( circleOpacity );
857 QColor circleStrokeColor;
858 if ( jsonPaint.contains( QStringLiteral(
"circle-stroke-color" ) ) )
860 const QVariant jsonCircleStrokeColor = jsonPaint.value( QStringLiteral(
"circle-stroke-color" ) );
861 switch ( jsonCircleStrokeColor.type() )
868 case QVariant::StringList:
872 case QVariant::String:
873 circleStrokeColor =
parseColor( jsonCircleStrokeColor.toString(), context );
883 double circleStrokeWidth = -1.0;
884 if ( jsonPaint.contains( QStringLiteral(
"circle-stroke-width" ) ) )
886 const QVariant circleStrokeWidthJson = jsonPaint.value( QStringLiteral(
"circle-stroke-width" ) );
887 switch ( circleStrokeWidthJson.type() )
890 case QVariant::LongLong:
891 case QVariant::Double:
896 circleStrokeWidth = -1.0;
901 case QVariant::StringList:
911 double circleStrokeOpacity = -1.0;
912 if ( jsonPaint.contains( QStringLiteral(
"circle-stroke-opacity" ) ) )
914 const QVariant jsonCircleStrokeOpacity = jsonPaint.value( QStringLiteral(
"circle-stroke-opacity" ) );
915 switch ( jsonCircleStrokeOpacity.type() )
918 case QVariant::LongLong:
919 case QVariant::Double:
920 circleStrokeOpacity = jsonCircleStrokeOpacity.toDouble();
928 case QVariant::StringList:
937 if ( ( circleStrokeOpacity != -1 ) && circleStrokeColor.isValid() )
939 circleStrokeColor.setAlphaF( circleStrokeOpacity );
943 QPointF circleTranslate;
944 if ( jsonPaint.contains( QStringLiteral(
"circle-translate" ) ) )
946 const QVariant jsonCircleTranslate = jsonPaint.value( QStringLiteral(
"circle-translate" ) );
947 switch ( jsonCircleTranslate.type() )
955 case QVariant::StringList:
966 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsMarkerSymbol >() );
968 Q_ASSERT( markerSymbolLayer );
971 symbol->setOutputUnit( context.
targetUnit() );
972 symbol->setDataDefinedProperties( ddProperties );
974 if ( !circleTranslate.isNull() )
976 markerSymbolLayer->
setOffset( circleTranslate );
980 if ( circleFillColor.isValid() )
984 if ( circleDiameter != -1 )
986 markerSymbolLayer->
setSize( circleDiameter );
989 if ( circleStrokeColor.isValid() )
993 if ( circleStrokeWidth != -1 )
1006 hasLabeling =
false;
1007 hasRenderer =
false;
1009 if ( !jsonLayer.contains( QStringLiteral(
"layout" ) ) )
1011 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
1014 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
1015 if ( !jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
1021 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
1027 if ( jsonLayout.contains( QStringLiteral(
"text-size" ) ) )
1029 const QVariant jsonTextSize = jsonLayout.
value( QStringLiteral(
"text-size" ) );
1030 switch ( jsonTextSize.type() )
1033 case QVariant::LongLong:
1034 case QVariant::Double:
1044 case QVariant::List:
1045 case QVariant::StringList:
1055 if ( textSizeProperty )
1062 constexpr
double EM_TO_CHARS = 2.0;
1064 double textMaxWidth = -1;
1065 if ( jsonLayout.contains( QStringLiteral(
"text-max-width" ) ) )
1067 const QVariant jsonTextMaxWidth = jsonLayout.value( QStringLiteral(
"text-max-width" ) );
1068 switch ( jsonTextMaxWidth.type() )
1071 case QVariant::LongLong:
1072 case QVariant::Double:
1073 textMaxWidth = jsonTextMaxWidth.toDouble() * EM_TO_CHARS;
1080 case QVariant::List:
1081 case QVariant::StringList:
1093 textMaxWidth = 10 * EM_TO_CHARS;
1096 double textLetterSpacing = -1;
1097 if ( jsonLayout.contains( QStringLiteral(
"text-letter-spacing" ) ) )
1099 const QVariant jsonTextLetterSpacing = jsonLayout.value( QStringLiteral(
"text-letter-spacing" ) );
1100 switch ( jsonTextLetterSpacing.type() )
1103 case QVariant::LongLong:
1104 case QVariant::Double:
1105 textLetterSpacing = jsonTextLetterSpacing.toDouble();
1112 case QVariant::List:
1113 case QVariant::StringList:
1124 bool foundFont =
false;
1126 QString fontStyleName;
1128 if ( jsonLayout.contains( QStringLiteral(
"text-font" ) ) )
1130 auto splitFontFamily = [](
const QString & fontName, QString & family, QString & style ) ->
bool
1132 const QStringList textFontParts = fontName.split(
' ' );
1133 for (
int i = 1; i < textFontParts.size(); ++i )
1135 const QString candidateFontName = textFontParts.mid( 0, i ).join(
' ' );
1136 const QString candidateFontStyle = textFontParts.mid( i ).join(
' ' );
1139 family = candidateFontName;
1140 style = candidateFontStyle;
1145 if ( QFontDatabase().hasFamily( fontName ) )
1155 const QVariant jsonTextFont = jsonLayout.value( QStringLiteral(
"text-font" ) );
1156 if ( jsonTextFont.type() != QVariant::List && jsonTextFont.type() != QVariant::StringList && jsonTextFont.type() != QVariant::String
1157 && jsonTextFont.type() != QVariant::Map )
1163 switch ( jsonTextFont.type() )
1165 case QVariant::List:
1166 case QVariant::StringList:
1167 fontName = jsonTextFont.toList().value( 0 ).toString();
1170 case QVariant::String:
1171 fontName = jsonTextFont.toString();
1176 QString familyCaseString = QStringLiteral(
"CASE " );
1177 QString styleCaseString = QStringLiteral(
"CASE " );
1179 const QVariantList stops = jsonTextFont.toMap().value( QStringLiteral(
"stops" ) ).toList();
1182 for (
int i = 0; i < stops.length() - 1; ++i )
1185 const QVariant bz = stops.value( i ).toList().value( 0 );
1186 const QString bv = stops.value( i ).toList().value( 1 ).type() == QVariant::String ? stops.value( i ).toList().value( 1 ).toString() : stops.value( i ).toList().value( 1 ).toList().value( 0 ).toString();
1187 if ( bz.type() == QVariant::List || bz.type() == QVariant::StringList )
1189 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1195 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
1196 if ( tz.type() == QVariant::List || tz.type() == QVariant::StringList )
1198 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1203 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1205 familyCaseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1206 "THEN %3 " ).arg( bz.toString(),
1209 styleCaseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1210 "THEN %3 " ).arg( bz.toString(),
1216 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1222 const QString bv = stops.constLast().toList().value( 1 ).type() == QVariant::String ? stops.constLast().toList().value( 1 ).toString() : stops.constLast().toList().value( 1 ).toList().value( 0 ).toString();
1223 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1230 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1237 fontName = fontFamily;
1247 if ( splitFontFamily( fontName, fontFamily, fontStyleName ) )
1249 textFont = QFont( fontFamily );
1250 if ( !fontStyleName.isEmpty() )
1251 textFont.setStyleName( fontStyleName );
1261 fontName = QStringLiteral(
"Open Sans" );
1262 textFont = QFont( fontName );
1263 textFont.setStyleName( QStringLiteral(
"Regular" ) );
1264 fontStyleName = QStringLiteral(
"Regular" );
1269 fontName = QStringLiteral(
"Arial Unicode MS" );
1270 textFont = QFont( fontName );
1271 textFont.setStyleName( QStringLiteral(
"Regular" ) );
1272 fontStyleName = QStringLiteral(
"Regular" );
1277 fontName = QStringLiteral(
"Open Sans, Arial Unicode MS" );
1280 if ( !foundFont && !fontName.isEmpty() )
1282 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), fontName ) );
1287 if ( jsonPaint.contains( QStringLiteral(
"text-color" ) ) )
1289 const QVariant jsonTextColor = jsonPaint.value( QStringLiteral(
"text-color" ) );
1290 switch ( jsonTextColor.type() )
1296 case QVariant::List:
1297 case QVariant::StringList:
1301 case QVariant::String:
1302 textColor =
parseColor( jsonTextColor.toString(), context );
1313 textColor = QColor( 0, 0, 0 );
1318 if ( jsonPaint.contains( QStringLiteral(
"text-halo-color" ) ) )
1320 const QVariant jsonBufferColor = jsonPaint.value( QStringLiteral(
"text-halo-color" ) );
1321 switch ( jsonBufferColor.type() )
1327 case QVariant::List:
1328 case QVariant::StringList:
1332 case QVariant::String:
1333 bufferColor =
parseColor( jsonBufferColor.toString(), context );
1342 double bufferSize = 0.0;
1346 constexpr
double BUFFER_SIZE_SCALE = 2.0;
1347 if ( jsonPaint.contains( QStringLiteral(
"text-halo-width" ) ) )
1349 const QVariant jsonHaloWidth = jsonPaint.value( QStringLiteral(
"text-halo-width" ) );
1350 switch ( jsonHaloWidth.type() )
1353 case QVariant::LongLong:
1354 case QVariant::Double:
1363 case QVariant::List:
1364 case QVariant::StringList:
1375 double haloBlurSize = 0;
1376 if ( jsonPaint.contains( QStringLiteral(
"text-halo-blur" ) ) )
1378 const QVariant jsonTextHaloBlur = jsonPaint.value( QStringLiteral(
"text-halo-blur" ) );
1379 switch ( jsonTextHaloBlur.type() )
1382 case QVariant::LongLong:
1383 case QVariant::Double:
1397 if ( textColor.isValid() )
1399 if ( textSize >= 0 )
1404 if ( !fontStyleName.isEmpty() )
1407 if ( textLetterSpacing > 0 )
1409 QFont f = format.
font();
1410 f.setLetterSpacing( QFont::AbsoluteSpacing, textLetterSpacing );
1414 if ( bufferSize > 0 )
1421 if ( haloBlurSize > 0 )
1437 if ( textMaxWidth > 0 )
1444 auto processLabelField = [](
const QString & string,
bool & isExpression )->QString
1448 const QRegularExpression singleFieldRx( QStringLiteral(
"^{([^}]+)}$" ) );
1449 const QRegularExpressionMatch match = singleFieldRx.match(
string );
1450 if ( match.hasMatch() )
1452 isExpression =
false;
1453 return match.captured( 1 );
1456 const QRegularExpression multiFieldRx( QStringLiteral(
"(?={[^}]+})" ) );
1457 const QStringList parts =
string.split( multiFieldRx );
1458 if ( parts.size() > 1 )
1460 isExpression =
true;
1463 for (
const QString &part : parts )
1465 if ( part.isEmpty() )
1468 if ( !part.contains(
'{' ) )
1475 const QStringList split = part.split(
'}' );
1477 if ( !split.at( 1 ).isEmpty() )
1480 return QStringLiteral(
"concat(%1)" ).arg( res.join(
',' ) );
1484 isExpression =
false;
1489 if ( jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
1491 const QVariant jsonTextField = jsonLayout.value( QStringLiteral(
"text-field" ) );
1492 switch ( jsonTextField.type() )
1494 case QVariant::String:
1496 labelSettings.
fieldName = processLabelField( jsonTextField.toString(), labelSettings.
isExpression );
1500 case QVariant::List:
1501 case QVariant::StringList:
1503 const QVariantList textFieldList = jsonTextField.toList();
1511 if ( textFieldList.size() > 2 && textFieldList.at( 0 ).toString() == QLatin1String(
"format" ) )
1514 for (
int i = 1; i < textFieldList.size(); ++i )
1516 bool isExpression =
false;
1517 const QString part = processLabelField( textFieldList.at( i ).toString(), isExpression );
1518 if ( !isExpression )
1525 labelSettings.
fieldName = QStringLiteral(
"concat(%1)" ).arg( parts.join(
',' ) );
1546 if ( jsonLayout.contains( QStringLiteral(
"text-transform" ) ) )
1548 const QString textTransform = jsonLayout.value( QStringLiteral(
"text-transform" ) ).toString();
1549 if ( textTransform == QLatin1String(
"uppercase" ) )
1553 else if ( textTransform == QLatin1String(
"lowercase" ) )
1562 if ( jsonLayout.contains( QStringLiteral(
"symbol-placement" ) ) )
1564 const QString symbolPlacement = jsonLayout.value( QStringLiteral(
"symbol-placement" ) ).toString();
1565 if ( symbolPlacement == QLatin1String(
"line" ) )
1571 if ( jsonLayout.contains( QStringLiteral(
"text-rotation-alignment" ) ) )
1573 const QString textRotationAlignment = jsonLayout.value( QStringLiteral(
"text-rotation-alignment" ) ).toString();
1574 if ( textRotationAlignment == QLatin1String(
"viewport" ) )
1584 if ( jsonLayout.contains( QStringLiteral(
"text-offset" ) ) )
1586 const QVariant jsonTextOffset = jsonLayout.
value( QStringLiteral(
"text-offset" ) );
1589 switch ( jsonTextOffset.type() )
1592 textOffsetProperty =
parseInterpolatePointByZoom( jsonTextOffset.toMap(), context, !textSizeProperty ? textSize : 1.0, &textOffset );
1593 if ( !textSizeProperty )
1604 case QVariant::List:
1605 case QVariant::StringList:
1606 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1607 jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1615 if ( !textOffset.isNull() )
1618 labelSettings.
dist = std::abs( textOffset.y() ) - textSize;
1620 if ( textSizeProperty && !textOffsetProperty )
1627 if ( textOffset.isNull() )
1635 if ( jsonLayout.contains( QStringLiteral(
"text-justify" ) ) )
1637 const QVariant jsonTextJustify = jsonLayout.value( QStringLiteral(
"text-justify" ) );
1640 QString textAlign = QStringLiteral(
"center" );
1642 const QVariantMap conversionMap
1644 { QStringLiteral(
"left" ), QStringLiteral(
"left" ) },
1645 { QStringLiteral(
"center" ), QStringLiteral(
"center" ) },
1646 { QStringLiteral(
"right" ), QStringLiteral(
"right" ) },
1647 { QStringLiteral(
"auto" ), QStringLiteral(
"follow" ) }
1650 switch ( jsonTextJustify.type() )
1652 case QVariant::String:
1653 textAlign = jsonTextJustify.toString();
1656 case QVariant::List:
1669 if ( textAlign == QLatin1String(
"left" ) )
1670 labelSettings.
multilineAlign = Qgis::LabelMultiLineAlignment::Left;
1671 else if ( textAlign == QLatin1String(
"right" ) )
1672 labelSettings.
multilineAlign = Qgis::LabelMultiLineAlignment::Right;
1673 else if ( textAlign == QLatin1String(
"center" ) )
1674 labelSettings.
multilineAlign = Qgis::LabelMultiLineAlignment::Center;
1675 else if ( textAlign == QLatin1String(
"follow" ) )
1676 labelSettings.
multilineAlign = Qgis::LabelMultiLineAlignment::FollowPlacement;
1680 labelSettings.
multilineAlign = Qgis::LabelMultiLineAlignment::Center;
1685 if ( jsonLayout.contains( QStringLiteral(
"text-anchor" ) ) )
1687 const QVariant jsonTextAnchor = jsonLayout.value( QStringLiteral(
"text-anchor" ) );
1690 const QVariantMap conversionMap
1692 { QStringLiteral(
"center" ), 4 },
1693 { QStringLiteral(
"left" ), 5 },
1694 { QStringLiteral(
"right" ), 3 },
1695 { QStringLiteral(
"top" ), 7 },
1696 { QStringLiteral(
"bottom" ), 1 },
1697 { QStringLiteral(
"top-left" ), 8 },
1698 { QStringLiteral(
"top-right" ), 6 },
1699 { QStringLiteral(
"bottom-left" ), 2 },
1700 { QStringLiteral(
"bottom-right" ), 0 },
1703 switch ( jsonTextAnchor.type() )
1705 case QVariant::String:
1706 textAnchor = jsonTextAnchor.toString();
1709 case QVariant::List:
1722 if ( textAnchor == QLatin1String(
"center" ) )
1723 labelSettings.
quadOffset = Qgis::LabelQuadrantPosition::Over;
1724 else if ( textAnchor == QLatin1String(
"left" ) )
1725 labelSettings.
quadOffset = Qgis::LabelQuadrantPosition::Right;
1726 else if ( textAnchor == QLatin1String(
"right" ) )
1727 labelSettings.
quadOffset = Qgis::LabelQuadrantPosition::Left;
1728 else if ( textAnchor == QLatin1String(
"top" ) )
1729 labelSettings.
quadOffset = Qgis::LabelQuadrantPosition::Below;
1730 else if ( textAnchor == QLatin1String(
"bottom" ) )
1731 labelSettings.
quadOffset = Qgis::LabelQuadrantPosition::Above;
1732 else if ( textAnchor == QLatin1String(
"top-left" ) )
1733 labelSettings.
quadOffset = Qgis::LabelQuadrantPosition::BelowRight;
1734 else if ( textAnchor == QLatin1String(
"top-right" ) )
1735 labelSettings.
quadOffset = Qgis::LabelQuadrantPosition::BelowLeft;
1736 else if ( textAnchor == QLatin1String(
"bottom-left" ) )
1737 labelSettings.
quadOffset = Qgis::LabelQuadrantPosition::AboveRight;
1738 else if ( textAnchor == QLatin1String(
"bottom-right" ) )
1739 labelSettings.
quadOffset = Qgis::LabelQuadrantPosition::AboveLeft;
1743 if ( jsonLayout.contains( QStringLiteral(
"text-offset" ) ) )
1745 const QVariant jsonTextOffset = jsonLayout.value( QStringLiteral(
"text-offset" ) );
1748 switch ( jsonTextOffset.type() )
1754 case QVariant::List:
1755 case QVariant::StringList:
1756 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1757 jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1765 if ( !textOffset.isNull() )
1768 labelSettings.
xOffset = textOffset.x();
1769 labelSettings.
yOffset = textOffset.y();
1774 if ( jsonLayout.contains( QStringLiteral(
"icon-image" ) ) &&
1778 QString spriteProperty, spriteSizeProperty;
1779 const QString sprite =
retrieveSpriteAsBase64( jsonLayout.value( QStringLiteral(
"icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
1780 if ( !sprite.isEmpty() )
1783 markerLayer->
setPath( sprite );
1784 markerLayer->
setSize( spriteSize.width() );
1787 if ( !spriteProperty.isEmpty() )
1799 backgroundSettings.
setSize( spriteSize );
1807 if ( textSize >= 0 )
1830 if ( !jsonLayer.contains( QStringLiteral(
"layout" ) ) )
1832 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
1835 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
1837 if ( jsonLayout.value( QStringLiteral(
"symbol-placement" ) ).toString() == QLatin1String(
"line" ) && !jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
1841 double spacing = -1.0;
1842 if ( jsonLayout.contains( QStringLiteral(
"symbol-spacing" ) ) )
1844 const QVariant jsonSpacing = jsonLayout.
value( QStringLiteral(
"symbol-spacing" ) );
1845 switch ( jsonSpacing.type() )
1848 case QVariant::LongLong:
1849 case QVariant::Double:
1857 case QVariant::List:
1858 case QVariant::StringList:
1873 bool rotateMarkers =
true;
1874 if ( jsonLayout.contains( QStringLiteral(
"icon-rotation-alignment" ) ) )
1876 const QString alignment = jsonLayout.value( QStringLiteral(
"icon-rotation-alignment" ) ).toString();
1877 if ( alignment == QLatin1String(
"map" ) || alignment == QLatin1String(
"auto" ) )
1879 rotateMarkers =
true;
1881 else if ( alignment == QLatin1String(
"viewport" ) )
1883 rotateMarkers =
false;
1888 double rotation = 0.0;
1889 if ( jsonLayout.contains( QStringLiteral(
"icon-rotate" ) ) )
1891 const QVariant jsonIconRotate = jsonLayout.
value( QStringLiteral(
"icon-rotate" ) );
1892 switch ( jsonIconRotate.type() )
1895 case QVariant::LongLong:
1896 case QVariant::Double:
1897 rotation = jsonIconRotate.toDouble();
1904 case QVariant::List:
1905 case QVariant::StringList:
1926 QString spriteProperty, spriteSizeProperty;
1927 const QString sprite =
retrieveSpriteAsBase64( jsonLayout.value( QStringLiteral(
"icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
1928 if ( !sprite.isNull() )
1930 markerLayer->
setPath( sprite );
1931 markerLayer->
setSize( spriteSize.width() );
1934 if ( !spriteProperty.isEmpty() )
1941 if ( jsonLayout.contains( QStringLiteral(
"icon-size" ) ) )
1943 const QVariant jsonIconSize = jsonLayout.value( QStringLiteral(
"icon-size" ) );
1946 switch ( jsonIconSize.type() )
1949 case QVariant::LongLong:
1950 case QVariant::Double:
1952 size = jsonIconSize.toDouble();
1953 if ( !spriteSizeProperty.isEmpty() )
1956 QgsProperty::fromExpression( QStringLiteral(
"with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
1965 case QVariant::List:
1966 case QVariant::StringList:
1971 markerLayer->
setSize( size * spriteSize.width() );
1974 if ( !spriteSizeProperty.isEmpty() )
1991 std::unique_ptr< QgsSymbol > symbol = std::make_unique< QgsLineSymbol >(
QgsSymbolLayerList() << lineSymbol );
1994 symbol->setOutputUnit( context.
targetUnit() );
1998 rendererStyle.
setSymbol( symbol.release() );
2001 else if ( jsonLayout.contains( QStringLiteral(
"icon-image" ) ) )
2003 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
2006 QString spriteProperty, spriteSizeProperty;
2007 const QString sprite =
retrieveSpriteAsBase64( jsonLayout.value( QStringLiteral(
"icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
2008 if ( !sprite.isEmpty() )
2011 rasterMarker->
setPath( sprite );
2012 rasterMarker->
setSize( spriteSize.width() );
2016 if ( !spriteProperty.isEmpty() )
2022 if ( jsonLayout.contains( QStringLiteral(
"icon-size" ) ) )
2024 const QVariant jsonIconSize = jsonLayout.value( QStringLiteral(
"icon-size" ) );
2027 switch ( jsonIconSize.type() )
2030 case QVariant::LongLong:
2031 case QVariant::Double:
2033 size = jsonIconSize.toDouble();
2034 if ( !spriteSizeProperty.isEmpty() )
2037 QgsProperty::fromExpression( QStringLiteral(
"with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
2046 case QVariant::List:
2047 case QVariant::StringList:
2052 rasterMarker->
setSize( size * spriteSize.width() );
2055 if ( !spriteSizeProperty.isEmpty() )
2068 double rotation = 0.0;
2069 if ( jsonLayout.contains( QStringLiteral(
"icon-rotate" ) ) )
2071 const QVariant jsonIconRotate = jsonLayout.value( QStringLiteral(
"icon-rotate" ) );
2072 switch ( jsonIconRotate.type() )
2075 case QVariant::LongLong:
2076 case QVariant::Double:
2077 rotation = jsonIconRotate.toDouble();
2084 case QVariant::List:
2085 case QVariant::StringList:
2095 double iconOpacity = -1.0;
2096 if ( jsonPaint.contains( QStringLiteral(
"icon-opacity" ) ) )
2098 const QVariant jsonIconOpacity = jsonPaint.value( QStringLiteral(
"icon-opacity" ) );
2099 switch ( jsonIconOpacity.type() )
2102 case QVariant::LongLong:
2103 case QVariant::Double:
2104 iconOpacity = jsonIconOpacity.toDouble();
2111 case QVariant::List:
2112 case QVariant::StringList:
2123 rasterMarker->
setAngle( rotation );
2124 if ( iconOpacity >= 0 )
2128 rendererStyle.
setSymbol( markerSymbol );
2139 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2140 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2141 if ( stops.empty() )
2144 QString caseString = QStringLiteral(
"CASE " );
2145 const QString colorComponent(
"color_part(%1,'%2')" );
2147 for (
int i = 0; i < stops.length() - 1; ++i )
2150 const QString bz = stops.at( i ).toList().value( 0 ).toString();
2152 const QString tz = stops.at( i + 1 ).toList().value( 0 ).toString();
2154 const QVariant bcVariant = stops.at( i ).toList().value( 1 );
2155 const QVariant tcVariant = stops.at( i + 1 ).toList().value( 1 );
2157 const QColor bottomColor =
parseColor( bcVariant.toString(), context );
2158 const QColor topColor =
parseColor( tcVariant.toString(), context );
2160 if ( i == 0 && bottomColor.isValid() )
2167 caseString += QStringLiteral(
"WHEN @vector_tile_zoom < %1 THEN color_hsla(%2, %3, %4, %5) " )
2168 .arg( bz ).arg( bcHue ).arg( bcSat ).arg( bcLight ).arg( bcAlpha );
2171 if ( bottomColor.isValid() && topColor.isValid() )
2183 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2184 "%3, %4, %5, %6) " ).arg( bz, tz,
2195 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2196 "%3, %4, %5, %6) " ).arg( bz, tz,
2197 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"hsl_hue" ), colorComponent.arg( topColorExpr ).arg(
"hsl_hue" ), base, 1, &context ),
2198 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"hsl_saturation" ), colorComponent.arg( topColorExpr ).arg(
"hsl_saturation" ), base, 1, &context ),
2199 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"lightness" ), colorComponent.arg( topColorExpr ).arg(
"lightness" ), base, 1, &context ),
2200 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"alpha" ), colorComponent.arg( topColorExpr ).arg(
"alpha" ), base, 1, &context ) );
2205 const QString tz = stops.last().toList().value( 0 ).toString();
2206 const QVariant tcVariant = stops.last().toList().value( 1 );
2207 const QColor topColor =
parseColor( stops.last().toList().value( 1 ), context );
2208 if ( topColor.isValid() )
2215 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2216 "ELSE color_hsla(%2, %3, %4, %5) END" ).arg( tz ).arg( tcHue ).arg( tcSat ).arg( tcLight ).arg( tcAlpha );
2222 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2223 "ELSE color_hsla(%2, %3, %4, %5) END" ).arg( tz )
2224 .arg( colorComponent.arg( topColorExpr ).arg(
"hsl_hue" ) ).arg( colorComponent.arg( topColorExpr ).arg(
"hsl_saturation" ) ).arg( colorComponent.arg( topColorExpr ).arg(
"lightness" ) ).arg( colorComponent.arg( topColorExpr ).arg(
"alpha" ) );
2227 if ( !stops.empty() && defaultColor )
2228 *defaultColor =
parseColor( stops.value( 0 ).toList().value( 1 ).toString(), context );
2235 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2236 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2237 if ( stops.empty() )
2240 QString scaleExpression;
2241 if ( stops.size() <= 2 )
2244 stops.last().toList().value( 0 ).toDouble(),
2245 stops.value( 0 ).toList().value( 1 ),
2246 stops.last().toList().value( 1 ), base, multiplier, &context );
2250 scaleExpression =
parseStops( base, stops, multiplier, context );
2253 if ( !stops.empty() && defaultNumber )
2254 *defaultNumber = stops.value( 0 ).toList().value( 1 ).toDouble() * multiplier;
2264 context = *contextPtr;
2266 const double base = json.value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2267 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2268 if ( stops.empty() )
2271 QString scaleExpression;
2272 if ( stops.length() <= 2 )
2274 const QVariant bv = stops.value( 0 ).toList().value( 1 );
2275 const QVariant tv = stops.last().toList().value( 1 );
2276 double bottom = 0.0;
2278 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2279 scaleExpression = QStringLiteral(
"set_color_part(@symbol_color, 'alpha', %1)" )
2281 stops.last().toList().value( 0 ).toDouble(),
2282 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2283 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ), base, 1, &context ) );
2294 QString caseString = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN set_color_part(@symbol_color, 'alpha', %2)" )
2295 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
2296 .arg( stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity );
2298 for (
int i = 0; i < stops.size() - 1; ++i )
2300 const QVariant bv = stops.value( i ).toList().value( 1 );
2301 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2302 double bottom = 0.0;
2304 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2306 caseString += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
2307 "THEN set_color_part(@symbol_color, 'alpha', %3)" )
2308 .arg( stops.value( i ).toList().value( 0 ).toString(),
2309 stops.value( i + 1 ).toList().value( 0 ).toString(),
2311 stops.value( i + 1 ).toList().value( 0 ).toDouble(),
2312 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2313 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ),
2314 base, 1, &context ) );
2317 caseString += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
2318 "THEN set_color_part(@symbol_color, 'alpha', %2) END" )
2319 .arg( stops.last().toList().value( 0 ).toString() )
2320 .arg( stops.last().toList().value( 1 ).toDouble() * maxOpacity );
2326 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2327 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2328 if ( stops.empty() )
2331 QString scaleExpression;
2332 if ( stops.size() <= 2 )
2334 scaleExpression = QStringLiteral(
"array(%1,%2)" ).arg(
interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(),
2335 stops.last().toList().value( 0 ).toDouble(),
2336 stops.value( 0 ).toList().value( 1 ).toList().value( 0 ),
2337 stops.last().toList().value( 1 ).toList().value( 0 ), base, multiplier, &context ),
2339 stops.last().toList().value( 0 ).toDouble(),
2340 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ),
2341 stops.last().toList().value( 1 ).toList().value( 1 ), base, multiplier, &context )
2346 scaleExpression =
parsePointStops( base, stops, context, multiplier );
2349 if ( !stops.empty() && defaultPoint )
2350 *defaultPoint = QPointF( stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble() * multiplier,
2351 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble() * multiplier );
2357 const QVariantMap &conversionMap, QString *defaultString )
2359 const QVariantList stops = json.
value( QStringLiteral(
"stops" ) ).toList();
2360 if ( stops.empty() )
2363 const QString scaleExpression =
parseStringStops( stops, context, conversionMap, defaultString );
2370 QString caseString = QStringLiteral(
"CASE " );
2372 for (
int i = 0; i < stops.length() - 1; ++i )
2375 const QVariant bz = stops.value( i ).toList().value( 0 );
2376 const QVariant bv = stops.value( i ).toList().value( 1 );
2377 if ( bv.type() != QVariant::List && bv.type() != QVariant::StringList )
2384 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2385 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2386 if ( tv.type() != QVariant::List && tv.type() != QVariant::StringList )
2392 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2393 "THEN array(%3,%4)" ).arg( bz.toString(),
2395 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 0 ), tv.toList().value( 0 ), base, multiplier, &context ),
2396 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 1 ), tv.toList().value( 1 ), base, multiplier, &context ) );
2398 caseString += QLatin1String(
"END" );
2404 if ( stops.length() < 2 )
2407 QString caseString = QStringLiteral(
"CASE " );
2409 for (
int i = 0; i < stops.length() - 1; ++i )
2412 const QVariant bz = stops.value( i ).toList().value( 0 );
2413 const QList<QVariant> bv = stops.value( i ).toList().value( 1 ).toList();
2416 for (
const QVariant &value : bv )
2418 const double number = value.toDouble( &ok );
2420 bl << QString::number( number * multiplier );
2424 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2425 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2426 "THEN array(%3) " ).arg( bz.toString(),
2430 const QVariant lz = stops.value( stops.length() - 1 ).toList().value( 0 );
2431 const QList<QVariant> lv = stops.value( stops.length() - 1 ).toList().value( 1 ).toList();
2434 for (
const QVariant &value : lv )
2436 const double number = value.toDouble( &ok );
2438 ll << QString::number( number * multiplier );
2440 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 "
2441 "THEN array(%2) " ).arg( lz.toString(),
2443 caseString += QLatin1String(
"END" );
2449 QString caseString = QStringLiteral(
"CASE " );
2451 for (
int i = 0; i < stops.length() - 1; ++i )
2454 const QVariant bz = stops.value( i ).toList().value( 0 );
2455 const QVariant bv = stops.value( i ).toList().value( 1 );
2456 if ( bz.type() == QVariant::List || bz.type() == QVariant::StringList )
2458 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2463 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2464 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2465 if ( tz.type() == QVariant::List || tz.type() == QVariant::StringList )
2467 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2471 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2472 "THEN %3 " ).arg( bz.toString(),
2477 const QVariant z = stops.last().toList().value( 0 );
2478 const QVariant v = stops.last().toList().value( 1 );
2479 QString vStr = v.toString();
2480 if ( ( QMetaType::Type )v.type() == QMetaType::QVariantList )
2483 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 "
2484 "THEN ( ( %2 ) * %3 ) END" ).arg( z.toString() ).arg( vStr ).arg( multiplier );
2488 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 "
2489 "THEN %2 END" ).arg( z.toString() ).arg( v.toDouble() * multiplier );
2497 QString caseString = QStringLiteral(
"CASE " );
2499 for (
int i = 0; i < stops.length() - 1; ++i )
2502 const QVariant bz = stops.value( i ).toList().value( 0 );
2503 const QString bv = stops.value( i ).toList().value( 1 ).toString();
2504 if ( bz.type() == QVariant::List || bz.type() == QVariant::StringList )
2506 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2511 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2512 if ( tz.type() == QVariant::List || tz.type() == QVariant::StringList )
2514 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2518 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2519 "THEN %3 " ).arg( bz.toString(),
2523 caseString += QStringLiteral(
"ELSE %1 END" ).arg(
QgsExpression::quotedValue( conversionMap.value( stops.constLast().toList().value( 1 ).toString(),
2524 stops.constLast().toList().value( 1 ) ) ) );
2525 if ( defaultString )
2526 *defaultString = stops.constLast().toList().value( 1 ).toString();
2532 const QString method = json.
value( 0 ).toString();
2533 if ( method == QLatin1String(
"interpolate" ) )
2537 else if ( method == QLatin1String(
"match" ) )
2539 return parseMatchList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
2549 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
2550 if ( attribute.isEmpty() )
2552 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
2556 QString caseString = QStringLiteral(
"CASE " );
2558 for (
int i = 2; i < json.length() - 1; i += 2 )
2560 const QVariantList keys = json.value( i ).toList();
2562 QStringList matchString;
2563 for (
const QVariant &key : keys )
2568 const QVariant value = json.value( i + 1 );
2570 QString valueString;
2575 const QColor color =
parseColor( value, context );
2582 const double v = value.toDouble() * multiplier;
2583 valueString = QString::number( v );
2589 const double v = value.toDouble() * maxOpacity;
2590 valueString = QString::number( v );
2596 valueString = QStringLiteral(
"array(%1,%2)" ).arg( value.toList().value( 0 ).toDouble() * multiplier,
2597 value.toList().value( 0 ).toDouble() * multiplier );
2603 caseString += QStringLiteral(
"WHEN %1 IN (%2) THEN %3 " ).arg( attribute,
2604 matchString.join(
',' ), valueString );
2613 const QColor color =
parseColor( json.constLast(), context );
2615 *defaultColor = color;
2623 const double v = json.constLast().toDouble() * multiplier;
2624 if ( defaultNumber )
2626 elseValue = QString::number( v );
2632 const double v = json.constLast().toDouble() * maxOpacity;
2633 if ( defaultNumber )
2635 elseValue = QString::number( v );
2641 elseValue = QStringLiteral(
"array(%1,%2)" ).arg( json.constLast().toList().value( 0 ).toDouble() * multiplier,
2642 json.constLast().toList().value( 0 ).toDouble() * multiplier );
2648 caseString += QStringLiteral(
"ELSE %1 END" ).arg( elseValue );
2654 if ( json.value( 0 ).toString() != QLatin1String(
"interpolate" ) )
2656 context.
pushWarning( QObject::tr(
"%1: Could not interpret value list" ).arg( context.
layerId() ) );
2661 const QString technique = json.value( 1 ).toList().value( 0 ).toString();
2662 if ( technique == QLatin1String(
"linear" ) )
2664 else if ( technique == QLatin1String(
"exponential" ) )
2665 base = json.value( 1 ).toList(). value( 1 ).toDouble();
2666 else if ( technique == QLatin1String(
"cubic-bezier" ) )
2668 context.
pushWarning( QObject::tr(
"%1: Cubic-bezier interpolation is not supported, linear used instead." ).arg( context.
layerId() ) );
2673 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation method %2" ).arg( context.
layerId(), technique ) );
2677 if ( json.value( 2 ).toList().value( 0 ).toString() != QLatin1String(
"zoom" ) )
2679 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation input %2" ).arg( context.
layerId(), json.value( 2 ).toString() ) );
2685 for (
int i = 3; i < json.length(); i += 2 )
2687 stops.push_back( QVariantList() << json.value( i ).toString() << json.value( i + 1 ) );
2691 props.insert( QStringLiteral(
"stops" ), stops );
2692 props.insert( QStringLiteral(
"base" ), base );
2695 case PropertyType::Color:
2698 case PropertyType::Numeric:
2701 case PropertyType::Opacity:
2704 case PropertyType::Point:
2712 if ( ( QMetaType::Type )colorExpression.type() == QMetaType::QVariantList )
2716 return parseValue( colorExpression, context,
true );
2721 if ( color.type() != QVariant::String )
2723 context.
pushWarning( QObject::tr(
"%1: Could not parse non-string color %2, skipping" ).arg( context.
layerId(), color.toString() ) );
2732 hue = std::max( 0, color.hslHue() );
2733 saturation = color.hslSaturation() / 255.0 * 100;
2734 lightness = color.lightness() / 255.0 * 100;
2735 alpha = color.alpha();
2743 context = *contextPtr;
2747 if ( valueMin.canConvert( QMetaType::Double ) && valueMax.canConvert( QMetaType::Double ) )
2749 bool minDoubleOk =
true;
2750 const double min = valueMin.toDouble( &minDoubleOk );
2751 bool maxDoubleOk =
true;
2752 const double max = valueMax.toDouble( &maxDoubleOk );
2753 if ( minDoubleOk && maxDoubleOk &&
qgsDoubleNear( min, max ) )
2755 return QString::number( min * multiplier );
2759 QString minValueExpr = valueMin.toString();
2760 QString maxValueExpr = valueMax.toString();
2761 if ( ( QMetaType::Type )valueMin.type() == QMetaType::QVariantList )
2765 if ( ( QMetaType::Type )valueMax.type() == QMetaType::QVariantList )
2770 if ( minValueExpr == maxValueExpr )
2772 return minValueExpr;
2778 expression = QStringLiteral(
"scale_linear(@vector_tile_zoom,%1,%2,%3,%4)" ).arg( zoomMin )
2780 .arg( minValueExpr )
2781 .arg( maxValueExpr );
2785 expression = QStringLiteral(
"scale_exp(@vector_tile_zoom,%1,%2,%3,%4,%5)" ).arg( zoomMin )
2787 .arg( minValueExpr )
2788 .arg( maxValueExpr )
2792 if ( multiplier != 1 )
2793 return QStringLiteral(
"%1 * %2" ).arg( expression ).arg( multiplier );
2800 if ( style == QLatin1String(
"round" ) )
2801 return Qt::RoundCap;
2802 else if ( style == QLatin1String(
"square" ) )
2803 return Qt::SquareCap;
2810 if ( style == QLatin1String(
"bevel" ) )
2811 return Qt::BevelJoin;
2812 else if ( style == QLatin1String(
"round" ) )
2813 return Qt::RoundJoin;
2815 return Qt::MiterJoin;
2820 QString op = expression.value( 0 ).toString();
2821 if ( op == QLatin1String(
"%" ) && expression.size() >= 3 )
2823 return QStringLiteral(
"%1 %2 %3" ).arg( parseValue( expression.value( 1 ), context ) ).arg( op ).arg( parseValue( expression.value( 2 ), context ) );
2825 else if ( op == QLatin1String(
"to-number" ) )
2827 return QStringLiteral(
"to_real(%1)" ).arg( parseValue( expression.value( 1 ), context ) );
2829 if ( op == QLatin1String(
"literal" ) )
2831 return expression.value( 1 ).toString();
2833 else if ( op == QLatin1String(
"all" )
2834 || op == QLatin1String(
"any" )
2835 || op == QLatin1String(
"none" ) )
2838 for (
int i = 1; i < expression.size(); ++i )
2840 const QString part = parseValue( expression.at( i ), context );
2841 if ( part.isEmpty() )
2843 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
2849 if ( op == QLatin1String(
"none" ) )
2850 return QStringLiteral(
"NOT (%1)" ).arg( parts.join( QLatin1String(
") AND NOT (" ) ) );
2852 QString operatorString;
2853 if ( op == QLatin1String(
"all" ) )
2854 operatorString = QStringLiteral(
") AND (" );
2855 else if ( op == QLatin1String(
"any" ) )
2856 operatorString = QStringLiteral(
") OR (" );
2858 return QStringLiteral(
"(%1)" ).arg( parts.join( operatorString ) );
2860 else if ( op ==
'!' )
2863 QVariantList contraJsonExpr = expression.value( 1 ).toList();
2864 contraJsonExpr[0] = QString( op + contraJsonExpr[0].toString() );
2866 return parseKey( contraJsonExpr, context );
2868 else if ( op == QLatin1String(
"==" )
2869 || op == QLatin1String(
"!=" )
2870 || op == QLatin1String(
">=" )
2872 || op == QLatin1String(
"<=" )
2876 if ( op == QLatin1String(
"==" ) )
2877 op = QStringLiteral(
"IS" );
2878 else if ( op == QLatin1String(
"!=" ) )
2879 op = QStringLiteral(
"IS NOT" );
2880 return QStringLiteral(
"%1 %2 %3" ).arg( parseKey( expression.value( 1 ), context ),
2881 op, parseValue( expression.value( 2 ), context ) );
2883 else if ( op == QLatin1String(
"has" ) )
2885 return parseKey( expression.value( 1 ), context ) + QStringLiteral(
" IS NOT NULL" );
2887 else if ( op == QLatin1String(
"!has" ) )
2889 return parseKey( expression.value( 1 ), context ) + QStringLiteral(
" IS NULL" );
2891 else if ( op == QLatin1String(
"in" ) || op == QLatin1String(
"!in" ) )
2893 const QString key = parseKey( expression.value( 1 ), context );
2895 for (
int i = 2; i < expression.size(); ++i )
2897 const QString part = parseValue( expression.at( i ), context );
2898 if ( part.isEmpty() )
2900 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
2905 if ( op == QLatin1String(
"in" ) )
2906 return QStringLiteral(
"%1 IN (%2)" ).arg( key, parts.join( QLatin1String(
", " ) ) );
2908 return QStringLiteral(
"(%1 IS NULL OR %1 NOT IN (%2))" ).arg( key, parts.join( QLatin1String(
", " ) ) );
2910 else if ( op == QLatin1String(
"get" ) )
2912 return parseKey( expression.value( 1 ), context );
2914 else if ( op == QLatin1String(
"match" ) )
2916 const QString attribute = expression.value( 1 ).toList().value( 1 ).toString();
2918 if ( expression.size() == 5
2919 && expression.at( 3 ).type() == QVariant::Bool && expression.at( 3 ).toBool() ==
true
2920 && expression.at( 4 ).type() == QVariant::Bool && expression.at( 4 ).toBool() ==
false )
2923 if ( expression.at( 2 ).type() == QVariant::List || expression.at( 2 ).type() == QVariant::StringList )
2926 for (
const QVariant &p : expression.at( 2 ).toList() )
2928 parts << parseValue( p, context );
2931 if ( parts.size() > 1 )
2936 else if ( expression.at( 2 ).type() == QVariant::String || expression.at( 2 ).type() == QVariant::Int
2937 || expression.at( 2 ).type() == QVariant::Double || expression.at( 2 ).type() == QVariant::LongLong )
2943 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
2949 QString caseString = QStringLiteral(
"CASE " );
2950 for (
int i = 2; i < expression.size() - 2; i += 2 )
2952 if ( expression.at( i ).type() == QVariant::List || expression.at( i ).type() == QVariant::StringList )
2955 for (
const QVariant &p : expression.at( i ).toList() )
2960 if ( parts.size() > 1 )
2965 else if ( expression.at( i ).type() == QVariant::String || expression.at( i ).type() == QVariant::Int
2966 || expression.at( i ).type() == QVariant::Double || expression.at( i ).type() == QVariant::LongLong )
2971 caseString += QStringLiteral(
"THEN %1 " ).arg( parseValue( expression.at( i + 1 ), context, colorExpected ) );
2973 caseString += QStringLiteral(
"ELSE %1 END" ).arg( parseValue( expression.last(), context, colorExpected ) );
2977 else if ( op == QLatin1String(
"to-string" ) )
2979 return QStringLiteral(
"to_string(%1)" ).arg(
parseExpression( expression.value( 1 ).toList(), context ) );
2983 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
2992 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
2996 const QVariantMap spriteDefinition = context.
spriteDefinitions().value( name ).toMap();
2997 if ( spriteDefinition.size() == 0 )
2999 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3003 const QImage sprite = context.
spriteImage().copy( spriteDefinition.value( QStringLiteral(
"x" ) ).toInt(),
3004 spriteDefinition.value( QStringLiteral(
"y" ) ).toInt(),
3005 spriteDefinition.value( QStringLiteral(
"width" ) ).toInt(),
3006 spriteDefinition.value( QStringLiteral(
"height" ) ).toInt() );
3007 if ( sprite.isNull() )
3009 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3013 spriteSize = sprite.size() / spriteDefinition.value( QStringLiteral(
"pixelRatio" ) ).toDouble() * context.
pixelSizeConversionFactor();
3021 auto prepareBase64 = [](
const QImage & sprite )
3024 if ( !sprite.isNull() )
3027 QBuffer buffer( &blob );
3028 buffer.open( QIODevice::WriteOnly );
3029 sprite.save( &buffer,
"PNG" );
3031 const QByteArray encoded = blob.toBase64();
3032 path = QString( encoded );
3033 path.prepend( QLatin1String(
"base64:" ) );
3038 switch ( value.type() )
3040 case QVariant::String:
3042 QString spriteName = value.toString();
3043 const QRegularExpression fieldNameMatch( QStringLiteral(
"{([^}]+)}" ) );
3044 QRegularExpressionMatch match = fieldNameMatch.match( spriteName );
3045 if ( match.hasMatch() )
3047 const QString fieldName = match.captured( 1 );
3048 spriteProperty = QStringLiteral(
"CASE" );
3049 spriteSizeProperty = QStringLiteral(
"CASE" );
3051 spriteName.replace(
"(", QLatin1String(
"\\(" ) );
3052 spriteName.replace(
")", QLatin1String(
"\\)" ) );
3053 spriteName.replace( fieldNameMatch, QStringLiteral(
"([^\\/\\\\]+)" ) );
3054 const QRegularExpression fieldValueMatch( spriteName );
3056 for (
const QString &name : spriteNames )
3058 match = fieldValueMatch.match( name );
3059 if ( match.hasMatch() )
3063 const QString fieldValue = match.captured( 1 );
3065 path = prepareBase64( sprite );
3066 if ( spritePath.isEmpty() && !path.isEmpty() )
3072 spriteProperty += QStringLiteral(
" WHEN \"%1\" = '%2' THEN '%3'" )
3073 .arg( fieldName, fieldValue, path );
3074 spriteSizeProperty += QStringLiteral(
" WHEN \"%1\" = '%2' THEN %3" )
3075 .arg( fieldName ).arg( fieldValue ).arg( size.width() );
3079 spriteProperty += QLatin1String(
" END" );
3080 spriteSizeProperty += QLatin1String(
" END" );
3084 spriteProperty.clear();
3085 spriteSizeProperty.clear();
3086 const QImage sprite =
retrieveSprite( spriteName, context, spriteSize );
3087 spritePath = prepareBase64( sprite );
3094 const QVariantList stops = value.toMap().value( QStringLiteral(
"stops" ) ).toList();
3095 if ( stops.size() == 0 )
3102 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, spriteSize );
3103 spritePath = prepareBase64( sprite );
3105 spriteProperty = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN '%2'" )
3106 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
3108 spriteSizeProperty = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN %2" )
3109 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
3110 .arg( spriteSize.width() );
3112 for (
int i = 0; i < stops.size() - 1; ++i )
3115 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, size );
3116 path = prepareBase64( sprite );
3118 spriteProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
3120 .arg( stops.value( i ).toList().value( 0 ).toString(),
3121 stops.value( i + 1 ).toList().value( 0 ).toString(),
3123 spriteSizeProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
3125 .arg( stops.value( i ).toList().value( 0 ).toString(),
3126 stops.value( i + 1 ).toList().value( 0 ).toString() )
3127 .arg( size.width() );
3129 sprite =
retrieveSprite( stops.last().toList().value( 1 ).toString(), context, size );
3130 path = prepareBase64( sprite );
3132 spriteProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
3134 .arg( stops.last().toList().value( 0 ).toString() )
3136 spriteSizeProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
3138 .arg( stops.last().toList().value( 0 ).toString() )
3139 .arg( size.width() );
3143 case QVariant::List:
3145 const QVariantList json = value.toList();
3146 const QString method = json.value( 0 ).toString();
3147 if ( method != QLatin1String(
"match" ) )
3149 context.
pushWarning( QObject::tr(
"%1: Could not interpret sprite value list with method %2" ).arg( context.
layerId(), method ) );
3153 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
3154 if ( attribute.isEmpty() )
3156 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
3160 spriteProperty = QStringLiteral(
"CASE " );
3161 spriteSizeProperty = QStringLiteral(
"CASE " );
3163 for (
int i = 2; i < json.length() - 1; i += 2 )
3165 const QVariantList keys = json.value( i ).toList();
3167 QStringList matchString;
3168 for (
const QVariant &key : keys )
3173 const QVariant value = json.value( i + 1 );
3175 const QImage sprite =
retrieveSprite( value.toString(), context, spriteSize );
3176 spritePath = prepareBase64( sprite );
3178 spriteProperty += QStringLiteral(
" WHEN %1 IN (%2) "
3179 "THEN '%3' " ).arg( attribute,
3180 matchString.join(
',' ),
3183 spriteSizeProperty += QStringLiteral(
" WHEN %1 IN (%2) "
3184 "THEN %3 " ).arg( attribute,
3185 matchString.join(
',' ) ).arg( spriteSize.width() );
3188 const QImage sprite =
retrieveSprite( json.constLast().toString(), context, spriteSize );
3189 spritePath = prepareBase64( sprite );
3191 spriteProperty += QStringLiteral(
"ELSE %1 END" ).arg( spritePath );
3192 spriteSizeProperty += QStringLiteral(
"ELSE %3 END" ).arg( spriteSize.width() );
3207 switch ( value.type() )
3209 case QVariant::List:
3210 case QVariant::StringList:
3213 case QVariant::Bool:
3214 case QVariant::String:
3215 if ( colorExpected )
3220 return parseValue(
c, context );
3226 case QVariant::LongLong:
3227 case QVariant::Double:
3228 return value.toString();
3230 case QVariant::Color:
3231 c = value.value<QColor>();
3232 return QString(
"color_rgba(%1,%2,%3,%4)" ).arg(
c.red() ).arg(
c.green() ).arg(
c.blue() ).arg(
c.alpha() );
3235 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression part" ).arg( context.
layerId() ) );
3243 if ( value.toString() == QLatin1String(
"$type" ) )
3245 return QStringLiteral(
"_geom_type" );
3247 if ( value.toString() == QLatin1String(
"level" ) )
3249 return QStringLiteral(
"level" );
3251 else if ( ( value.type() == QVariant::List && value.toList().size() == 1 ) || value.type() == QVariant::StringList )
3253 if ( value.toList().size() > 1 )
3254 return value.toList().at( 1 ).toString();
3257 QString valueString = value.toList().value( 0 ).toString();
3258 if ( valueString == QLatin1String(
"geometry-type" ) )
3260 return QStringLiteral(
"_geom_type" );
3265 else if ( value.type() == QVariant::List && value.toList().size() > 1 )
3274 return mRenderer ? mRenderer->clone() :
nullptr;
3279 return mLabeling ? mLabeling->clone() :
nullptr;
3282 bool QgsMapBoxGlStyleConverter::numericArgumentsOnly(
const QVariant &bottomVariant,
const QVariant &topVariant,
double &bottom,
double &top )
3284 if ( bottomVariant.canConvert( QMetaType::Double ) && topVariant.canConvert( QMetaType::Double ) )
3286 bool bDoubleOk, tDoubleOk;
3287 bottom = bottomVariant.toDouble( &bDoubleOk );
3288 top = topVariant.toDouble( &tDoubleOk );
3289 return ( bDoubleOk && tDoubleOk );
3300 mWarnings << warning;
3315 return mSizeConversionFactor;
3320 mSizeConversionFactor = sizeConversionFactor;
3325 return mSpriteImage;
3330 return mSpriteDefinitions;
3335 mSpriteImage = image;
3336 mSpriteDefinitions = definitions;