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 if ( !jsonLayer.contains( QStringLiteral(
"paint" ) ) )
188 context.
pushWarning( QObject::tr(
"%1: Layer has no paint property, skipping" ).arg( jsonLayer.value( QStringLiteral(
"id" ) ).toString() ) );
192 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
197 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsFillSymbol >() );
201 if ( jsonPaint.contains( isBackgroundStyle ? QStringLiteral(
"background-color" ) : QStringLiteral(
"fill-color" ) ) )
203 const QVariant jsonFillColor = jsonPaint.value( isBackgroundStyle ? QStringLiteral(
"background-color" ) : QStringLiteral(
"fill-color" ) );
204 switch ( jsonFillColor.type() )
211 case QVariant::StringList:
215 case QVariant::String:
216 fillColor =
parseColor( jsonFillColor.toString(), context );
229 fillColor = QColor( 0, 0, 0 );
232 QColor fillOutlineColor;
233 if ( !isBackgroundStyle )
235 if ( !jsonPaint.contains( QStringLiteral(
"fill-outline-color" ) ) )
237 if ( fillColor.isValid() )
238 fillOutlineColor = fillColor;
246 const QVariant jsonFillOutlineColor = jsonPaint.value( QStringLiteral(
"fill-outline-color" ) );
247 switch ( jsonFillOutlineColor.type() )
254 case QVariant::StringList:
258 case QVariant::String:
259 fillOutlineColor =
parseColor( jsonFillOutlineColor.toString(), context );
269 double fillOpacity = -1.0;
270 double rasterOpacity = -1.0;
271 if ( jsonPaint.contains( isBackgroundStyle ? QStringLiteral(
"background-opacity" ) : QStringLiteral(
"fill-opacity" ) ) )
273 const QVariant jsonFillOpacity = jsonPaint.value( isBackgroundStyle ? QStringLiteral(
"background-opacity" ) : QStringLiteral(
"fill-opacity" ) );
274 switch ( jsonFillOpacity.type() )
277 case QVariant::Double:
278 fillOpacity = jsonFillOpacity.toDouble();
279 rasterOpacity = fillOpacity;
296 case QVariant::StringList:
316 QPointF fillTranslate;
317 if ( jsonPaint.contains( QStringLiteral(
"fill-translate" ) ) )
319 const QVariant jsonFillTranslate = jsonPaint.value( QStringLiteral(
"fill-translate" ) );
320 switch ( jsonFillTranslate.type() )
328 case QVariant::StringList:
340 Q_ASSERT( fillSymbol );
343 symbol->setOutputUnit( context.
targetUnit() );
346 if ( !fillTranslate.isNull() )
352 if ( jsonPaint.contains( isBackgroundStyle ? QStringLiteral(
"background-pattern" ) : QStringLiteral(
"fill-pattern" ) ) )
356 const QVariant fillPatternJson = jsonPaint.value( isBackgroundStyle ? QStringLiteral(
"background-pattern" ) : QStringLiteral(
"fill-pattern" ) );
359 fillColor = QColor();
360 fillOutlineColor = QColor();
367 QString spriteProperty, spriteSizeProperty;
368 const QString sprite =
retrieveSpriteAsBase64( fillPatternJson, context, spriteSize, spriteProperty, spriteSizeProperty );
369 if ( !sprite.isEmpty() )
374 rasterFill->
setWidth( spriteSize.width() );
378 if ( rasterOpacity >= 0 )
383 if ( !spriteProperty.isEmpty() )
390 symbol->appendSymbolLayer( rasterFill );
396 if ( fillOpacity != -1 )
398 symbol->setOpacity( fillOpacity );
401 if ( fillOutlineColor.isValid() )
410 if ( fillColor.isValid() )
426 if ( !jsonLayer.contains( QStringLiteral(
"paint" ) ) )
428 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
433 QString rasterLineSprite;
435 const QVariantMap jsonPaint = jsonLayer.
value( QStringLiteral(
"paint" ) ).toMap();
436 if ( jsonPaint.contains( QStringLiteral(
"line-pattern" ) ) )
438 const QVariant jsonLinePattern = jsonPaint.value( QStringLiteral(
"line-pattern" ) );
439 switch ( jsonLinePattern.type() )
442 case QVariant::String:
445 QString spriteProperty, spriteSizeProperty;
446 rasterLineSprite =
retrieveSpriteAsBase64( jsonLinePattern, context, spriteSize, spriteProperty, spriteSizeProperty );
452 case QVariant::StringList:
457 if ( rasterLineSprite.isEmpty() )
460 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-pattern property" ).arg( context.
layerId() ) );
467 if ( jsonPaint.contains( QStringLiteral(
"line-color" ) ) )
469 const QVariant jsonLineColor = jsonPaint.value( QStringLiteral(
"line-color" ) );
470 switch ( jsonLineColor.type() )
478 case QVariant::StringList:
483 case QVariant::String:
484 lineColor =
parseColor( jsonLineColor.toString(), context );
495 lineColor = QColor( 0, 0, 0 );
499 double lineWidth = 1.0;
501 if ( jsonPaint.contains( QStringLiteral(
"line-width" ) ) )
503 const QVariant jsonLineWidth = jsonPaint.
value( QStringLiteral(
"line-width" ) );
504 switch ( jsonLineWidth.type() )
507 case QVariant::Double:
518 case QVariant::StringList:
529 double lineOffset = 0.0;
530 if ( jsonPaint.contains( QStringLiteral(
"line-offset" ) ) )
532 const QVariant jsonLineOffset = jsonPaint.value( QStringLiteral(
"line-offset" ) );
533 switch ( jsonLineOffset.type() )
536 case QVariant::Double:
546 case QVariant::StringList:
556 double lineOpacity = -1.0;
557 if ( jsonPaint.contains( QStringLiteral(
"line-opacity" ) ) )
559 const QVariant jsonLineOpacity = jsonPaint.value( QStringLiteral(
"line-opacity" ) );
560 switch ( jsonLineOpacity.type() )
563 case QVariant::Double:
564 lineOpacity = jsonLineOpacity.toDouble();
570 context.
pushWarning( QObject::tr(
"%1: Could not set opacity of layer, opacity already defined in stroke color" ).arg( context.
layerId() ) );
579 case QVariant::StringList:
582 context.
pushWarning( QObject::tr(
"%1: Could not set opacity of layer, opacity already defined in stroke color" ).arg( context.
layerId() ) );
596 QVector< double > dashVector;
597 if ( jsonPaint.contains( QStringLiteral(
"line-dasharray" ) ) )
599 const QVariant jsonLineDashArray = jsonPaint.value( QStringLiteral(
"line-dasharray" ) );
600 switch ( jsonLineDashArray.type() )
604 QString arrayExpression;
607 arrayExpression = QStringLiteral(
"array_to_string(array_foreach(%1,@element * (%2)), ';')" )
608 .arg(
parseArrayStops( jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList(), context, 1 ),
613 arrayExpression = QStringLiteral(
"array_to_string(%1, ';')" ).arg(
parseArrayStops( jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList(), context, lineWidth ) );
617 const QVariantList dashSource = jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList().first().toList().value( 1 ).toList();
618 for (
const QVariant &v : dashSource )
620 dashVector << v.toDouble() * lineWidth;
626 case QVariant::StringList:
630 QString arrayExpression = QStringLiteral(
"array_to_string(array_foreach(array(%1),@element * (%2)), ';')" )
631 .arg( jsonLineDashArray.toStringList().join(
',' ),
635 const QVariantList dashSource = jsonLineDashArray.toList();
636 for (
const QVariant &v : dashSource )
638 dashVector << v.toDouble() * lineWidth;
649 Qt::PenCapStyle penCapStyle = Qt::FlatCap;
650 Qt::PenJoinStyle penJoinStyle = Qt::MiterJoin;
651 if ( jsonLayer.contains( QStringLiteral(
"layout" ) ) )
653 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
654 if ( jsonLayout.contains( QStringLiteral(
"line-cap" ) ) )
656 penCapStyle =
parseCapStyle( jsonLayout.value( QStringLiteral(
"line-cap" ) ).toString() );
658 if ( jsonLayout.contains( QStringLiteral(
"line-join" ) ) )
660 penJoinStyle =
parseJoinStyle( jsonLayout.value( QStringLiteral(
"line-join" ) ).toString() );
664 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsLineSymbol >() );
665 symbol->setOutputUnit( context.
targetUnit() );
667 if ( !rasterLineSprite.isEmpty() )
677 if ( lineOpacity != -1 )
679 symbol->setOpacity( lineOpacity );
681 if ( lineWidth != -1 )
685 symbol->changeSymbolLayer( 0, lineSymbol );
690 Q_ASSERT( lineSymbol );
700 if ( lineOpacity != -1 )
702 symbol->setOpacity( lineOpacity );
704 if ( lineColor.isValid() )
708 if ( lineWidth != -1 )
712 if ( !dashVector.empty() )
726 if ( !jsonLayer.contains( QStringLiteral(
"paint" ) ) )
728 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
732 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
736 QColor circleFillColor;
737 if ( jsonPaint.contains( QStringLiteral(
"circle-color" ) ) )
739 const QVariant jsonCircleColor = jsonPaint.
value( QStringLiteral(
"circle-color" ) );
740 switch ( jsonCircleColor.type() )
747 case QVariant::StringList:
751 case QVariant::String:
752 circleFillColor =
parseColor( jsonCircleColor.toString(), context );
763 circleFillColor = QColor( 0, 0, 0 );
767 double circleDiameter = 10.0;
768 if ( jsonPaint.contains( QStringLiteral(
"circle-radius" ) ) )
770 const QVariant jsonCircleRadius = jsonPaint.value( QStringLiteral(
"circle-radius" ) );
771 switch ( jsonCircleRadius.type() )
774 case QVariant::Double:
784 case QVariant::StringList:
794 double circleOpacity = -1.0;
795 if ( jsonPaint.contains( QStringLiteral(
"circle-opacity" ) ) )
797 const QVariant jsonCircleOpacity = jsonPaint.value( QStringLiteral(
"circle-opacity" ) );
798 switch ( jsonCircleOpacity.type() )
801 case QVariant::Double:
802 circleOpacity = jsonCircleOpacity.toDouble();
810 case QVariant::StringList:
819 if ( ( circleOpacity != -1 ) && circleFillColor.isValid() )
821 circleFillColor.setAlphaF( circleOpacity );
825 QColor circleStrokeColor;
826 if ( jsonPaint.contains( QStringLiteral(
"circle-stroke-color" ) ) )
828 const QVariant jsonCircleStrokeColor = jsonPaint.value( QStringLiteral(
"circle-stroke-color" ) );
829 switch ( jsonCircleStrokeColor.type() )
836 case QVariant::StringList:
840 case QVariant::String:
841 circleStrokeColor =
parseColor( jsonCircleStrokeColor.toString(), context );
851 double circleStrokeWidth = -1.0;
852 if ( jsonPaint.contains( QStringLiteral(
"circle-stroke-width" ) ) )
854 const QVariant circleStrokeWidthJson = jsonPaint.value( QStringLiteral(
"circle-stroke-width" ) );
855 switch ( circleStrokeWidthJson.type() )
858 case QVariant::Double:
863 circleStrokeWidth = -1.0;
868 case QVariant::StringList:
878 double circleStrokeOpacity = -1.0;
879 if ( jsonPaint.contains( QStringLiteral(
"circle-stroke-opacity" ) ) )
881 const QVariant jsonCircleStrokeOpacity = jsonPaint.value( QStringLiteral(
"circle-stroke-opacity" ) );
882 switch ( jsonCircleStrokeOpacity.type() )
885 case QVariant::Double:
886 circleStrokeOpacity = jsonCircleStrokeOpacity.toDouble();
894 case QVariant::StringList:
903 if ( ( circleStrokeOpacity != -1 ) && circleStrokeColor.isValid() )
905 circleStrokeColor.setAlphaF( circleStrokeOpacity );
909 QPointF circleTranslate;
910 if ( jsonPaint.contains( QStringLiteral(
"circle-translate" ) ) )
912 const QVariant jsonCircleTranslate = jsonPaint.value( QStringLiteral(
"circle-translate" ) );
913 switch ( jsonCircleTranslate.type() )
921 case QVariant::StringList:
932 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsMarkerSymbol >() );
934 Q_ASSERT( markerSymbolLayer );
937 symbol->setOutputUnit( context.
targetUnit() );
938 symbol->setDataDefinedProperties( ddProperties );
940 if ( !circleTranslate.isNull() )
942 markerSymbolLayer->
setOffset( circleTranslate );
946 if ( circleFillColor.isValid() )
950 if ( circleDiameter != -1 )
952 markerSymbolLayer->
setSize( circleDiameter );
955 if ( circleStrokeColor.isValid() )
959 if ( circleStrokeWidth != -1 )
975 if ( !jsonLayer.contains( QStringLiteral(
"layout" ) ) )
977 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
980 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
981 if ( !jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
987 if ( !jsonLayer.contains( QStringLiteral(
"paint" ) ) )
989 context.
pushWarning( QObject::tr(
"%1: Style layer has no paint property, skipping" ).arg( context.
layerId() ) );
992 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
998 if ( jsonLayout.contains( QStringLiteral(
"text-size" ) ) )
1000 const QVariant jsonTextSize = jsonLayout.
value( QStringLiteral(
"text-size" ) );
1001 switch ( jsonTextSize.type() )
1004 case QVariant::Double:
1014 case QVariant::List:
1015 case QVariant::StringList:
1025 if ( textSizeProperty )
1032 constexpr
double EM_TO_CHARS = 2.0;
1034 double textMaxWidth = -1;
1035 if ( jsonLayout.contains( QStringLiteral(
"text-max-width" ) ) )
1037 const QVariant jsonTextMaxWidth = jsonLayout.value( QStringLiteral(
"text-max-width" ) );
1038 switch ( jsonTextMaxWidth.type() )
1041 case QVariant::Double:
1042 textMaxWidth = jsonTextMaxWidth.toDouble() * EM_TO_CHARS;
1049 case QVariant::List:
1050 case QVariant::StringList:
1062 textMaxWidth = 10 * EM_TO_CHARS;
1065 double textLetterSpacing = -1;
1066 if ( jsonLayout.contains( QStringLiteral(
"text-letter-spacing" ) ) )
1068 const QVariant jsonTextLetterSpacing = jsonLayout.value( QStringLiteral(
"text-letter-spacing" ) );
1069 switch ( jsonTextLetterSpacing.type() )
1072 case QVariant::Double:
1073 textLetterSpacing = jsonTextLetterSpacing.toDouble();
1080 case QVariant::List:
1081 case QVariant::StringList:
1092 bool foundFont =
false;
1094 QString fontStyleName;
1096 if ( jsonLayout.contains( QStringLiteral(
"text-font" ) ) )
1098 auto splitFontFamily = [](
const QString & fontName, QString & family, QString & style ) ->
bool
1100 const QStringList textFontParts = fontName.split(
' ' );
1101 for (
int i = 1; i < textFontParts.size(); ++i )
1103 const QString candidateFontName = textFontParts.mid( 0, i ).join(
' ' );
1104 const QString candidateFontStyle = textFontParts.mid( i ).join(
' ' );
1107 family = candidateFontName;
1108 style = candidateFontStyle;
1113 if ( QFontDatabase().hasFamily( fontName ) )
1123 const QVariant jsonTextFont = jsonLayout.value( QStringLiteral(
"text-font" ) );
1124 if ( jsonTextFont.type() != QVariant::List && jsonTextFont.type() != QVariant::StringList && jsonTextFont.type() != QVariant::String
1125 && jsonTextFont.type() != QVariant::Map )
1131 switch ( jsonTextFont.type() )
1133 case QVariant::List:
1134 case QVariant::StringList:
1135 fontName = jsonTextFont.toList().value( 0 ).toString();
1138 case QVariant::String:
1139 fontName = jsonTextFont.toString();
1144 QString familyCaseString = QStringLiteral(
"CASE " );
1145 QString styleCaseString = QStringLiteral(
"CASE " );
1147 const QVariantList stops = jsonTextFont.toMap().value( QStringLiteral(
"stops" ) ).toList();
1150 for (
int i = 0; i < stops.length() - 1; ++i )
1153 const QVariant bz = stops.value( i ).toList().value( 0 );
1154 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();
1155 if ( bz.type() == QVariant::List || bz.type() == QVariant::StringList )
1157 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1163 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
1164 if ( tz.type() == QVariant::List || tz.type() == QVariant::StringList )
1166 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1171 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1173 familyCaseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1174 "THEN %3 " ).arg( bz.toString(),
1177 styleCaseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1178 "THEN %3 " ).arg( bz.toString(),
1184 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1190 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();
1191 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1198 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1205 fontName = fontFamily;
1215 if ( splitFontFamily( fontName, fontFamily, fontStyleName ) )
1217 textFont = QFont( fontFamily );
1218 if ( !fontStyleName.isEmpty() )
1219 textFont.setStyleName( fontStyleName );
1229 fontName = QStringLiteral(
"Open Sans" );
1230 textFont = QFont( fontName );
1231 textFont.setStyleName( QStringLiteral(
"Regular" ) );
1232 fontStyleName = QStringLiteral(
"Regular" );
1237 fontName = QStringLiteral(
"Arial Unicode MS" );
1238 textFont = QFont( fontName );
1239 textFont.setStyleName( QStringLiteral(
"Regular" ) );
1240 fontStyleName = QStringLiteral(
"Regular" );
1245 fontName = QStringLiteral(
"Open Sans, Arial Unicode MS" );
1248 if ( !foundFont && !fontName.isEmpty() )
1250 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), fontName ) );
1255 if ( jsonPaint.contains( QStringLiteral(
"text-color" ) ) )
1257 const QVariant jsonTextColor = jsonPaint.value( QStringLiteral(
"text-color" ) );
1258 switch ( jsonTextColor.type() )
1264 case QVariant::List:
1265 case QVariant::StringList:
1269 case QVariant::String:
1270 textColor =
parseColor( jsonTextColor.toString(), context );
1281 textColor = QColor( 0, 0, 0 );
1286 if ( jsonPaint.contains( QStringLiteral(
"text-halo-color" ) ) )
1288 const QVariant jsonBufferColor = jsonPaint.value( QStringLiteral(
"text-halo-color" ) );
1289 switch ( jsonBufferColor.type() )
1295 case QVariant::List:
1296 case QVariant::StringList:
1300 case QVariant::String:
1301 bufferColor =
parseColor( jsonBufferColor.toString(), context );
1310 double bufferSize = 0.0;
1314 constexpr
double BUFFER_SIZE_SCALE = 2.0;
1315 if ( jsonPaint.contains( QStringLiteral(
"text-halo-width" ) ) )
1317 const QVariant jsonHaloWidth = jsonPaint.value( QStringLiteral(
"text-halo-width" ) );
1318 switch ( jsonHaloWidth.type() )
1321 case QVariant::Double:
1330 case QVariant::List:
1331 case QVariant::StringList:
1342 double haloBlurSize = 0;
1343 if ( jsonPaint.contains( QStringLiteral(
"text-halo-blur" ) ) )
1345 const QVariant jsonTextHaloBlur = jsonPaint.value( QStringLiteral(
"text-halo-blur" ) );
1346 switch ( jsonTextHaloBlur.type() )
1349 case QVariant::Double:
1363 if ( textColor.isValid() )
1365 if ( textSize >= 0 )
1370 if ( !fontStyleName.isEmpty() )
1373 if ( textLetterSpacing > 0 )
1375 QFont f = format.
font();
1376 f.setLetterSpacing( QFont::AbsoluteSpacing, textLetterSpacing );
1380 if ( bufferSize > 0 )
1387 if ( haloBlurSize > 0 )
1403 if ( textMaxWidth > 0 )
1410 auto processLabelField = [](
const QString & string,
bool & isExpression )->QString
1414 const QRegularExpression singleFieldRx( QStringLiteral(
"^{([^}]+)}$" ) );
1415 const QRegularExpressionMatch match = singleFieldRx.match(
string );
1416 if ( match.hasMatch() )
1418 isExpression =
false;
1419 return match.captured( 1 );
1422 const QRegularExpression multiFieldRx( QStringLiteral(
"(?={[^}]+})" ) );
1423 const QStringList parts =
string.split( multiFieldRx );
1424 if ( parts.size() > 1 )
1426 isExpression =
true;
1429 for (
const QString &part : parts )
1431 if ( part.isEmpty() )
1434 if ( !part.contains(
'{' ) )
1441 const QStringList split = part.split(
'}' );
1443 if ( !split.at( 1 ).isEmpty() )
1446 return QStringLiteral(
"concat(%1)" ).arg( res.join(
',' ) );
1450 isExpression =
false;
1455 if ( jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
1457 const QVariant jsonTextField = jsonLayout.value( QStringLiteral(
"text-field" ) );
1458 switch ( jsonTextField.type() )
1460 case QVariant::String:
1462 labelSettings.
fieldName = processLabelField( jsonTextField.toString(), labelSettings.
isExpression );
1466 case QVariant::List:
1467 case QVariant::StringList:
1469 const QVariantList textFieldList = jsonTextField.toList();
1477 if ( textFieldList.size() > 2 && textFieldList.at( 0 ).toString() == QLatin1String(
"format" ) )
1480 for (
int i = 1; i < textFieldList.size(); ++i )
1482 bool isExpression =
false;
1483 const QString part = processLabelField( textFieldList.at( i ).toString(), isExpression );
1484 if ( !isExpression )
1491 labelSettings.
fieldName = QStringLiteral(
"concat(%1)" ).arg( parts.join(
',' ) );
1512 if ( jsonLayout.contains( QStringLiteral(
"text-transform" ) ) )
1514 const QString textTransform = jsonLayout.value( QStringLiteral(
"text-transform" ) ).toString();
1515 if ( textTransform == QLatin1String(
"uppercase" ) )
1519 else if ( textTransform == QLatin1String(
"lowercase" ) )
1528 if ( jsonLayout.contains( QStringLiteral(
"symbol-placement" ) ) )
1530 const QString symbolPlacement = jsonLayout.value( QStringLiteral(
"symbol-placement" ) ).toString();
1531 if ( symbolPlacement == QLatin1String(
"line" ) )
1537 if ( jsonLayout.contains( QStringLiteral(
"text-rotation-alignment" ) ) )
1539 const QString textRotationAlignment = jsonLayout.value( QStringLiteral(
"text-rotation-alignment" ) ).toString();
1540 if ( textRotationAlignment == QLatin1String(
"viewport" ) )
1550 if ( jsonLayout.contains( QStringLiteral(
"text-offset" ) ) )
1552 const QVariant jsonTextOffset = jsonLayout.
value( QStringLiteral(
"text-offset" ) );
1555 switch ( jsonTextOffset.type() )
1558 textOffsetProperty =
parseInterpolatePointByZoom( jsonTextOffset.toMap(), context, !textSizeProperty ? textSize : 1.0, &textOffset );
1559 if ( !textSizeProperty )
1570 case QVariant::List:
1571 case QVariant::StringList:
1572 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1573 jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1581 if ( !textOffset.isNull() )
1584 labelSettings.
dist = std::abs( textOffset.y() ) - textSize;
1586 if ( textSizeProperty && !textOffsetProperty )
1593 if ( textOffset.isNull() )
1601 if ( jsonLayout.contains( QStringLiteral(
"text-justify" ) ) )
1603 const QVariant jsonTextJustify = jsonLayout.value( QStringLiteral(
"text-justify" ) );
1606 QString textAlign = QStringLiteral(
"center" );
1608 const QVariantMap conversionMap
1610 { QStringLiteral(
"left" ), QStringLiteral(
"left" ) },
1611 { QStringLiteral(
"center" ), QStringLiteral(
"center" ) },
1612 { QStringLiteral(
"right" ), QStringLiteral(
"right" ) },
1613 { QStringLiteral(
"auto" ), QStringLiteral(
"follow" ) }
1616 switch ( jsonTextJustify.type() )
1618 case QVariant::String:
1619 textAlign = jsonTextJustify.toString();
1622 case QVariant::List:
1635 if ( textAlign == QLatin1String(
"left" ) )
1637 else if ( textAlign == QLatin1String(
"right" ) )
1639 else if ( textAlign == QLatin1String(
"center" ) )
1641 else if ( textAlign == QLatin1String(
"follow" ) )
1651 if ( jsonLayout.contains( QStringLiteral(
"text-anchor" ) ) )
1653 const QVariant jsonTextAnchor = jsonLayout.value( QStringLiteral(
"text-anchor" ) );
1656 const QVariantMap conversionMap
1658 { QStringLiteral(
"center" ), 4 },
1659 { QStringLiteral(
"left" ), 5 },
1660 { QStringLiteral(
"right" ), 3 },
1661 { QStringLiteral(
"top" ), 7 },
1662 { QStringLiteral(
"bottom" ), 1 },
1663 { QStringLiteral(
"top-left" ), 8 },
1664 { QStringLiteral(
"top-right" ), 6 },
1665 { QStringLiteral(
"bottom-left" ), 2 },
1666 { QStringLiteral(
"bottom-right" ), 0 },
1669 switch ( jsonTextAnchor.type() )
1671 case QVariant::String:
1672 textAnchor = jsonTextAnchor.toString();
1675 case QVariant::List:
1688 if ( textAnchor == QLatin1String(
"center" ) )
1690 else if ( textAnchor == QLatin1String(
"left" ) )
1692 else if ( textAnchor == QLatin1String(
"right" ) )
1694 else if ( textAnchor == QLatin1String(
"top" ) )
1696 else if ( textAnchor == QLatin1String(
"bottom" ) )
1698 else if ( textAnchor == QLatin1String(
"top-left" ) )
1700 else if ( textAnchor == QLatin1String(
"top-right" ) )
1702 else if ( textAnchor == QLatin1String(
"bottom-left" ) )
1704 else if ( textAnchor == QLatin1String(
"bottom-right" ) )
1709 if ( jsonLayout.contains( QStringLiteral(
"text-offset" ) ) )
1711 const QVariant jsonTextOffset = jsonLayout.value( QStringLiteral(
"text-offset" ) );
1714 switch ( jsonTextOffset.type() )
1720 case QVariant::List:
1721 case QVariant::StringList:
1722 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1723 jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1731 if ( !textOffset.isNull() )
1734 labelSettings.
xOffset = textOffset.x();
1735 labelSettings.
yOffset = textOffset.y();
1740 if ( jsonLayout.contains( QStringLiteral(
"icon-image" ) ) &&
1744 QString spriteProperty, spriteSizeProperty;
1745 const QString sprite =
retrieveSpriteAsBase64( jsonLayout.value( QStringLiteral(
"icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
1746 if ( !sprite.isEmpty() )
1749 markerLayer->
setPath( sprite );
1750 markerLayer->
setSize( spriteSize.width() );
1753 if ( !spriteProperty.isEmpty() )
1765 backgroundSettings.
setSize( spriteSize );
1773 if ( textSize >= 0 )
1796 if ( !jsonLayer.contains( QStringLiteral(
"layout" ) ) )
1798 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
1801 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
1803 if ( jsonLayout.value( QStringLiteral(
"symbol-placement" ) ).toString() == QLatin1String(
"line" ) && !jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
1807 double spacing = -1.0;
1808 if ( jsonLayout.contains( QStringLiteral(
"symbol-spacing" ) ) )
1810 const QVariant jsonSpacing = jsonLayout.
value( QStringLiteral(
"symbol-spacing" ) );
1811 switch ( jsonSpacing.type() )
1814 case QVariant::Double:
1822 case QVariant::List:
1823 case QVariant::StringList:
1838 bool rotateMarkers =
true;
1839 if ( jsonLayout.contains( QStringLiteral(
"icon-rotation-alignment" ) ) )
1841 const QString alignment = jsonLayout.value( QStringLiteral(
"icon-rotation-alignment" ) ).toString();
1842 if ( alignment == QLatin1String(
"map" ) || alignment == QLatin1String(
"auto" ) )
1844 rotateMarkers =
true;
1846 else if ( alignment == QLatin1String(
"viewport" ) )
1848 rotateMarkers =
false;
1853 double rotation = 0.0;
1854 if ( jsonLayout.contains( QStringLiteral(
"icon-rotate" ) ) )
1856 const QVariant jsonIconRotate = jsonLayout.
value( QStringLiteral(
"icon-rotate" ) );
1857 switch ( jsonIconRotate.type() )
1860 case QVariant::Double:
1861 rotation = jsonIconRotate.toDouble();
1868 case QVariant::List:
1869 case QVariant::StringList:
1890 QString spriteProperty, spriteSizeProperty;
1891 const QString sprite =
retrieveSpriteAsBase64( jsonLayout.value( QStringLiteral(
"icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
1892 if ( !sprite.isNull() )
1894 markerLayer->
setPath( sprite );
1895 markerLayer->
setSize( spriteSize.width() );
1898 if ( !spriteProperty.isEmpty() )
1905 if ( jsonLayout.contains( QStringLiteral(
"icon-size" ) ) )
1907 const QVariant jsonIconSize = jsonLayout.value( QStringLiteral(
"icon-size" ) );
1910 switch ( jsonIconSize.type() )
1913 case QVariant::Double:
1915 size = jsonIconSize.toDouble();
1916 if ( !spriteSizeProperty.isEmpty() )
1919 QgsProperty::fromExpression( QStringLiteral(
"with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
1928 case QVariant::List:
1929 case QVariant::StringList:
1934 markerLayer->
setSize( size * spriteSize.width() );
1937 if ( !spriteSizeProperty.isEmpty() )
1954 std::unique_ptr< QgsSymbol > symbol = std::make_unique< QgsLineSymbol >(
QgsSymbolLayerList() << lineSymbol );
1957 symbol->setOutputUnit( context.
targetUnit() );
1961 rendererStyle.
setSymbol( symbol.release() );
1964 else if ( jsonLayout.contains( QStringLiteral(
"icon-image" ) ) )
1966 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
1969 QString spriteProperty, spriteSizeProperty;
1970 const QString sprite =
retrieveSpriteAsBase64( jsonLayout.value( QStringLiteral(
"icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
1971 if ( !sprite.isEmpty() )
1974 rasterMarker->
setPath( sprite );
1975 rasterMarker->
setSize( spriteSize.width() );
1979 if ( !spriteProperty.isEmpty() )
1985 if ( jsonLayout.contains( QStringLiteral(
"icon-size" ) ) )
1987 const QVariant jsonIconSize = jsonLayout.value( QStringLiteral(
"icon-size" ) );
1990 switch ( jsonIconSize.type() )
1993 case QVariant::Double:
1995 size = jsonIconSize.toDouble();
1996 if ( !spriteSizeProperty.isEmpty() )
1999 QgsProperty::fromExpression( QStringLiteral(
"with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
2008 case QVariant::List:
2009 case QVariant::StringList:
2014 rasterMarker->
setSize( size * spriteSize.width() );
2017 if ( !spriteSizeProperty.isEmpty() )
2030 double rotation = 0.0;
2031 if ( jsonLayout.contains( QStringLiteral(
"icon-rotate" ) ) )
2033 const QVariant jsonIconRotate = jsonLayout.value( QStringLiteral(
"icon-rotate" ) );
2034 switch ( jsonIconRotate.type() )
2037 case QVariant::Double:
2038 rotation = jsonIconRotate.toDouble();
2045 case QVariant::List:
2046 case QVariant::StringList:
2056 double iconOpacity = -1.0;
2057 if ( jsonPaint.contains( QStringLiteral(
"icon-opacity" ) ) )
2059 const QVariant jsonIconOpacity = jsonPaint.value( QStringLiteral(
"icon-opacity" ) );
2060 switch ( jsonIconOpacity.type() )
2063 case QVariant::Double:
2064 iconOpacity = jsonIconOpacity.toDouble();
2071 case QVariant::List:
2072 case QVariant::StringList:
2083 rasterMarker->
setAngle( rotation );
2084 if ( iconOpacity >= 0 )
2088 rendererStyle.
setSymbol( markerSymbol );
2099 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2100 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2101 if ( stops.empty() )
2104 QString caseString = QStringLiteral(
"CASE " );
2105 const QString colorComponent(
"color_part(%1,'%2')" );
2107 for (
int i = 0; i < stops.length() - 1; ++i )
2110 const QString bz = stops.at( i ).toList().value( 0 ).toString();
2112 const QString tz = stops.at( i + 1 ).toList().value( 0 ).toString();
2114 const QVariant bcVariant = stops.at( i ).toList().value( 1 );
2115 const QVariant tcVariant = stops.at( i + 1 ).toList().value( 1 );
2117 const QColor bottomColor =
parseColor( bcVariant.toString(), context );
2118 const QColor topColor =
parseColor( tcVariant.toString(), context );
2120 if ( i == 0 && bottomColor.isValid() )
2127 caseString += QStringLiteral(
"WHEN @vector_tile_zoom < %1 THEN color_hsla(%2, %3, %4, %5) " )
2128 .arg( bz ).arg( bcHue ).arg( bcSat ).arg( bcLight ).arg( bcAlpha );
2131 if ( bottomColor.isValid() && topColor.isValid() )
2143 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2144 "%3, %4, %5, %6) " ).arg( bz, tz,
2155 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2156 "%3, %4, %5, %6) " ).arg( bz, tz,
2157 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"hsl_hue" ), colorComponent.arg( topColorExpr ).arg(
"hsl_hue" ), base, 1, &context ),
2158 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"hsl_saturation" ), colorComponent.arg( topColorExpr ).arg(
"hsl_saturation" ), base, 1, &context ),
2159 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"lightness" ), colorComponent.arg( topColorExpr ).arg(
"lightness" ), base, 1, &context ),
2160 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"alpha" ), colorComponent.arg( topColorExpr ).arg(
"alpha" ), base, 1, &context ) );
2165 const QString tz = stops.last().toList().value( 0 ).toString();
2166 const QVariant tcVariant = stops.last().toList().value( 1 );
2167 const QColor topColor =
parseColor( stops.last().toList().value( 1 ), context );
2168 if ( topColor.isValid() )
2175 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2176 "ELSE color_hsla(%2, %3, %4, %5) END" ).arg( tz ).arg( tcHue ).arg( tcSat ).arg( tcLight ).arg( tcAlpha );
2182 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2183 "ELSE color_hsla(%2, %3, %4, %5) END" ).arg( tz )
2184 .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" ) );
2187 if ( !stops.empty() && defaultColor )
2188 *defaultColor =
parseColor( stops.value( 0 ).toList().value( 1 ).toString(), context );
2195 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2196 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2197 if ( stops.empty() )
2200 QString scaleExpression;
2201 if ( stops.size() <= 2 )
2204 stops.last().toList().value( 0 ).toDouble(),
2205 stops.value( 0 ).toList().value( 1 ),
2206 stops.last().toList().value( 1 ), base, multiplier, &context );
2210 scaleExpression =
parseStops( base, stops, multiplier, context );
2213 if ( !stops.empty() && defaultNumber )
2214 *defaultNumber = stops.value( 0 ).toList().value( 1 ).toDouble() * multiplier;
2224 context = *contextPtr;
2226 const double base = json.value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2227 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2228 if ( stops.empty() )
2231 QString scaleExpression;
2232 if ( stops.length() <= 2 )
2234 const QVariant bv = stops.value( 0 ).toList().value( 1 );
2235 const QVariant tv = stops.last().toList().value( 1 );
2236 double bottom = 0.0;
2238 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2239 scaleExpression = QStringLiteral(
"set_color_part(@symbol_color, 'alpha', %1)" )
2241 stops.last().toList().value( 0 ).toDouble(),
2242 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2243 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ), base, 1, &context ) );
2254 QString caseString = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN set_color_part(@symbol_color, 'alpha', %2)" )
2255 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
2256 .arg( stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity );
2258 for (
int i = 0; i < stops.size() - 1; ++i )
2260 const QVariant bv = stops.value( i ).toList().value( 1 );
2261 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2262 double bottom = 0.0;
2264 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2266 caseString += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
2267 "THEN set_color_part(@symbol_color, 'alpha', %3)" )
2268 .arg( stops.value( i ).toList().value( 0 ).toString(),
2269 stops.value( i + 1 ).toList().value( 0 ).toString(),
2271 stops.value( i + 1 ).toList().value( 0 ).toDouble(),
2272 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2273 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ),
2274 base, 1, &context ) );
2277 caseString += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
2278 "THEN set_color_part(@symbol_color, 'alpha', %2) END" )
2279 .arg( stops.last().toList().value( 0 ).toString() )
2280 .arg( stops.last().toList().value( 1 ).toDouble() * maxOpacity );
2286 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2287 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2288 if ( stops.empty() )
2291 QString scaleExpression;
2292 if ( stops.size() <= 2 )
2294 scaleExpression = QStringLiteral(
"array(%1,%2)" ).arg(
interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(),
2295 stops.last().toList().value( 0 ).toDouble(),
2296 stops.value( 0 ).toList().value( 1 ).toList().value( 0 ),
2297 stops.last().toList().value( 1 ).toList().value( 0 ), base, multiplier, &context ),
2299 stops.last().toList().value( 0 ).toDouble(),
2300 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ),
2301 stops.last().toList().value( 1 ).toList().value( 1 ), base, multiplier, &context )
2306 scaleExpression =
parsePointStops( base, stops, context, multiplier );
2309 if ( !stops.empty() && defaultPoint )
2310 *defaultPoint = QPointF( stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble() * multiplier,
2311 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble() * multiplier );
2317 const QVariantMap &conversionMap, QString *defaultString )
2319 const QVariantList stops = json.
value( QStringLiteral(
"stops" ) ).toList();
2320 if ( stops.empty() )
2323 const QString scaleExpression =
parseStringStops( stops, context, conversionMap, defaultString );
2330 QString caseString = QStringLiteral(
"CASE " );
2332 for (
int i = 0; i < stops.length() - 1; ++i )
2335 const QVariant bz = stops.value( i ).toList().value( 0 );
2336 const QVariant bv = stops.value( i ).toList().value( 1 );
2337 if ( bv.type() != QVariant::List && bv.type() != QVariant::StringList )
2344 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2345 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2346 if ( tv.type() != QVariant::List && tv.type() != QVariant::StringList )
2352 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2353 "THEN array(%3,%4)" ).arg( bz.toString(),
2355 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 0 ), tv.toList().value( 0 ), base, multiplier, &context ),
2356 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 1 ), tv.toList().value( 1 ), base, multiplier, &context ) );
2358 caseString += QLatin1String(
"END" );
2364 if ( stops.length() < 2 )
2367 QString caseString = QStringLiteral(
"CASE " );
2369 for (
int i = 0; i < stops.length() - 1; ++i )
2372 const QVariant bz = stops.value( i ).toList().value( 0 );
2373 const QList<QVariant> bv = stops.value( i ).toList().value( 1 ).toList();
2376 for (
const QVariant &value : bv )
2378 const double number = value.toDouble( &ok );
2380 bl << QString::number( number * multiplier );
2384 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2385 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2386 "THEN array(%3) " ).arg( bz.toString(),
2390 const QVariant lz = stops.value( stops.length() - 1 ).toList().value( 0 );
2391 const QList<QVariant> lv = stops.value( stops.length() - 1 ).toList().value( 1 ).toList();
2394 for (
const QVariant &value : lv )
2396 const double number = value.toDouble( &ok );
2398 ll << QString::number( number * multiplier );
2400 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 "
2401 "THEN array(%2) " ).arg( lz.toString(),
2403 caseString += QLatin1String(
"END" );
2409 QString caseString = QStringLiteral(
"CASE " );
2411 for (
int i = 0; i < stops.length() - 1; ++i )
2414 const QVariant bz = stops.value( i ).toList().value( 0 );
2415 const QVariant bv = stops.value( i ).toList().value( 1 );
2416 if ( bz.type() == QVariant::List || bz.type() == QVariant::StringList )
2418 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2423 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2424 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2425 if ( tz.type() == QVariant::List || tz.type() == QVariant::StringList )
2427 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2431 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2432 "THEN %3 " ).arg( bz.toString(),
2437 const QVariant z = stops.last().toList().value( 0 );
2438 const QVariant v = stops.last().toList().value( 1 );
2439 QString vStr = v.toString();
2440 if ( ( QMetaType::Type )v.type() == QMetaType::QVariantList )
2443 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 "
2444 "THEN ( ( %2 ) * %3 ) END" ).arg( z.toString() ).arg( vStr ).arg( multiplier );
2448 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 "
2449 "THEN %2 END" ).arg( z.toString() ).arg( v.toDouble() * multiplier );
2457 QString caseString = QStringLiteral(
"CASE " );
2459 for (
int i = 0; i < stops.length() - 1; ++i )
2462 const QVariant bz = stops.value( i ).toList().value( 0 );
2463 const QString bv = stops.value( i ).toList().value( 1 ).toString();
2464 if ( bz.type() == QVariant::List || bz.type() == QVariant::StringList )
2466 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2471 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2472 if ( tz.type() == QVariant::List || tz.type() == QVariant::StringList )
2474 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2478 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2479 "THEN %3 " ).arg( bz.toString(),
2483 caseString += QStringLiteral(
"ELSE %1 END" ).arg(
QgsExpression::quotedValue( conversionMap.value( stops.constLast().toList().value( 1 ).toString(),
2484 stops.constLast().toList().value( 1 ) ) ) );
2485 if ( defaultString )
2486 *defaultString = stops.constLast().toList().value( 1 ).toString();
2492 const QString method = json.
value( 0 ).toString();
2493 if ( method == QLatin1String(
"interpolate" ) )
2497 else if ( method == QLatin1String(
"match" ) )
2499 return parseMatchList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
2509 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
2510 if ( attribute.isEmpty() )
2512 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
2516 QString caseString = QStringLiteral(
"CASE " );
2518 for (
int i = 2; i < json.length() - 1; i += 2 )
2520 const QVariantList keys = json.value( i ).toList();
2522 QStringList matchString;
2523 for (
const QVariant &key : keys )
2528 const QVariant value = json.value( i + 1 );
2530 QString valueString;
2535 const QColor color =
parseColor( value, context );
2542 const double v = value.toDouble() * multiplier;
2543 valueString = QString::number( v );
2549 const double v = value.toDouble() * maxOpacity;
2550 valueString = QString::number( v );
2556 valueString = QStringLiteral(
"array(%1,%2)" ).arg( value.toList().value( 0 ).toDouble() * multiplier,
2557 value.toList().value( 0 ).toDouble() * multiplier );
2563 caseString += QStringLiteral(
"WHEN %1 IN (%2) THEN %3 " ).arg( attribute,
2564 matchString.join(
',' ), valueString );
2573 const QColor color =
parseColor( json.constLast(), context );
2575 *defaultColor = color;
2583 const double v = json.constLast().toDouble() * multiplier;
2584 if ( defaultNumber )
2586 elseValue = QString::number( v );
2592 const double v = json.constLast().toDouble() * maxOpacity;
2593 if ( defaultNumber )
2595 elseValue = QString::number( v );
2601 elseValue = QStringLiteral(
"array(%1,%2)" ).arg( json.constLast().toList().value( 0 ).toDouble() * multiplier,
2602 json.constLast().toList().value( 0 ).toDouble() * multiplier );
2608 caseString += QStringLiteral(
"ELSE %1 END" ).arg( elseValue );
2614 if ( json.value( 0 ).toString() != QLatin1String(
"interpolate" ) )
2616 context.
pushWarning( QObject::tr(
"%1: Could not interpret value list" ).arg( context.
layerId() ) );
2621 const QString technique = json.value( 1 ).toList().value( 0 ).toString();
2622 if ( technique == QLatin1String(
"linear" ) )
2624 else if ( technique == QLatin1String(
"exponential" ) )
2625 base = json.value( 1 ).toList(). value( 1 ).toDouble();
2626 else if ( technique == QLatin1String(
"cubic-bezier" ) )
2628 context.
pushWarning( QObject::tr(
"%1: Cubic-bezier interpolation is not supported, linear used instead." ).arg( context.
layerId() ) );
2633 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation method %2" ).arg( context.
layerId(), technique ) );
2637 if ( json.value( 2 ).toList().value( 0 ).toString() != QLatin1String(
"zoom" ) )
2639 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation input %2" ).arg( context.
layerId(), json.value( 2 ).toString() ) );
2645 for (
int i = 3; i < json.length(); i += 2 )
2647 stops.push_back( QVariantList() << json.value( i ).toString() << json.value( i + 1 ) );
2651 props.insert( QStringLiteral(
"stops" ), stops );
2652 props.insert( QStringLiteral(
"base" ), base );
2655 case PropertyType::Color:
2658 case PropertyType::Numeric:
2661 case PropertyType::Opacity:
2664 case PropertyType::Point:
2672 if ( ( QMetaType::Type )colorExpression.type() == QMetaType::QVariantList )
2676 return parseValue( colorExpression, context,
true );
2681 if ( color.type() != QVariant::String )
2683 context.
pushWarning( QObject::tr(
"%1: Could not parse non-string color %2, skipping" ).arg( context.
layerId(), color.toString() ) );
2692 hue = std::max( 0, color.hslHue() );
2693 saturation = color.hslSaturation() / 255.0 * 100;
2694 lightness = color.lightness() / 255.0 * 100;
2695 alpha = color.alpha();
2703 context = *contextPtr;
2707 if ( valueMin.canConvert( QMetaType::Double ) && valueMax.canConvert( QMetaType::Double ) )
2709 bool minDoubleOk =
true;
2710 const double min = valueMin.toDouble( &minDoubleOk );
2711 bool maxDoubleOk =
true;
2712 const double max = valueMax.toDouble( &maxDoubleOk );
2713 if ( minDoubleOk && maxDoubleOk &&
qgsDoubleNear( min, max ) )
2715 return QString::number( min * multiplier );
2719 QString minValueExpr = valueMin.toString();
2720 QString maxValueExpr = valueMax.toString();
2721 if ( ( QMetaType::Type )valueMin.type() == QMetaType::QVariantList )
2725 if ( ( QMetaType::Type )valueMax.type() == QMetaType::QVariantList )
2730 if ( minValueExpr == maxValueExpr )
2732 return minValueExpr;
2738 expression = QStringLiteral(
"scale_linear(@vector_tile_zoom,%1,%2,%3,%4)" ).arg( zoomMin )
2740 .arg( minValueExpr )
2741 .arg( maxValueExpr );
2745 expression = QStringLiteral(
"scale_exp(@vector_tile_zoom,%1,%2,%3,%4,%5)" ).arg( zoomMin )
2747 .arg( minValueExpr )
2748 .arg( maxValueExpr )
2752 if ( multiplier != 1 )
2753 return QStringLiteral(
"%1 * %2" ).arg( expression ).arg( multiplier );
2760 if ( style == QLatin1String(
"round" ) )
2761 return Qt::RoundCap;
2762 else if ( style == QLatin1String(
"square" ) )
2763 return Qt::SquareCap;
2770 if ( style == QLatin1String(
"bevel" ) )
2771 return Qt::BevelJoin;
2772 else if ( style == QLatin1String(
"round" ) )
2773 return Qt::RoundJoin;
2775 return Qt::MiterJoin;
2780 QString op = expression.value( 0 ).toString();
2781 if ( op == QLatin1String(
"%" ) && expression.size() >= 3 )
2783 return QStringLiteral(
"%1 %2 %3" ).arg( parseValue( expression.value( 1 ), context ) ).arg( op ).arg( parseValue( expression.value( 2 ), context ) );
2785 else if ( op == QLatin1String(
"to-number" ) )
2787 return QStringLiteral(
"to_real(%1)" ).arg( parseValue( expression.value( 1 ), context ) );
2789 if ( op == QLatin1String(
"literal" ) )
2791 return expression.value( 1 ).toString();
2793 else if ( op == QLatin1String(
"all" )
2794 || op == QLatin1String(
"any" )
2795 || op == QLatin1String(
"none" ) )
2798 for (
int i = 1; i < expression.size(); ++i )
2800 const QString part = parseValue( expression.at( i ), context );
2801 if ( part.isEmpty() )
2803 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
2809 if ( op == QLatin1String(
"none" ) )
2810 return QStringLiteral(
"NOT (%1)" ).arg( parts.join( QLatin1String(
") AND NOT (" ) ) );
2812 QString operatorString;
2813 if ( op == QLatin1String(
"all" ) )
2814 operatorString = QStringLiteral(
") AND (" );
2815 else if ( op == QLatin1String(
"any" ) )
2816 operatorString = QStringLiteral(
") OR (" );
2818 return QStringLiteral(
"(%1)" ).arg( parts.join( operatorString ) );
2820 else if ( op ==
'!' )
2823 QVariantList contraJsonExpr = expression.value( 1 ).toList();
2824 contraJsonExpr[0] = QString( op + contraJsonExpr[0].toString() );
2826 return parseKey( contraJsonExpr, context );
2828 else if ( op == QLatin1String(
"==" )
2829 || op == QLatin1String(
"!=" )
2830 || op == QLatin1String(
">=" )
2832 || op == QLatin1String(
"<=" )
2836 if ( op == QLatin1String(
"==" ) )
2837 op = QStringLiteral(
"IS" );
2838 else if ( op == QLatin1String(
"!=" ) )
2839 op = QStringLiteral(
"IS NOT" );
2840 return QStringLiteral(
"%1 %2 %3" ).arg( parseKey( expression.value( 1 ), context ),
2841 op, parseValue( expression.value( 2 ), context ) );
2843 else if ( op == QLatin1String(
"has" ) )
2845 return parseKey( expression.value( 1 ), context ) + QStringLiteral(
" IS NOT NULL" );
2847 else if ( op == QLatin1String(
"!has" ) )
2849 return parseKey( expression.value( 1 ), context ) + QStringLiteral(
" IS NULL" );
2851 else if ( op == QLatin1String(
"in" ) || op == QLatin1String(
"!in" ) )
2853 const QString key = parseKey( expression.value( 1 ), context );
2855 for (
int i = 2; i < expression.size(); ++i )
2857 const QString part = parseValue( expression.at( i ), context );
2858 if ( part.isEmpty() )
2860 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
2865 if ( op == QLatin1String(
"in" ) )
2866 return QStringLiteral(
"%1 IN (%2)" ).arg( key, parts.join( QLatin1String(
", " ) ) );
2868 return QStringLiteral(
"(%1 IS NULL OR %1 NOT IN (%2))" ).arg( key, parts.join( QLatin1String(
", " ) ) );
2870 else if ( op == QLatin1String(
"get" ) )
2872 return parseKey( expression.value( 1 ), context );
2874 else if ( op == QLatin1String(
"match" ) )
2876 const QString attribute = expression.value( 1 ).toList().value( 1 ).toString();
2878 if ( expression.size() == 5
2879 && expression.at( 3 ).type() == QVariant::Bool && expression.at( 3 ).toBool() ==
true
2880 && expression.at( 4 ).type() == QVariant::Bool && expression.at( 4 ).toBool() ==
false )
2883 if ( expression.at( 2 ).type() == QVariant::List || expression.at( 2 ).type() == QVariant::StringList )
2886 for (
const QVariant &p : expression.at( 2 ).toList() )
2888 parts << parseValue( p, context );
2891 if ( parts.size() > 1 )
2896 else if ( expression.at( 2 ).type() == QVariant::String || expression.at( 2 ).type() == QVariant::Int
2897 || expression.at( 2 ).type() == QVariant::Double )
2903 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
2909 QString caseString = QStringLiteral(
"CASE " );
2910 for (
int i = 2; i < expression.size() - 2; i += 2 )
2912 if ( expression.at( i ).type() == QVariant::List || expression.at( i ).type() == QVariant::StringList )
2915 for (
const QVariant &p : expression.at( i ).toList() )
2920 if ( parts.size() > 1 )
2925 else if ( expression.at( i ).type() == QVariant::String || expression.at( i ).type() == QVariant::Int
2926 || expression.at( i ).type() == QVariant::Double )
2931 caseString += QStringLiteral(
"THEN %1 " ).arg( parseValue( expression.at( i + 1 ), context, colorExpected ) );
2933 caseString += QStringLiteral(
"ELSE %1 END" ).arg( parseValue( expression.last(), context, colorExpected ) );
2937 else if ( op == QLatin1String(
"to-string" ) )
2939 return QStringLiteral(
"to_string(%1)" ).arg(
parseExpression( expression.value( 1 ).toList(), context ) );
2943 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
2952 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
2956 const QVariantMap spriteDefinition = context.
spriteDefinitions().value( name ).toMap();
2957 if ( spriteDefinition.size() == 0 )
2959 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
2963 const QImage sprite = context.
spriteImage().copy( spriteDefinition.value( QStringLiteral(
"x" ) ).toInt(),
2964 spriteDefinition.value( QStringLiteral(
"y" ) ).toInt(),
2965 spriteDefinition.value( QStringLiteral(
"width" ) ).toInt(),
2966 spriteDefinition.value( QStringLiteral(
"height" ) ).toInt() );
2967 if ( sprite.isNull() )
2969 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
2973 spriteSize = sprite.size() / spriteDefinition.value( QStringLiteral(
"pixelRatio" ) ).toDouble() * context.
pixelSizeConversionFactor();
2981 auto prepareBase64 = [](
const QImage & sprite )
2984 if ( !sprite.isNull() )
2987 QBuffer buffer( &blob );
2988 buffer.open( QIODevice::WriteOnly );
2989 sprite.save( &buffer,
"PNG" );
2991 const QByteArray encoded = blob.toBase64();
2992 path = QString( encoded );
2993 path.prepend( QLatin1String(
"base64:" ) );
2998 switch ( value.type() )
3000 case QVariant::String:
3002 QString spriteName = value.toString();
3003 const QRegularExpression fieldNameMatch( QStringLiteral(
"{([^}]+)}" ) );
3004 QRegularExpressionMatch match = fieldNameMatch.match( spriteName );
3005 if ( match.hasMatch() )
3007 const QString fieldName = match.captured( 1 );
3008 spriteProperty = QStringLiteral(
"CASE" );
3009 spriteSizeProperty = QStringLiteral(
"CASE" );
3011 spriteName.replace(
"(", QLatin1String(
"\\(" ) );
3012 spriteName.replace(
")", QLatin1String(
"\\)" ) );
3013 spriteName.replace( fieldNameMatch, QStringLiteral(
"([^\\/\\\\]+)" ) );
3014 const QRegularExpression fieldValueMatch( spriteName );
3016 for (
const QString &name : spriteNames )
3018 match = fieldValueMatch.match( name );
3019 if ( match.hasMatch() )
3023 const QString fieldValue = match.captured( 1 );
3025 path = prepareBase64( sprite );
3026 if ( spritePath.isEmpty() && !path.isEmpty() )
3032 spriteProperty += QStringLiteral(
" WHEN \"%1\" = '%2' THEN '%3'" )
3033 .arg( fieldName, fieldValue, path );
3034 spriteSizeProperty += QStringLiteral(
" WHEN \"%1\" = '%2' THEN %3" )
3035 .arg( fieldName ).arg( fieldValue ).arg( size.width() );
3039 spriteProperty += QLatin1String(
" END" );
3040 spriteSizeProperty += QLatin1String(
" END" );
3044 spriteProperty.clear();
3045 spriteSizeProperty.clear();
3046 const QImage sprite =
retrieveSprite( spriteName, context, spriteSize );
3047 spritePath = prepareBase64( sprite );
3054 const QVariantList stops = value.toMap().value( QStringLiteral(
"stops" ) ).toList();
3055 if ( stops.size() == 0 )
3062 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, spriteSize );
3063 spritePath = prepareBase64( sprite );
3065 spriteProperty = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN '%2'" )
3066 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
3068 spriteSizeProperty = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN %2" )
3069 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
3070 .arg( spriteSize.width() );
3072 for (
int i = 0; i < stops.size() - 1; ++i )
3075 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, size );
3076 path = prepareBase64( sprite );
3078 spriteProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
3080 .arg( stops.value( i ).toList().value( 0 ).toString(),
3081 stops.value( i + 1 ).toList().value( 0 ).toString(),
3083 spriteSizeProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
3085 .arg( stops.value( i ).toList().value( 0 ).toString(),
3086 stops.value( i + 1 ).toList().value( 0 ).toString() )
3087 .arg( size.width() );
3089 sprite =
retrieveSprite( stops.last().toList().value( 1 ).toString(), context, size );
3090 path = prepareBase64( sprite );
3092 spriteProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
3094 .arg( stops.last().toList().value( 0 ).toString() )
3096 spriteSizeProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
3098 .arg( stops.last().toList().value( 0 ).toString() )
3099 .arg( size.width() );
3103 case QVariant::List:
3105 const QVariantList json = value.toList();
3106 const QString method = json.value( 0 ).toString();
3107 if ( method != QLatin1String(
"match" ) )
3109 context.
pushWarning( QObject::tr(
"%1: Could not interpret sprite value list with method %2" ).arg( context.
layerId(), method ) );
3113 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
3114 if ( attribute.isEmpty() )
3116 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
3120 spriteProperty = QStringLiteral(
"CASE " );
3121 spriteSizeProperty = QStringLiteral(
"CASE " );
3123 for (
int i = 2; i < json.length() - 1; i += 2 )
3125 const QVariantList keys = json.value( i ).toList();
3127 QStringList matchString;
3128 for (
const QVariant &key : keys )
3133 const QVariant value = json.value( i + 1 );
3135 const QImage sprite =
retrieveSprite( value.toString(), context, spriteSize );
3136 spritePath = prepareBase64( sprite );
3138 spriteProperty += QStringLiteral(
" WHEN %1 IN (%2) "
3139 "THEN '%3' " ).arg( attribute,
3140 matchString.join(
',' ),
3143 spriteSizeProperty += QStringLiteral(
" WHEN %1 IN (%2) "
3144 "THEN %3 " ).arg( attribute,
3145 matchString.join(
',' ) ).arg( spriteSize.width() );
3148 const QImage sprite =
retrieveSprite( json.constLast().toString(), context, spriteSize );
3149 spritePath = prepareBase64( sprite );
3151 spriteProperty += QStringLiteral(
"ELSE %1 END" ).arg( spritePath );
3152 spriteSizeProperty += QStringLiteral(
"ELSE %3 END" ).arg( spriteSize.width() );
3167 switch ( value.type() )
3169 case QVariant::List:
3170 case QVariant::StringList:
3173 case QVariant::Bool:
3174 case QVariant::String:
3175 if ( colorExpected )
3180 return parseValue(
c, context );
3186 case QVariant::Double:
3187 return value.toString();
3189 case QVariant::Color:
3190 c = value.value<QColor>();
3191 return QString(
"color_rgba(%1,%2,%3,%4)" ).arg(
c.red() ).arg(
c.green() ).arg(
c.blue() ).arg(
c.alpha() );
3194 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression part" ).arg( context.
layerId() ) );
3202 if ( value.toString() == QLatin1String(
"$type" ) )
3204 return QStringLiteral(
"_geom_type" );
3206 if ( value.toString() == QLatin1String(
"level" ) )
3208 return QStringLiteral(
"level" );
3210 else if ( ( value.type() == QVariant::List && value.toList().size() == 1 ) || value.type() == QVariant::StringList )
3212 if ( value.toList().size() > 1 )
3213 return value.toList().at( 1 ).toString();
3216 QString valueString = value.toList().value( 0 ).toString();
3217 if ( valueString == QLatin1String(
"geometry-type" ) )
3219 return QStringLiteral(
"_geom_type" );
3224 else if ( value.type() == QVariant::List && value.toList().size() > 1 )
3233 return mRenderer ? mRenderer->clone() :
nullptr;
3238 return mLabeling ? mLabeling->clone() :
nullptr;
3241 bool QgsMapBoxGlStyleConverter::numericArgumentsOnly(
const QVariant &bottomVariant,
const QVariant &topVariant,
double &bottom,
double &top )
3243 if ( bottomVariant.canConvert( QMetaType::Double ) && topVariant.canConvert( QMetaType::Double ) )
3245 bool bDoubleOk, tDoubleOk;
3246 bottom = bottomVariant.toDouble( &bDoubleOk );
3247 top = topVariant.toDouble( &tDoubleOk );
3248 return ( bDoubleOk && tDoubleOk );
3259 mWarnings << warning;
3274 return mSizeConversionFactor;
3279 mSizeConversionFactor = sizeConversionFactor;
3284 return mSpriteImage;
3289 return mSpriteDefinitions;
3294 mSpriteImage = image;
3295 mSpriteDefinitions = definitions;
@ CentralPoint
Place symbols at the mid point of the line.
@ Viewport
Relative to the whole viewport/output device.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the pen join style used to render the line (e.g.
void setPenCapStyle(Qt::PenCapStyle style)
Sets the pen cap style used to render the line (e.g.
A paint effect which blurs a source picture, using a number of different blur methods.
@ StackBlur
Stack blur, a fast but low quality blur. Valid blur level values are between 0 - 16.
void setBlurUnit(const QgsUnitTypes::RenderUnit unit)
Sets the units used for the blur level (radius).
void setBlurMethod(const BlurMethod method)
Sets the blur method (algorithm) to use for performing the blur.
void setBlurLevel(const double level)
Sets blur level (radius)
A paint effect which consists of a stack of other chained paint effects.
void appendEffect(QgsPaintEffect *effect)
Appends an effect to the end of the stack.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QVariant::Type fieldType=QVariant::Type::Invalid)
Create an expression allowing to evaluate if a field is equal to a value.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
static bool fontFamilyHasStyle(const QString &family, const QString &style)
Check whether font family on system has specific style.
static QVariant parseJson(const std::string &jsonString)
Converts JSON jsonString to a QVariant, in case of parsing error an invalid QVariant is returned and ...
void setPlacementFlags(QgsLabeling::LinePlacementFlags flags)
Returns the line placement flags, which dictate how line labels can be placed above or below the line...
void setFactor(double factor)
Sets the obstacle factor, where 1.0 = default, < 1.0 more likely to be covered by labels,...
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
@ OnLine
Labels can be placed directly over a line feature.
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
virtual void setWidth(double width)
Sets the width of the line symbol layer.
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit for the line's offset.
void setOffset(double offset)
Sets the line's offset.
Context for a MapBox GL style conversion operation.
void setLayerId(const QString &value)
Sets the layer ID of the layer currently being converted.
QStringList warnings() const
Returns a list of warning messages generated during the conversion.
void pushWarning(const QString &warning)
Pushes a warning message generated during the conversion.
double pixelSizeConversionFactor() const
Returns the pixel size conversion factor, used to scale the original pixel sizes when converting styl...
QgsUnitTypes::RenderUnit targetUnit() const
Returns the target unit type.
void setPixelSizeConversionFactor(double sizeConversionFactor)
Sets the pixel size conversion factor, used to scale the original pixel sizes when converting styles.
void setSprites(const QImage &image, const QVariantMap &definitions)
Sets the sprite image and definitions JSON to use during conversion.
QString layerId() const
Returns the layer ID of the layer currently being converted.
void setTargetUnit(QgsUnitTypes::RenderUnit targetUnit)
Sets the target unit type.
QImage spriteImage() const
Returns the sprite image to use during conversion, or an invalid image if this is not set.
void clearWarnings()
Clears the list of warning messages.
QVariantMap spriteDefinitions() const
Returns the sprite definitions to use during conversion.
static QString parseOpacityStops(double base, const QVariantList &stops, int maxOpacity, QgsMapBoxGlStyleConversionContext &context)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate alpha ...
static QString parseColorExpression(const QVariant &colorExpression, QgsMapBoxGlStyleConversionContext &context)
Converts an expression representing a color to a string (can be color string or an expression where a...
static QString parseStops(double base, const QVariantList &stops, double multiplier, QgsMapBoxGlStyleConversionContext &context)
Parses a list of interpolation stops.
QgsVectorTileRenderer * renderer() const
Returns a new instance of a vector tile renderer representing the converted style,...
static QString parseExpression(const QVariantList &expression, QgsMapBoxGlStyleConversionContext &context, bool colorExpected=false)
Converts a MapBox GL expression to a QGIS expression.
PropertyType
Property types, for interpolated value conversion.
@ Numeric
Numeric property (e.g. line width, text size)
@ Opacity
Opacity property.
@ Point
Point/offset property.
QgsVectorTileLabeling * labeling() const
Returns a new instance of a vector tile labeling representing the converted style,...
static QgsProperty parseInterpolateByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, double *defaultNumber=nullptr)
Parses a numeric value which is interpolated by zoom range.
static Qt::PenJoinStyle parseJoinStyle(const QString &style)
Converts a value to Qt::PenJoinStyle enum from JSON value.
static QgsProperty parseInterpolateStringByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, const QVariantMap &conversionMap, QString *defaultString=nullptr)
Interpolates a string by zoom.
static QString interpolateExpression(double zoomMin, double zoomMax, QVariant valueMin, QVariant valueMax, double base, double multiplier=1, QgsMapBoxGlStyleConversionContext *contextPtr=0)
Generates an interpolation for values between valueMin and valueMax, scaled between the ranges zoomMi...
static QgsProperty parseInterpolatePointByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, QPointF *defaultPoint=nullptr)
Interpolates a point/offset with either scale_linear() or scale_exp() (depending on base value).
static bool parseCircleLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context)
Parses a circle layer.
Result convert(const QVariantMap &style, QgsMapBoxGlStyleConversionContext *context=nullptr)
Converts a JSON style map, and returns the resultant status of the conversion.
static QgsProperty parseInterpolateListByZoom(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Interpolates a list which starts with the interpolate function.
~QgsMapBoxGlStyleConverter()
Result
Result of conversion.
@ Success
Conversion was successful.
@ NoLayerList
No layer list was found in JSON input.
QgsMapBoxGlStyleConverter()
Constructor for QgsMapBoxGlStyleConverter.
static QImage retrieveSprite(const QString &name, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize)
Retrieves the sprite image with the specified name, taken from the specified context.
void parseLayers(const QVariantList &layers, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse list of layers from JSON.
static QgsProperty parseInterpolateColorByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, QColor *defaultColor=nullptr)
Parses a color value which is interpolated by zoom range.
static QgsProperty parseInterpolateOpacityByZoom(const QVariantMap &json, int maxOpacity, QgsMapBoxGlStyleConversionContext *contextPtr=0)
Interpolates opacity with either scale_linear() or scale_exp() (depending on base value).
static QString retrieveSpriteAsBase64(const QVariant &value, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize, QString &spriteProperty, QString &spriteSizeProperty)
Retrieves the sprite image with the specified name, taken from the specified context as a base64 enco...
static QColor parseColor(const QVariant &color, QgsMapBoxGlStyleConversionContext &context)
Parses a color in one of these supported formats:
static bool parseSymbolLayerAsRenderer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as a renderer.
static bool parseFillLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context, bool isBackgroundStyle=false)
Parses a fill layer.
static void parseSymbolLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, bool &hasRenderer, QgsVectorTileBasicLabelingStyle &labelingStyle, bool &hasLabeling, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as renderer or labeling.
static bool parseLineLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context)
Parses a line layer.
static void colorAsHslaComponents(const QColor &color, int &hue, int &saturation, int &lightness, int &alpha)
Takes a QColor object and returns HSLA components in required format for QGIS color_hsla() expression...
static Qt::PenCapStyle parseCapStyle(const QString &style)
Converts a value to Qt::PenCapStyle enum from JSON value.
static QString parseStringStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, const QVariantMap &conversionMap, QString *defaultString=nullptr)
Parses a list of interpolation stops containing string values.
static QgsProperty parseMatchList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a match function value list.
static QgsProperty parseValueList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a value list (e.g.
static QString parseArrayStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier=1)
Takes numerical arrays from stops.
static QString parsePointStops(double base, const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier=1)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate point/...
Line symbol layer type which draws repeating marker symbols along a line feature.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
virtual void setSize(double size)
Sets the symbol size.
void setAngle(double angle)
Sets the rotation angle for the marker.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol's size.
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol's offset.
A marker symbol type, for rendering Point and MultiPoint geometries.
void setEnabled(bool enabled)
Sets whether the effect is enabled.
Contains settings for how a map layer will be labeled.
double yOffset
Vertical offset of label.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
double xOffset
Horizontal offset of label.
QuadrantPosition quadOffset
Sets the quadrant in which to offset labels from feature.
QgsUnitTypes::RenderUnit offsetUnits
Units for offsets of label.
@ Curved
Arranges candidates following the curvature of a line feature. Applies to line layers only.
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
int priority
Label priority.
QgsUnitTypes::RenderUnit distUnits
Units the distance from feature to the label.
MultiLineAlign multilineAlign
Horizontal alignment of multi-line labels.
@ FontStyle
Font style name.
@ FontLetterSpacing
Letter spacing.
@ LinePlacementOptions
Line placement flags.
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
const QgsLabelObstacleSettings & obstacleSettings() const
Returns the label obstacle settings.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the label's property collection, used for data defined overrides.
bool isExpression
true if this label is made from a expression string, e.g., FieldName || 'mm'
double dist
Distance from feature to the label.
QString fieldName
Name of field (or an expression) to use for label text.
int autoWrapLength
If non-zero, indicates that label text should be automatically wrapped to (ideally) the specified num...
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection.
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
A store for object properties.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
QString expressionString() const
Returns the expression used for the property value.
QVariant value(const QgsExpressionContext &context, const QVariant &defaultValue=QVariant(), bool *ok=nullptr) const
Calculates the current value of the property, including any transforms which are set for the property...
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
A class for filling symbols with a repeated raster image.
void setWidthUnit(const QgsUnitTypes::RenderUnit unit)
Sets the units for the image's width.
void setWidth(const double width)
Sets the width for scaling the image used in the fill.
void setOpacity(double opacity)
Sets the opacity for the raster image used in the fill.
void setImageFilePath(const QString &imagePath)
Sets the path to the raster image used for the fill.
void setCoordinateMode(Qgis::SymbolCoordinateReference mode)
Set the coordinate mode for fill.
Line symbol layer type which draws line sections using a raster image file.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Raster marker symbol layer class.
void setOpacity(double opacity)
Set the marker opacity.
void setPath(const QString &path)
Set the marker raster image path.
void setBrushStyle(Qt::BrushStyle style)
void setStrokeStyle(Qt::PenStyle strokeStyle)
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
void setOffset(QPointF offset)
Sets an offset by which polygons will be translated during rendering.
void setStrokeColor(const QColor &strokeColor) override
Sets the stroke color for the symbol layer.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit for the fill's offset.
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
void setPenCapStyle(Qt::PenCapStyle style)
Sets the pen cap style used to render the line (e.g.
void setUseCustomDashPattern(bool b)
Sets whether the line uses a custom dash pattern.
void setCustomDashVector(const QVector< qreal > &vector)
Sets the custom dash vector, which is the pattern of alternating drawn/skipped lengths used while ren...
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the pen join style used to render the line (e.g.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Simple marker symbol layer, consisting of a rendered shape with solid fill color and an stroke.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit u)
Sets the unit for the width of the marker's stroke.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
void setStrokeWidth(double w)
Sets the width of the marker's stroke.
void setStrokeColor(const QColor &color) override
Sets the marker's stroke color.
static QColor parseColor(const QString &colorStr, bool strictEval=false)
Attempts to parse a string as a color using a variety of common formats, including hex codes,...
@ PropertyFile
Filename, eg for svg files.
@ PropertyAngle
Symbol angle.
@ PropertyCustomDash
Custom dash pattern.
@ PropertyOpacity
Opacity.
@ PropertyOffset
Symbol offset.
@ PropertyStrokeWidth
Stroke width.
@ PropertyFillColor
Fill color.
@ PropertyName
Name, eg shape name for simple markers.
@ PropertyInterval
Line marker interval.
@ PropertyStrokeColor
Stroke color.
@ PropertyWidth
Symbol width.
virtual void setColor(const QColor &color)
Sets the "representative" color for the symbol layer.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the symbol layer's property collection, used for data defined overrides.
@ PropertyOpacity
Opacity.
void setPlacements(Qgis::MarkerLinePlacements placements)
Sets the placement of the symbols.
Container for settings relating to a text background object.
void setMarkerSymbol(QgsMarkerSymbol *symbol)
Sets the current marker symbol for the background shape.
void setSizeType(SizeType type)
Sets the method used to determine the size of the background shape (e.g., fixed size or buffer around...
@ ShapeMarkerSymbol
Marker symbol.
void setType(ShapeType type)
Sets the type of background shape to draw (e.g., square, ellipse, SVG).
void setEnabled(bool enabled)
Sets whether the text background will be drawn.
void setSize(QSizeF size)
Sets the size of the background shape.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the shape's size.
void setColor(const QColor &color)
Sets the color for the buffer.
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the buffer.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the buffer size.
void setSize(double size)
Sets the size of the buffer.
Container for all settings relating to text rendering.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void setSize(double size)
Sets the size for rendered text.
void setFont(const QFont &font)
Sets the font used for rendering text.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the size of rendered text.
void setBackground(const QgsTextBackgroundSettings &backgroundSettings)
Sets the text's background settings.q.
void setNamedStyle(const QString &style)
Sets the named style for the font used for rendering text.
QFont font() const
Returns the font used for rendering text.
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
RenderUnit
Rendering size units.
Configuration of a single style within QgsVectorTileBasicLabeling.
void setGeometryType(QgsWkbTypes::GeometryType geomType)
Sets type of the geometry that will be used (point / line / polygon)
void setLayerName(const QString &name)
Sets name of the sub-layer to render (empty layer means that all layers match)
void setMinZoomLevel(int minZoom)
Sets minimum zoom level index (negative number means no limit)
void setFilterExpression(const QString &expr)
Sets filter expression (empty filter means that all features match)
void setMaxZoomLevel(int maxZoom)
Sets maximum zoom level index (negative number means no limit)
void setStyleName(const QString &name)
Sets human readable name of this style.
void setLabelSettings(const QgsPalLayerSettings &settings)
Sets labeling configuration of this style.
void setEnabled(bool enabled)
Sets whether this style is enabled (used for rendering)
Basic labeling configuration for vector tile layers.
Definition of map rendering of a subset of vector tile data.
void setEnabled(bool enabled)
Sets whether this style is enabled (used for rendering)
void setMinZoomLevel(int minZoom)
Sets minimum zoom level index (negative number means no limit)
void setLayerName(const QString &name)
Sets name of the sub-layer to render (empty layer means that all layers match)
void setGeometryType(QgsWkbTypes::GeometryType geomType)
Sets type of the geometry that will be used (point / line / polygon)
void setFilterExpression(const QString &expr)
Sets filter expression (empty filter means that all features match)
void setSymbol(QgsSymbol *sym)
Sets symbol for rendering. Takes ownership of the symbol.
void setStyleName(const QString &name)
Sets human readable name of this style.
void setMaxZoomLevel(int maxZoom)
Sets maximum zoom level index (negative number means no limit)
The default vector tile renderer implementation.
Base class for labeling configuration classes for vector tile layers.
Abstract base class for all vector tile renderer implementations.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
QList< QgsSymbolLayer * > QgsSymbolLayerList