46 if ( style.contains( QStringLiteral(
"layers" ) ) )
48 parseLayers( style.value( QStringLiteral(
"layers" ) ).toList(), context );
52 mError = QObject::tr(
"Could not find layers list in JSON" );
67 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
70 tmpContext = qgis::make_unique< QgsMapBoxGlStyleConversionContext >();
71 context = tmpContext.get();
74 QList<QgsVectorTileBasicRendererStyle> rendererStyles;
75 QList<QgsVectorTileBasicLabelingStyle> labelingStyles;
77 for (
const QVariant &layer : layers )
79 const QVariantMap jsonLayer = layer.toMap();
81 const QString layerType = jsonLayer.value( QStringLiteral(
"type" ) ).toString();
82 if ( layerType == QLatin1String(
"background" ) )
85 const QString styleId = jsonLayer.value( QStringLiteral(
"id" ) ).toString();
87 const QString layerName = jsonLayer.value( QStringLiteral(
"source-layer" ) ).toString();
89 const int minZoom = jsonLayer.value( QStringLiteral(
"minzoom" ), QStringLiteral(
"-1" ) ).toInt();
90 const int maxZoom = jsonLayer.value( QStringLiteral(
"maxzoom" ), QStringLiteral(
"-1" ) ).toInt();
92 const bool enabled = jsonLayer.value( QStringLiteral(
"visibility" ) ).toString() != QLatin1String(
"none" );
94 QString filterExpression;
95 if ( jsonLayer.contains( QStringLiteral(
"filter" ) ) )
97 filterExpression =
parseExpression( jsonLayer.value( QStringLiteral(
"filter" ) ).toList(), *context );
103 bool hasRendererStyle =
false;
104 bool hasLabelingStyle =
false;
105 if ( layerType == QLatin1String(
"fill" ) )
107 hasRendererStyle =
parseFillLayer( jsonLayer, rendererStyle, *context );
109 else if ( layerType == QLatin1String(
"line" ) )
111 hasRendererStyle =
parseLineLayer( jsonLayer, rendererStyle, *context );
113 else if ( layerType == QLatin1String(
"symbol" ) )
115 parseSymbolLayer( jsonLayer, rendererStyle, hasRendererStyle, labelingStyle, hasLabelingStyle, *context );
119 mWarnings << QObject::tr(
"%1: Skipping unknown layer type %2" ).arg( context->
layerId(), layerType );
124 if ( hasRendererStyle )
132 rendererStyles.append( rendererStyle );
135 if ( hasLabelingStyle )
143 labelingStyles.append( labelingStyle );
146 mWarnings.append( context->
warnings() );
150 mRenderer = qgis::make_unique< QgsVectorTileBasicRenderer >();
152 renderer->setStyles( rendererStyles );
154 mLabeling = qgis::make_unique< QgsVectorTileBasicLabeling >();
156 labeling->setStyles( labelingStyles );
161 if ( !jsonLayer.contains( QStringLiteral(
"paint" ) ) )
163 context.
pushWarning( QObject::tr(
"%1: Layer has no paint property, skipping" ).arg( jsonLayer.value( QStringLiteral(
"id" ) ).toString() ) );
167 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
174 if ( jsonPaint.contains( QStringLiteral(
"fill-color" ) ) )
176 const QVariant jsonFillColor = jsonPaint.
value( QStringLiteral(
"fill-color" ) );
177 switch ( jsonFillColor.type() )
184 case QVariant::StringList:
188 case QVariant::String:
189 fillColor =
parseColor( jsonFillColor.toString(), context );
202 fillColor = QColor( 0, 0, 0 );
205 QColor fillOutlineColor;
206 if ( !jsonPaint.contains( QStringLiteral(
"fill-outline-color" ) ) )
209 if ( fillColor.isValid() )
210 fillOutlineColor = fillColor;
220 const QVariant jsonFillOutlineColor = jsonPaint.value( QStringLiteral(
"fill-outline-color" ) );
221 switch ( jsonFillOutlineColor.type() )
228 case QVariant::StringList:
232 case QVariant::String:
233 fillOutlineColor =
parseColor( jsonFillOutlineColor.toString(), context );
242 double fillOpacity = -1.0;
243 double rasterOpacity = -1.0;
244 if ( jsonPaint.contains( QStringLiteral(
"fill-opacity" ) ) )
246 const QVariant jsonFillOpacity = jsonPaint.value( QStringLiteral(
"fill-opacity" ) );
247 switch ( jsonFillOpacity.type() )
250 case QVariant::Double:
251 fillOpacity = jsonFillOpacity.toDouble();
252 rasterOpacity = fillOpacity;
258 context.
pushWarning( QObject::tr(
"%1: Could not set opacity of layer, opacity already defined in fill color" ).arg( context.
layerId() ) );
269 case QVariant::StringList:
272 context.
pushWarning( QObject::tr(
"%1: Could not set opacity of layer, opacity already defined in fill color" ).arg( context.
layerId() ) );
289 QPointF fillTranslate;
290 if ( jsonPaint.contains( QStringLiteral(
"fill-translate" ) ) )
292 const QVariant jsonFillTranslate = jsonPaint.value( QStringLiteral(
"fill-translate" ) );
293 switch ( jsonFillTranslate.type() )
301 case QVariant::StringList:
319 if ( !fillTranslate.isNull() )
325 if ( jsonPaint.contains( QStringLiteral(
"fill-pattern" ) ) )
329 const QVariant fillPatternJson = jsonPaint.value( QStringLiteral(
"fill-pattern" ) );
332 fillColor = QColor();
333 fillOutlineColor = QColor();
340 QString spriteProperty, spriteSizeProperty;
341 const QString sprite =
retrieveSpriteAsBase64( fillPatternJson, context, spriteSize, spriteProperty, spriteSizeProperty );
342 if ( !sprite.isEmpty() )
347 rasterFill->
setWidth( spriteSize.width() );
351 if ( rasterOpacity >= 0 )
356 if ( !spriteProperty.isEmpty() )
363 symbol->appendSymbolLayer( rasterFill );
369 if ( fillOpacity != -1 )
371 symbol->setOpacity( fillOpacity );
374 if ( fillOutlineColor.isValid() )
383 if ( fillColor.isValid() )
399 if ( !jsonLayer.contains( QStringLiteral(
"paint" ) ) )
401 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
405 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
406 if ( jsonPaint.contains( QStringLiteral(
"line-pattern" ) ) )
408 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-pattern property" ).arg( context.
layerId() ) );
416 if ( jsonPaint.contains( QStringLiteral(
"line-color" ) ) )
418 const QVariant jsonLineColor = jsonPaint.
value( QStringLiteral(
"line-color" ) );
419 switch ( jsonLineColor.type() )
427 case QVariant::StringList:
432 case QVariant::String:
433 lineColor =
parseColor( jsonLineColor.toString(), context );
444 lineColor = QColor( 0, 0, 0 );
448 double lineWidth = 1.0;
449 if ( jsonPaint.contains( QStringLiteral(
"line-width" ) ) )
451 const QVariant jsonLineWidth = jsonPaint.value( QStringLiteral(
"line-width" ) );
452 switch ( jsonLineWidth.type() )
455 case QVariant::Double:
465 case QVariant::StringList:
475 double lineOffset = 0.0;
476 if ( jsonPaint.contains( QStringLiteral(
"line-offset" ) ) )
478 const QVariant jsonLineOffset = jsonPaint.value( QStringLiteral(
"line-offset" ) );
479 switch ( jsonLineOffset.type() )
482 case QVariant::Double:
492 case QVariant::StringList:
502 double lineOpacity = -1.0;
503 if ( jsonPaint.contains( QStringLiteral(
"line-opacity" ) ) )
505 const QVariant jsonLineOpacity = jsonPaint.value( QStringLiteral(
"line-opacity" ) );
506 switch ( jsonLineOpacity.type() )
509 case QVariant::Double:
510 lineOpacity = jsonLineOpacity.toDouble();
516 context.
pushWarning( QObject::tr(
"%1: Could not set opacity of layer, opacity already defined in stroke color" ).arg( context.
layerId() ) );
525 case QVariant::StringList:
528 context.
pushWarning( QObject::tr(
"%1: Could not set opacity of layer, opacity already defined in stroke color" ).arg( context.
layerId() ) );
542 QVector< double > dashVector;
543 if ( jsonPaint.contains( QStringLiteral(
"line-dasharray" ) ) )
545 const QVariant jsonLineDashArray = jsonPaint.value( QStringLiteral(
"line-dasharray" ) );
546 switch ( jsonLineDashArray.type() )
551 const QVariantList dashSource = jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList().last().toList().value( 1 ).toList();
552 for (
const QVariant &v : dashSource )
560 case QVariant::StringList:
562 const QVariantList dashSource = jsonLineDashArray.toList();
563 for (
const QVariant &v : dashSource )
576 Qt::PenCapStyle penCapStyle = Qt::FlatCap;
577 Qt::PenJoinStyle penJoinStyle = Qt::MiterJoin;
578 if ( jsonLayer.contains( QStringLiteral(
"layout" ) ) )
580 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
581 if ( jsonLayout.contains( QStringLiteral(
"line-cap" ) ) )
583 penCapStyle =
parseCapStyle( jsonLayout.value( QStringLiteral(
"line-cap" ) ).toString() );
585 if ( jsonLayout.contains( QStringLiteral(
"line-join" ) ) )
587 penJoinStyle =
parseJoinStyle( jsonLayout.value( QStringLiteral(
"line-join" ) ).toString() );
603 if ( lineOpacity != -1 )
605 symbol->setOpacity( lineOpacity );
607 if ( lineColor.isValid() )
611 if ( lineWidth != -1 )
615 if ( !dashVector.empty() )
631 if ( !jsonLayer.contains( QStringLiteral(
"layout" ) ) )
633 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
636 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
637 if ( !jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
643 if ( !jsonLayer.contains( QStringLiteral(
"paint" ) ) )
645 context.
pushWarning( QObject::tr(
"%1: Style layer has no paint property, skipping" ).arg( context.
layerId() ) );
648 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
653 QString textSizeProperty;
654 if ( jsonLayout.contains( QStringLiteral(
"text-size" ) ) )
656 const QVariant jsonTextSize = jsonLayout.value( QStringLiteral(
"text-size" ) );
657 switch ( jsonTextSize.type() )
660 case QVariant::Double:
671 case QVariant::StringList:
681 if ( !textSizeProperty.isEmpty() )
688 constexpr
double EM_TO_CHARS = 2.0;
690 double textMaxWidth = -1;
691 if ( jsonLayout.contains( QStringLiteral(
"text-max-width" ) ) )
693 const QVariant jsonTextMaxWidth = jsonLayout.value( QStringLiteral(
"text-max-width" ) );
694 switch ( jsonTextMaxWidth.type() )
697 case QVariant::Double:
698 textMaxWidth = jsonTextMaxWidth.toDouble() * EM_TO_CHARS;
706 case QVariant::StringList:
718 textMaxWidth = 10 * EM_TO_CHARS;
721 double textLetterSpacing = -1;
722 if ( jsonLayout.contains( QStringLiteral(
"text-letter-spacing" ) ) )
724 const QVariant jsonTextLetterSpacing = jsonLayout.value( QStringLiteral(
"text-letter-spacing" ) );
725 switch ( jsonTextLetterSpacing.type() )
728 case QVariant::Double:
729 textLetterSpacing = jsonTextLetterSpacing.toDouble();
737 case QVariant::StringList:
748 bool foundFont =
false;
750 if ( jsonLayout.contains( QStringLiteral(
"text-font" ) ) )
752 auto splitFontFamily = [](
const QString & fontName, QString & family, QString & style ) ->
bool
754 const QStringList textFontParts = fontName.split(
' ' );
755 for (
int i = 1; i < textFontParts.size(); ++i )
757 const QString candidateFontName = textFontParts.mid( 0, i ).join(
' ' );
758 const QString candidateFontStyle = textFontParts.mid( i ).join(
' ' );
761 family = candidateFontName;
762 style = candidateFontStyle;
767 if ( QFontDatabase().hasFamily( fontName ) )
777 const QVariant jsonTextFont = jsonLayout.value( QStringLiteral(
"text-font" ) );
778 if ( jsonTextFont.type() != QVariant::List && jsonTextFont.type() != QVariant::StringList && jsonTextFont.type() != QVariant::String
779 && jsonTextFont.type() != QVariant::Map )
785 switch ( jsonTextFont.type() )
788 case QVariant::StringList:
789 fontName = jsonTextFont.toList().value( 0 ).toString();
792 case QVariant::String:
793 fontName = jsonTextFont.toString();
798 QString familyCaseString = QStringLiteral(
"CASE " );
799 QString styleCaseString = QStringLiteral(
"CASE " );
802 const QVariantList stops = jsonTextFont.toMap().value( QStringLiteral(
"stops" ) ).toList();
805 for (
int i = 0; i < stops.length() - 1; ++i )
808 const QVariant bz = stops.value( i ).toList().value( 0 );
809 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();
810 if ( bz.type() == QVariant::List || bz.type() == QVariant::StringList )
812 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
818 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
819 if ( tz.type() == QVariant::List || tz.type() == QVariant::StringList )
821 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
826 if ( splitFontFamily( bv, fontFamily, fontStyle ) )
828 familyCaseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
829 "THEN %3 " ).arg( bz.toString(),
832 styleCaseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
833 "THEN %3 " ).arg( bz.toString(),
839 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
845 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();
846 if ( splitFontFamily( bv, fontFamily, fontStyle ) )
853 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
860 fontName = fontFamily;
871 if ( splitFontFamily( fontName, fontFamily, fontStyle ) )
873 textFont = QFont( fontFamily );
874 if ( !fontStyle.isEmpty() )
875 textFont.setStyleName( fontStyle );
885 fontName = QStringLiteral(
"Open Sans" );
886 textFont = QFont( fontName );
887 textFont.setStyleName( QStringLiteral(
"Regular" ) );
892 fontName = QStringLiteral(
"Arial Unicode MS" );
893 textFont = QFont( fontName );
894 textFont.setStyleName( QStringLiteral(
"Regular" ) );
899 fontName = QStringLiteral(
"Open Sans, Arial Unicode MS" );
902 if ( !foundFont && !fontName.isEmpty() )
904 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), fontName ) );
909 if ( jsonPaint.contains( QStringLiteral(
"text-color" ) ) )
911 const QVariant jsonTextColor = jsonPaint.value( QStringLiteral(
"text-color" ) );
912 switch ( jsonTextColor.type() )
919 case QVariant::StringList:
923 case QVariant::String:
924 textColor =
parseColor( jsonTextColor.toString(), context );
935 textColor = QColor( 0, 0, 0 );
940 if ( jsonPaint.contains( QStringLiteral(
"text-halo-color" ) ) )
942 const QVariant jsonBufferColor = jsonPaint.value( QStringLiteral(
"text-halo-color" ) );
943 switch ( jsonBufferColor.type() )
950 case QVariant::StringList:
954 case QVariant::String:
955 bufferColor =
parseColor( jsonBufferColor.toString(), context );
964 double bufferSize = 0.0;
968 constexpr
double BUFFER_SIZE_SCALE = 2.0;
969 if ( jsonPaint.contains( QStringLiteral(
"text-halo-width" ) ) )
971 const QVariant jsonHaloWidth = jsonPaint.value( QStringLiteral(
"text-halo-width" ) );
972 switch ( jsonHaloWidth.type() )
975 case QVariant::Double:
985 case QVariant::StringList:
996 double haloBlurSize = 0;
997 if ( jsonPaint.contains( QStringLiteral(
"text-halo-blur" ) ) )
999 const QVariant jsonTextHaloBlur = jsonPaint.value( QStringLiteral(
"text-halo-blur" ) );
1000 switch ( jsonTextHaloBlur.type() )
1003 case QVariant::Double:
1017 if ( textColor.isValid() )
1019 if ( textSize >= 0 )
1023 if ( textLetterSpacing > 0 )
1025 QFont f = format.
font();
1026 f.setLetterSpacing( QFont::AbsoluteSpacing, textLetterSpacing );
1030 if ( bufferSize > 0 )
1037 if ( haloBlurSize > 0 )
1053 if ( textMaxWidth > 0 )
1060 auto processLabelField = [](
const QString & string,
bool & isExpression )->QString
1064 const QRegularExpression singleFieldRx( QStringLiteral(
"^{([^}]+)}$" ) );
1065 QRegularExpressionMatch match = singleFieldRx.match(
string );
1066 if ( match.hasMatch() )
1068 isExpression =
false;
1069 return match.captured( 1 );
1072 const QRegularExpression multiFieldRx( QStringLiteral(
"(?={[^}]+})" ) );
1073 const QStringList parts =
string.split( multiFieldRx );
1074 if ( parts.size() > 1 )
1076 isExpression =
true;
1079 for (
const QString &part : parts )
1081 if ( part.isEmpty() )
1085 const QStringList split = part.split(
'}' );
1087 if ( !split.at( 1 ).isEmpty() )
1090 return QStringLiteral(
"concat(%1)" ).arg( res.join(
',' ) );
1094 isExpression =
false;
1099 if ( jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
1101 const QVariant jsonTextField = jsonLayout.value( QStringLiteral(
"text-field" ) );
1102 switch ( jsonTextField.type() )
1104 case QVariant::String:
1106 labelSettings.
fieldName = processLabelField( jsonTextField.toString(), labelSettings.
isExpression );
1110 case QVariant::List:
1111 case QVariant::StringList:
1113 const QVariantList textFieldList = jsonTextField.toList();
1121 if ( textFieldList.size() > 2 && textFieldList.at( 0 ).toString() == QLatin1String(
"format" ) )
1124 for (
int i = 1; i < textFieldList.size(); ++i )
1126 bool isExpression =
false;
1127 const QString part = processLabelField( textFieldList.at( i ).toString(), isExpression );
1128 if ( !isExpression )
1135 labelSettings.
fieldName = QStringLiteral(
"concat(%1)" ).arg( parts.join(
',' ) );
1156 if ( jsonLayout.contains( QStringLiteral(
"text-transform" ) ) )
1158 const QString textTransform = jsonLayout.value( QStringLiteral(
"text-transform" ) ).toString();
1159 if ( textTransform == QLatin1String(
"uppercase" ) )
1163 else if ( textTransform == QLatin1String(
"lowercase" ) )
1172 if ( jsonLayout.contains( QStringLiteral(
"symbol-placement" ) ) )
1174 const QString symbolPlacement = jsonLayout.value( QStringLiteral(
"symbol-placement" ) ).toString();
1175 if ( symbolPlacement == QLatin1String(
"line" ) )
1181 if ( jsonLayout.contains( QStringLiteral(
"text-rotation-alignment" ) ) )
1183 const QString textRotationAlignment = jsonLayout.value( QStringLiteral(
"text-rotation-alignment" ) ).toString();
1184 if ( textRotationAlignment == QLatin1String(
"viewport" ) )
1193 QString textOffsetProperty;
1194 if ( jsonLayout.contains( QStringLiteral(
"text-offset" ) ) )
1196 const QVariant jsonTextOffset = jsonLayout.value( QStringLiteral(
"text-offset" ) );
1199 switch ( jsonTextOffset.type() )
1202 textOffsetProperty =
parseInterpolatePointByZoom( jsonTextOffset.toMap(), context, textSizeProperty.isEmpty() ? textSize : 1.0, &textOffset );
1203 if ( textSizeProperty.isEmpty() )
1214 case QVariant::List:
1215 case QVariant::StringList:
1216 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1217 jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1225 if ( !textOffset.isNull() )
1228 labelSettings.
dist = std::abs( textOffset.y() ) - textSize;
1230 if ( !textSizeProperty.isEmpty() && textOffsetProperty.isEmpty() )
1237 if ( textOffset.isNull() )
1245 if ( jsonLayout.contains( QStringLiteral(
"text-justify" ) ) )
1247 const QVariant jsonTextJustify = jsonLayout.value( QStringLiteral(
"text-justify" ) );
1250 QString textAlign = QStringLiteral(
"center" );
1252 const QVariantMap conversionMap
1254 { QStringLiteral(
"left" ), QStringLiteral(
"left" ) },
1255 { QStringLiteral(
"center" ), QStringLiteral(
"center" ) },
1256 { QStringLiteral(
"right" ), QStringLiteral(
"right" ) },
1257 { QStringLiteral(
"auto" ), QStringLiteral(
"follow" ) }
1260 switch ( jsonTextJustify.type() )
1262 case QVariant::String:
1263 textAlign = jsonTextJustify.toString();
1266 case QVariant::List:
1279 if ( textAlign == QLatin1String(
"left" ) )
1281 else if ( textAlign == QLatin1String(
"right" ) )
1283 else if ( textAlign == QLatin1String(
"center" ) )
1285 else if ( textAlign == QLatin1String(
"follow" ) )
1295 if ( jsonLayout.contains( QStringLiteral(
"text-anchor" ) ) )
1297 const QVariant jsonTextAnchor = jsonLayout.value( QStringLiteral(
"text-anchor" ) );
1300 const QVariantMap conversionMap
1302 { QStringLiteral(
"center" ), 4 },
1303 { QStringLiteral(
"left" ), 5 },
1304 { QStringLiteral(
"right" ), 3 },
1305 { QStringLiteral(
"top" ), 7 },
1306 { QStringLiteral(
"bottom" ), 1 },
1307 { QStringLiteral(
"top-left" ), 8 },
1308 { QStringLiteral(
"top-right" ), 6 },
1309 { QStringLiteral(
"bottom-left" ), 2 },
1310 { QStringLiteral(
"bottom-right" ), 0 },
1313 switch ( jsonTextAnchor.type() )
1315 case QVariant::String:
1316 textAnchor = jsonTextAnchor.toString();
1319 case QVariant::List:
1332 if ( textAnchor == QLatin1String(
"center" ) )
1334 else if ( textAnchor == QLatin1String(
"left" ) )
1336 else if ( textAnchor == QLatin1String(
"right" ) )
1338 else if ( textAnchor == QLatin1String(
"top" ) )
1340 else if ( textAnchor == QLatin1String(
"bottom" ) )
1342 else if ( textAnchor == QLatin1String(
"top-left" ) )
1344 else if ( textAnchor == QLatin1String(
"top-right" ) )
1346 else if ( textAnchor == QLatin1String(
"bottom-left" ) )
1348 else if ( textAnchor == QLatin1String(
"bottom-right" ) )
1353 if ( jsonLayout.contains( QStringLiteral(
"text-offset" ) ) )
1355 const QVariant jsonTextOffset = jsonLayout.value( QStringLiteral(
"text-offset" ) );
1358 switch ( jsonTextOffset.type() )
1364 case QVariant::List:
1365 case QVariant::StringList:
1366 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1367 jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1375 if ( !textOffset.isNull() )
1378 labelSettings.
xOffset = textOffset.x();
1379 labelSettings.
yOffset = textOffset.y();
1384 if ( jsonLayout.contains( QStringLiteral(
"icon-image" ) ) &&
1388 QString spriteProperty, spriteSizeProperty;
1389 const QString sprite =
retrieveSpriteAsBase64( jsonLayout.value( QStringLiteral(
"icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
1390 if ( !sprite.isEmpty() )
1393 markerLayer->
setPath( sprite );
1394 markerLayer->
setSize( spriteSize.width() );
1397 if ( !spriteProperty.isEmpty() )
1409 backgroundSettings.
setSize( spriteSize );
1417 if ( textSize >= 0 )
1440 if ( !jsonLayer.contains( QStringLiteral(
"layout" ) ) )
1442 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
1445 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
1447 if ( jsonLayout.value( QStringLiteral(
"symbol-placement" ) ).toString() == QLatin1String(
"line" ) && !jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
1451 double spacing = -1.0;
1452 if ( jsonLayout.contains( QStringLiteral(
"symbol-spacing" ) ) )
1454 const QVariant jsonSpacing = jsonLayout.
value( QStringLiteral(
"symbol-spacing" ) );
1455 switch ( jsonSpacing.type() )
1458 case QVariant::Double:
1466 case QVariant::List:
1467 case QVariant::StringList:
1482 bool rotateMarkers =
true;
1483 if ( jsonLayout.contains( QStringLiteral(
"icon-rotation-alignment" ) ) )
1485 const QString alignment = jsonLayout.value( QStringLiteral(
"icon-rotation-alignment" ) ).toString();
1486 if ( alignment == QLatin1String(
"map" ) || alignment == QLatin1String(
"auto" ) )
1488 rotateMarkers =
true;
1490 else if ( alignment == QLatin1String(
"viewport" ) )
1492 rotateMarkers =
false;
1497 double rotation = 0.0;
1498 if ( jsonLayout.contains( QStringLiteral(
"icon-rotate" ) ) )
1500 const QVariant jsonIconRotate = jsonLayout.
value( QStringLiteral(
"icon-rotate" ) );
1501 switch ( jsonIconRotate.type() )
1504 case QVariant::Double:
1505 rotation = jsonIconRotate.toDouble();
1512 case QVariant::List:
1513 case QVariant::StringList:
1534 QString spriteProperty, spriteSizeProperty;
1535 const QString sprite =
retrieveSpriteAsBase64( jsonLayout.value( QStringLiteral(
"icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
1536 if ( !sprite.isNull() )
1538 markerLayer->
setPath( sprite );
1539 markerLayer->
setSize( spriteSize.width() );
1542 if ( !spriteProperty.isEmpty() )
1549 if ( jsonLayout.contains( QStringLiteral(
"icon-size" ) ) )
1551 const QVariant jsonIconSize = jsonLayout.value( QStringLiteral(
"icon-size" ) );
1554 switch ( jsonIconSize.type() )
1557 case QVariant::Double:
1559 size = jsonIconSize.toDouble();
1560 if ( !spriteSizeProperty.isEmpty() )
1563 QgsProperty::fromExpression( QStringLiteral(
"with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
1572 case QVariant::List:
1573 case QVariant::StringList:
1578 markerLayer->
setSize( size * spriteSize.width() );
1581 if ( !spriteSizeProperty.isEmpty() )
1598 std::unique_ptr< QgsSymbol > symbol = qgis::make_unique< QgsLineSymbol >(
QgsSymbolLayerList() << lineSymbol );
1601 symbol->setOutputUnit( context.
targetUnit() );
1605 rendererStyle.
setSymbol( symbol.release() );
1608 else if ( jsonLayout.contains( QStringLiteral(
"icon-image" ) ) )
1610 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
1613 QString spriteProperty, spriteSizeProperty;
1614 const QString sprite =
retrieveSpriteAsBase64( jsonLayout.value( QStringLiteral(
"icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
1615 if ( !sprite.isEmpty() )
1618 rasterMarker->
setPath( sprite );
1619 rasterMarker->
setSize( spriteSize.width() );
1623 if ( !spriteProperty.isEmpty() )
1629 if ( jsonLayout.contains( QStringLiteral(
"icon-size" ) ) )
1631 const QVariant jsonIconSize = jsonLayout.value( QStringLiteral(
"icon-size" ) );
1634 switch ( jsonIconSize.type() )
1637 case QVariant::Double:
1639 size = jsonIconSize.toDouble();
1640 if ( !spriteSizeProperty.isEmpty() )
1643 QgsProperty::fromExpression( QStringLiteral(
"with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
1652 case QVariant::List:
1653 case QVariant::StringList:
1658 rasterMarker->
setSize( size * spriteSize.width() );
1661 if ( !spriteSizeProperty.isEmpty() )
1674 double rotation = 0.0;
1675 if ( jsonLayout.contains( QStringLiteral(
"icon-rotate" ) ) )
1677 const QVariant jsonIconRotate = jsonLayout.value( QStringLiteral(
"icon-rotate" ) );
1678 switch ( jsonIconRotate.type() )
1681 case QVariant::Double:
1682 rotation = jsonIconRotate.toDouble();
1689 case QVariant::List:
1690 case QVariant::StringList:
1700 double iconOpacity = -1.0;
1701 if ( jsonPaint.contains( QStringLiteral(
"icon-opacity" ) ) )
1703 const QVariant jsonIconOpacity = jsonPaint.value( QStringLiteral(
"icon-opacity" ) );
1704 switch ( jsonIconOpacity.type() )
1707 case QVariant::Double:
1708 iconOpacity = jsonIconOpacity.toDouble();
1715 case QVariant::List:
1716 case QVariant::StringList:
1727 rasterMarker->
setAngle( rotation );
1728 if ( iconOpacity >= 0 )
1732 rendererStyle.
setSymbol( markerSymbol );
1743 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
1744 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
1745 if ( stops.empty() )
1748 QString caseString = QStringLiteral(
"CASE " );
1750 for (
int i = 0; i < stops.length() - 1; ++i )
1753 const QString bz = stops.at( i ).toList().value( 0 ).toString();
1755 const QString tz = stops.at( i + 1 ).toList().value( 0 ).toString();
1757 const QColor bottomColor =
parseColor( stops.at( i ).toList().value( 1 ), context );
1758 const QColor topColor =
parseColor( stops.at( i + 1 ).toList().value( 1 ), context );
1771 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
1772 "%3, %4, %5, %6) " ).arg( bz, tz,
1780 const QString tz = stops.last().toList().value( 0 ).toString();
1781 const QColor topColor =
parseColor( stops.last().toList().value( 1 ), context );
1788 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
1789 "ELSE color_hsla(%2, %3, %4, %5) END" ).arg( tz )
1790 .arg( tcHue ).arg( tcSat ).arg( tcLight ).arg( tcAlpha );
1793 if ( !stops.empty() && defaultColor )
1794 *defaultColor =
parseColor( stops.value( 0 ).toList().value( 1 ).toString(), context );
1801 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
1802 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
1803 if ( stops.empty() )
1806 QString scaleExpression;
1807 if ( stops.size() <= 2 )
1810 stops.last().toList().value( 0 ).toDouble(),
1811 stops.value( 0 ).toList().value( 1 ).toDouble(),
1812 stops.last().toList().value( 1 ).toDouble(), base, multiplier );
1816 scaleExpression =
parseStops( base, stops, multiplier, context );
1819 if ( !stops.empty() && defaultNumber )
1820 *defaultNumber = stops.value( 0 ).toList().value( 1 ).toDouble() * multiplier;
1827 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
1828 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
1829 if ( stops.empty() )
1832 QString scaleExpression;
1833 if ( stops.length() <= 2 )
1835 scaleExpression = QStringLiteral(
"set_color_part(@symbol_color, 'alpha', %1)" )
1837 stops.last().toList().value( 0 ).toDouble(),
1838 stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity,
1839 stops.last().toList().value( 1 ).toDouble() * maxOpacity, base ) );
1850 QString caseString = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN set_color_part(@symbol_color, 'alpha', %2)" )
1851 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
1852 .arg( stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity );
1854 for (
int i = 0; i < stops.size() - 1; ++i )
1856 caseString += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
1857 "THEN set_color_part(@symbol_color, 'alpha', %3)" )
1858 .arg( stops.value( i ).toList().value( 0 ).toString(),
1859 stops.value( i + 1 ).toList().value( 0 ).toString(),
1861 stops.value( i + 1 ).toList().value( 0 ).toDouble(),
1862 stops.value( i ).toList().value( 1 ).toDouble() * maxOpacity,
1863 stops.value( i + 1 ).toList().value( 1 ).toDouble() * maxOpacity, base ) );
1866 caseString += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
1867 "THEN set_color_part(@symbol_color, 'alpha', %2) END" )
1868 .arg( stops.last().toList().value( 0 ).toString() )
1869 .arg( stops.last().toList().value( 1 ).toDouble() * maxOpacity );
1875 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
1876 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
1877 if ( stops.empty() )
1880 QString scaleExpression;
1881 if ( stops.size() <= 2 )
1883 scaleExpression = QStringLiteral(
"array(%1,%2)" ).arg(
interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(),
1884 stops.last().toList().value( 0 ).toDouble(),
1885 stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble(),
1886 stops.last().toList().value( 1 ).toList().value( 0 ).toDouble(), base, multiplier ),
1888 stops.last().toList().value( 0 ).toDouble(),
1889 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble(),
1890 stops.last().toList().value( 1 ).toList().value( 1 ).toDouble(), base, multiplier )
1895 scaleExpression =
parsePointStops( base, stops, context, multiplier );
1898 if ( !stops.empty() && defaultPoint )
1899 *defaultPoint = QPointF( stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble() * multiplier,
1900 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble() * multiplier );
1906 const QVariantMap &conversionMap, QString *defaultString )
1908 const QVariantList stops = json.
value( QStringLiteral(
"stops" ) ).toList();
1909 if ( stops.empty() )
1912 QString scaleExpression =
parseStringStops( stops, context, conversionMap, defaultString );
1919 QString caseString = QStringLiteral(
"CASE " );
1921 for (
int i = 0; i < stops.length() - 1; ++i )
1924 const QVariant bz = stops.value( i ).toList().value( 0 );
1925 const QVariant bv = stops.value( i ).toList().value( 1 );
1926 if ( bv.type() != QVariant::List && bv.type() != QVariant::StringList )
1933 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
1934 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
1935 if ( tv.type() != QVariant::List && tv.type() != QVariant::StringList )
1941 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1942 "THEN array(%3,%4)" ).arg( bz.toString(),
1944 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 0 ).toDouble(), tv.toList().value( 0 ).toDouble(), base, multiplier ),
1945 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 1 ).toDouble(), tv.toList().value( 1 ).toDouble(), base, multiplier ) );
1947 caseString += QLatin1String(
"END" );
1953 QString caseString = QStringLiteral(
"CASE " );
1955 for (
int i = 0; i < stops.length() - 1; ++i )
1958 const QVariant bz = stops.value( i ).toList().value( 0 );
1959 const QVariant bv = stops.value( i ).toList().value( 1 );
1960 if ( bz.type() == QVariant::List || bz.type() == QVariant::StringList )
1962 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1967 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
1968 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
1969 if ( tz.type() == QVariant::List || tz.type() == QVariant::StringList )
1971 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1975 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1976 "THEN %3 " ).arg( bz.toString(),
1978 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toDouble(), tv.toDouble(), base, multiplier ) );
1981 const QVariant z = stops.last().toList().value( 0 );
1982 const QVariant v = stops.last().toList().value( 1 );
1983 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 "
1984 "THEN %2 END" ).arg( z.toString() ).arg( v.toDouble() * multiplier );
1990 QString caseString = QStringLiteral(
"CASE " );
1992 for (
int i = 0; i < stops.length() - 1; ++i )
1995 const QVariant bz = stops.value( i ).toList().value( 0 );
1996 const QString bv = stops.value( i ).toList().value( 1 ).toString();
1997 if ( bz.type() == QVariant::List || bz.type() == QVariant::StringList )
1999 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2004 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2005 if ( tz.type() == QVariant::List || tz.type() == QVariant::StringList )
2007 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2011 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2012 "THEN %3 " ).arg( bz.toString(),
2016 caseString += QStringLiteral(
"ELSE %1 END" ).arg(
QgsExpression::quotedValue( conversionMap.value( stops.constLast().toList().value( 1 ).toString(),
2017 stops.constLast().toList().value( 1 ) ) ) );
2018 if ( defaultString )
2019 *defaultString = stops.constLast().toList().value( 1 ).toString();
2025 const QString method = json.
value( 0 ).toString();
2026 if ( method == QLatin1String(
"interpolate" ) )
2030 else if ( method == QLatin1String(
"match" ) )
2032 return parseMatchList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
2036 context.
pushWarning( QObject::tr(
"%1: Could not interpret value list with method %2" ).arg( context.
layerId(), method ) );
2043 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
2044 if ( attribute.isEmpty() )
2046 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
2050 QString caseString = QStringLiteral(
"CASE " );
2052 for (
int i = 2; i < json.length() - 1; i += 2 )
2054 const QVariantList keys = json.value( i ).toList();
2056 QStringList matchString;
2057 for (
const QVariant &key : keys )
2062 const QVariant value = json.value( i + 1 );
2064 QString valueString;
2069 const QColor color =
parseColor( value, context );
2076 const double v = value.toDouble() * multiplier;
2077 valueString = QString::number( v );
2083 const double v = value.toDouble() * maxOpacity;
2084 valueString = QString::number( v );
2090 valueString = QStringLiteral(
"array(%1,%2)" ).arg( value.toList().value( 0 ).toDouble() * multiplier,
2091 value.toList().value( 0 ).toDouble() * multiplier );
2097 caseString += QStringLiteral(
"WHEN %1 IN (%2) THEN %3 " ).arg( attribute,
2098 matchString.join(
',' ), valueString );
2107 const QColor color =
parseColor( json.constLast(), context );
2109 *defaultColor = color;
2117 const double v = json.constLast().toDouble() * multiplier;
2118 if ( defaultNumber )
2120 elseValue = QString::number( v );
2126 const double v = json.constLast().toDouble() * maxOpacity;
2127 if ( defaultNumber )
2129 elseValue = QString::number( v );
2135 elseValue = QStringLiteral(
"array(%1,%2)" ).arg( json.constLast().toList().value( 0 ).toDouble() * multiplier,
2136 json.constLast().toList().value( 0 ).toDouble() * multiplier );
2142 caseString += QStringLiteral(
"ELSE %1 END" ).arg( elseValue );
2148 if ( json.value( 0 ).toString() != QLatin1String(
"interpolate" ) )
2150 context.
pushWarning( QObject::tr(
"%1: Could not interpret value list" ).arg( context.
layerId() ) );
2155 const QString technique = json.value( 1 ).toList().value( 0 ).toString();
2156 if ( technique == QLatin1String(
"linear" ) )
2158 else if ( technique == QLatin1String(
"exponential" ) )
2159 base = json.value( 1 ).toList(). value( 1 ).toDouble();
2160 else if ( technique == QLatin1String(
"cubic-bezier" ) )
2162 context.
pushWarning( QObject::tr(
"%1: Cubic-bezier interpolation is not supported, linear used instead." ).arg( context.
layerId() ) );
2167 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation method %2" ).arg( context.
layerId(), technique ) );
2171 if ( json.value( 2 ).toList().value( 0 ).toString() != QLatin1String(
"zoom" ) )
2173 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation input %2" ).arg( context.
layerId(), json.value( 2 ).toString() ) );
2179 for (
int i = 3; i < json.length(); i += 2 )
2181 stops.push_back( QVariantList() << json.value( i ).toString() << json.value( i + 1 ).toString() );
2185 props.insert( QStringLiteral(
"stops" ), stops );
2186 props.insert( QStringLiteral(
"base" ), base );
2189 case PropertyType::Color:
2192 case PropertyType::Numeric:
2195 case PropertyType::Opacity:
2198 case PropertyType::Point:
2206 if ( color.type() != QVariant::String )
2208 context.
pushWarning( QObject::tr(
"%1: Could not parse non-string color %2, skipping" ).arg( context.
layerId(), color.toString() ) );
2217 hue = std::max( 0, color.hslHue() );
2218 saturation = color.hslSaturation() / 255.0 * 100;
2219 lightness = color.lightness() / 255.0 * 100;
2220 alpha = color.alpha();
2227 return QString::number( valueMin * multiplier );
2232 expression = QStringLiteral(
"scale_linear(@vector_tile_zoom,%1,%2,%3,%4)" ).arg( zoomMin )
2239 expression = QStringLiteral(
"scale_exp(@vector_tile_zoom,%1,%2,%3,%4,%5)" ).arg( zoomMin )
2246 if ( multiplier != 1 )
2247 return QStringLiteral(
"%1 * %2" ).arg( expression ).arg( multiplier );
2254 if ( style == QLatin1String(
"round" ) )
2255 return Qt::RoundCap;
2256 else if ( style == QLatin1String(
"square" ) )
2257 return Qt::SquareCap;
2264 if ( style == QLatin1String(
"bevel" ) )
2265 return Qt::BevelJoin;
2266 else if ( style == QLatin1String(
"round" ) )
2267 return Qt::RoundJoin;
2269 return Qt::MiterJoin;
2274 QString op = expression.value( 0 ).toString();
2275 if ( op == QLatin1String(
"all" )
2276 || op == QLatin1String(
"any" )
2277 || op == QLatin1String(
"none" ) )
2280 for (
int i = 1; i < expression.size(); ++i )
2282 QString part = parseValue( expression.at( i ), context );
2283 if ( part.isEmpty() )
2285 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
2291 if ( op == QLatin1String(
"none" ) )
2292 return QStringLiteral(
"NOT (%1)" ).arg( parts.join( QLatin1String(
") AND NOT (" ) ) );
2294 QString operatorString;
2295 if ( op == QLatin1String(
"all" ) )
2296 operatorString = QStringLiteral(
") AND (" );
2297 else if ( op == QLatin1String(
"any" ) )
2298 operatorString = QStringLiteral(
") OR (" );
2300 return QStringLiteral(
"(%1)" ).arg( parts.join( operatorString ) );
2302 else if ( op ==
'!' )
2305 QVariantList contraJsonExpr = expression.value( 1 ).toList();
2306 contraJsonExpr[0] = QString( op + contraJsonExpr[0].toString() );
2308 return parseKey( contraJsonExpr );
2310 else if ( op == QLatin1String(
"==" )
2311 || op == QLatin1String(
"!=" )
2312 || op == QLatin1String(
">=" )
2314 || op == QLatin1String(
"<=" )
2318 if ( op == QLatin1String(
"==" ) )
2319 op = QStringLiteral(
"IS" );
2320 else if ( op == QLatin1String(
"!=" ) )
2321 op = QStringLiteral(
"IS NOT" );
2322 return QStringLiteral(
"%1 %2 %3" ).arg( parseKey( expression.value( 1 ) ),
2323 op, parseValue( expression.value( 2 ), context ) );
2325 else if ( op == QLatin1String(
"has" ) )
2327 return parseKey( expression.value( 1 ) ) + QStringLiteral(
" IS NOT NULL" );
2329 else if ( op == QLatin1String(
"!has" ) )
2331 return parseKey( expression.value( 1 ) ) + QStringLiteral(
" IS NULL" );
2333 else if ( op == QLatin1String(
"in" ) || op == QLatin1String(
"!in" ) )
2335 const QString key = parseKey( expression.value( 1 ) );
2337 for (
int i = 2; i < expression.size(); ++i )
2339 QString part = parseValue( expression.at( i ), context );
2340 if ( part.isEmpty() )
2342 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
2347 if ( op == QLatin1String(
"in" ) )
2348 return QStringLiteral(
"%1 IN (%2)" ).arg( key, parts.join( QLatin1String(
", " ) ) );
2350 return QStringLiteral(
"(%1 IS NULL OR %1 NOT IN (%2))" ).arg( key, parts.join( QLatin1String(
", " ) ) );
2352 else if ( op == QLatin1String(
"get" ) )
2354 return parseKey( expression.value( 1 ) );
2356 else if ( op == QLatin1String(
"match" ) )
2358 const QString attribute = expression.value( 1 ).toList().value( 1 ).toString();
2360 if ( expression.size() == 5
2361 && expression.at( 3 ).type() == QVariant::Bool && expression.at( 3 ).toBool() ==
true
2362 && expression.at( 4 ).type() == QVariant::Bool && expression.at( 4 ).toBool() ==
false )
2365 if ( expression.at( 2 ).type() == QVariant::List || expression.at( 2 ).type() == QVariant::StringList )
2368 for (
const QVariant &p : expression.at( 2 ).toList() )
2373 if ( parts.size() > 1 )
2378 else if ( expression.at( 2 ).type() == QVariant::String || expression.at( 2 ).type() == QVariant::Int
2379 || expression.at( 2 ).type() == QVariant::Double )
2385 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
2391 QString caseString = QStringLiteral(
"CASE " );
2392 for (
int i = 2; i < expression.size() - 2; i += 2 )
2394 if ( expression.at( i ).type() == QVariant::List || expression.at( i ).type() == QVariant::StringList )
2397 for (
const QVariant &p : expression.at( i ).toList() )
2402 if ( parts.size() > 1 )
2407 else if ( expression.at( i ).type() == QVariant::String || expression.at( i ).type() == QVariant::Int
2408 || expression.at( i ).type() == QVariant::Double )
2419 else if ( op == QLatin1String(
"to-string" ) )
2421 return QStringLiteral(
"to_string(%1)" ).arg(
parseExpression( expression.value( 1 ).toList(), context ) );
2425 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
2434 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
2438 const QVariantMap spriteDefinition = context.
spriteDefinitions().value( name ).toMap();
2439 if ( spriteDefinition.size() == 0 )
2441 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
2445 const QImage sprite = context.
spriteImage().copy( spriteDefinition.value( QStringLiteral(
"x" ) ).toInt(),
2446 spriteDefinition.value( QStringLiteral(
"y" ) ).toInt(),
2447 spriteDefinition.value( QStringLiteral(
"width" ) ).toInt(),
2448 spriteDefinition.value( QStringLiteral(
"height" ) ).toInt() );
2449 if ( sprite.isNull() )
2451 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
2455 spriteSize = sprite.size() / spriteDefinition.value( QStringLiteral(
"pixelRatio" ) ).toDouble() * context.
pixelSizeConversionFactor();
2463 auto prepareBase64 = [](
const QImage & sprite )
2466 if ( !sprite.isNull() )
2469 QBuffer buffer( &blob );
2470 buffer.open( QIODevice::WriteOnly );
2471 sprite.save( &buffer,
"PNG" );
2473 QByteArray encoded = blob.toBase64();
2474 path = QString( encoded );
2475 path.prepend( QLatin1String(
"base64:" ) );
2480 switch ( value.type() )
2482 case QVariant::String:
2484 QString spriteName = value.toString();
2485 QRegularExpression fieldNameMatch( QStringLiteral(
"{([^}]+)}" ) );
2486 QRegularExpressionMatch match = fieldNameMatch.match( spriteName );
2487 if ( match.hasMatch() )
2489 const QString fieldName = match.captured( 1 );
2490 spriteProperty = QStringLiteral(
"CASE" );
2491 spriteSizeProperty = QStringLiteral(
"CASE" );
2493 spriteName.replace(
"(", QLatin1String(
"\\(" ) );
2494 spriteName.replace(
")", QLatin1String(
"\\)" ) );
2495 spriteName.replace( fieldNameMatch, QStringLiteral(
"([^\\/\\\\]+)" ) );
2496 QRegularExpression fieldValueMatch( spriteName );
2498 for (
const QString &name : spriteNames )
2500 match = fieldValueMatch.match( name );
2501 if ( match.hasMatch() )
2505 const QString fieldValue = match.captured( 1 );
2507 path = prepareBase64( sprite );
2508 if ( spritePath.isEmpty() && !path.isEmpty() )
2514 spriteProperty += QStringLiteral(
" WHEN \"%1\" = '%2' THEN '%3'" )
2515 .arg( fieldName, fieldValue, path );
2516 spriteSizeProperty += QStringLiteral(
" WHEN \"%1\" = '%2' THEN %3" )
2517 .arg( fieldName ).arg( fieldValue ).arg( size.width() );
2521 spriteProperty += QLatin1String(
" END" );
2522 spriteSizeProperty += QLatin1String(
" END" );
2526 spriteProperty.clear();
2527 spriteSizeProperty.clear();
2528 const QImage sprite =
retrieveSprite( spriteName, context, spriteSize );
2529 spritePath = prepareBase64( sprite );
2536 const QVariantList stops = value.toMap().value( QStringLiteral(
"stops" ) ).toList();
2537 if ( stops.size() == 0 )
2544 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, spriteSize );
2545 spritePath = prepareBase64( sprite );
2547 spriteProperty = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN '%2'" )
2548 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
2550 spriteSizeProperty = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN %2" )
2551 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
2552 .arg( spriteSize.width() );
2554 for (
int i = 0; i < stops.size() - 1; ++i )
2557 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, size );
2558 path = prepareBase64( sprite );
2560 spriteProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
2562 .arg( stops.value( i ).toList().value( 0 ).toString(),
2563 stops.value( i + 1 ).toList().value( 0 ).toString(),
2565 spriteSizeProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
2567 .arg( stops.value( i ).toList().value( 0 ).toString(),
2568 stops.value( i + 1 ).toList().value( 0 ).toString() )
2569 .arg( size.width() );
2571 sprite =
retrieveSprite( stops.last().toList().value( 1 ).toString(), context, size );
2572 path = prepareBase64( sprite );
2574 spriteProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
2576 .arg( stops.last().toList().value( 0 ).toString() )
2578 spriteSizeProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
2580 .arg( stops.last().toList().value( 0 ).toString() )
2581 .arg( size.width() );
2585 case QVariant::List:
2587 const QVariantList json = value.toList();
2588 const QString method = json.value( 0 ).toString();
2589 if ( method != QLatin1String(
"match" ) )
2591 context.
pushWarning( QObject::tr(
"%1: Could not interpret sprite value list with method %2" ).arg( context.
layerId(), method ) );
2595 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
2596 if ( attribute.isEmpty() )
2598 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
2602 spriteProperty = QStringLiteral(
"CASE " );
2603 spriteSizeProperty = QStringLiteral(
"CASE " );
2605 for (
int i = 2; i < json.length() - 1; i += 2 )
2607 const QVariantList keys = json.value( i ).toList();
2609 QStringList matchString;
2610 for (
const QVariant &key : keys )
2615 const QVariant value = json.value( i + 1 );
2617 const QImage sprite =
retrieveSprite( value.toString(), context, spriteSize );
2618 spritePath = prepareBase64( sprite );
2620 spriteProperty += QStringLiteral(
" WHEN %1 IN (%2) "
2621 "THEN '%3' " ).arg( attribute,
2622 matchString.join(
',' ),
2625 spriteSizeProperty += QStringLiteral(
" WHEN %1 IN (%2) "
2626 "THEN %3 " ).arg( attribute,
2627 matchString.join(
',' ) ).arg( spriteSize.width() );
2630 const QImage sprite =
retrieveSprite( json.constLast().toString(), context, spriteSize );
2631 spritePath = prepareBase64( sprite );
2633 spriteProperty += QStringLiteral(
"ELSE %1 END" ).arg( spritePath );
2634 spriteSizeProperty += QStringLiteral(
"ELSE %3 END" ).arg( spriteSize.width() );
2648 switch ( value.type() )
2650 case QVariant::List:
2651 case QVariant::StringList:
2654 case QVariant::String:
2658 case QVariant::Double:
2659 return value.toString();
2662 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression part" ).arg( context.
layerId() ) );
2668 QString QgsMapBoxGlStyleConverter::parseKey(
const QVariant &value )
2670 if ( value.toString() == QLatin1String(
"$type" ) )
2671 return QStringLiteral(
"_geom_type" );
2672 else if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
2674 if ( value.toList().size() > 1 )
2675 return value.toList().at( 1 ).toString();
2677 return value.toList().value( 0 ).toString();
2684 return mRenderer ? mRenderer->clone() :
nullptr;
2689 return mLabeling ? mLabeling->clone() :
nullptr;
2698 mWarnings << warning;
2713 return mSizeConversionFactor;
2718 mSizeConversionFactor = sizeConversionFactor;
2723 return mSpriteImage;
2728 return mSpriteDefinitions;
2733 mSpriteImage = image;
2734 mSpriteDefinitions = definitions;