48#include <QRegularExpression>
51#include "moc_qgsmapboxglstyleconverter.cpp"
53using namespace Qt::StringLiterals;
63 if ( style.contains( u
"sources"_s ) )
65 parseSources( style.value( u
"sources"_s ).toMap(), context );
68 if ( style.contains( u
"layers"_s ) )
70 parseLayers( style.value( u
"layers"_s ).toList(), context );
74 mError = QObject::tr(
"Could not find layers list in JSON" );
87 qDeleteAll( mSources );
92 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
95 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
96 context = tmpContext.get();
99 QList<QgsVectorTileBasicRendererStyle> rendererStyles;
100 QList<QgsVectorTileBasicLabelingStyle> labelingStyles;
103 bool hasRendererBackgroundStyle =
false;
105 for (
const QVariant &layer : layers )
107 const QVariantMap jsonLayer = layer.toMap();
109 const QString layerType = jsonLayer.value( u
"type"_s ).toString();
110 if ( layerType ==
"background"_L1 )
112 hasRendererBackgroundStyle =
parseFillLayer( jsonLayer, rendererBackgroundStyle, *context,
true );
113 if ( hasRendererBackgroundStyle )
123 const QString styleId = jsonLayer.value( u
"id"_s ).toString();
126 if ( layerType.compare(
"raster"_L1, Qt::CaseInsensitive ) == 0 )
129 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
130 if ( jsonPaint.contains( u
"raster-opacity"_s ) )
132 const QVariant jsonRasterOpacity = jsonPaint.value( u
"raster-opacity"_s );
133 double defaultOpacity = 1;
137 mRasterSubLayers.append( raster );
141 const QString layerName = jsonLayer.value( u
"source-layer"_s ).toString();
143 const int minZoom = jsonLayer.value( u
"minzoom"_s, u
"-1"_s ).toInt();
152 int maxZoom = jsonLayer.value( u
"maxzoom"_s, u
"-1"_s ).toInt();
157 if ( jsonLayer.contains( u
"visibility"_s ) )
159 visibilyStr = jsonLayer.value( u
"visibility"_s ).toString();
161 else if ( jsonLayer.contains( u
"layout"_s ) && jsonLayer.value( u
"layout"_s ).userType() == QMetaType::Type::QVariantMap )
163 const QVariantMap jsonLayout = jsonLayer.value( u
"layout"_s ).toMap();
164 visibilyStr = jsonLayout.value( u
"visibility"_s ).toString();
167 const bool enabled = visibilyStr !=
"none"_L1;
169 QString filterExpression;
170 if ( jsonLayer.contains( u
"filter"_s ) )
172 filterExpression =
parseExpression( jsonLayer.value( u
"filter"_s ).toList(), *context );
178 bool hasRendererStyle =
false;
179 bool hasLabelingStyle =
false;
180 if ( layerType ==
"fill"_L1 )
182 hasRendererStyle =
parseFillLayer( jsonLayer, rendererStyle, *context );
184 else if ( layerType ==
"line"_L1 )
186 hasRendererStyle =
parseLineLayer( jsonLayer, rendererStyle, *context );
188 else if ( layerType ==
"circle"_L1 )
192 else if ( layerType ==
"symbol"_L1 )
194 parseSymbolLayer( jsonLayer, rendererStyle, hasRendererStyle, labelingStyle, hasLabelingStyle, *context );
198 mWarnings << QObject::tr(
"%1: Skipping unknown layer type %2" ).arg( context->
layerId(), layerType );
203 if ( hasRendererStyle )
211 rendererStyles.append( rendererStyle );
214 if ( hasLabelingStyle )
222 labelingStyles.append( labelingStyle );
225 mWarnings.append( context->
warnings() );
229 if ( hasRendererBackgroundStyle )
230 rendererStyles.prepend( rendererBackgroundStyle );
232 auto renderer = std::make_unique< QgsVectorTileBasicRenderer >();
233 renderer->setStyles( rendererStyles );
236 auto labeling = std::make_unique< QgsVectorTileBasicLabeling >();
237 labeling->setStyles( labelingStyles );
243 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
248 bool colorIsDataDefined =
false;
250 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsFillSymbol >() );
254 if ( jsonPaint.contains( isBackgroundStyle ? u
"background-color"_s : u
"fill-color"_s ) )
256 const QVariant jsonFillColor = jsonPaint.value( isBackgroundStyle ? u
"background-color"_s : u
"fill-color"_s );
257 switch ( jsonFillColor.userType() )
259 case QMetaType::Type::QVariantMap:
263 case QMetaType::Type::QVariantList:
264 case QMetaType::Type::QStringList:
265 colorIsDataDefined =
true;
269 case QMetaType::Type::QString:
270 fillColor =
parseColor( jsonFillColor.toString(), context );
275 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillColor.userType() ) ) ) );
283 fillColor = QColor( 0, 0, 0 );
286 QColor fillOutlineColor;
287 if ( !isBackgroundStyle )
289 if ( !jsonPaint.contains( u
"fill-outline-color"_s ) )
291 if ( fillColor.isValid() )
292 fillOutlineColor = fillColor;
300 const QVariant jsonFillOutlineColor = jsonPaint.value( u
"fill-outline-color"_s );
301 switch ( jsonFillOutlineColor.userType() )
303 case QMetaType::Type::QVariantMap:
307 case QMetaType::Type::QVariantList:
308 case QMetaType::Type::QStringList:
312 case QMetaType::Type::QString:
313 fillOutlineColor =
parseColor( jsonFillOutlineColor.toString(), context );
318 QObject::tr(
"%1: Skipping unsupported fill-outline-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillOutlineColor.userType() ) ) )
325 double fillOpacity = -1.0;
326 double rasterOpacity = -1.0;
327 if ( jsonPaint.contains( isBackgroundStyle ? u
"background-opacity"_s : u
"fill-opacity"_s ) )
329 const QVariant jsonFillOpacity = jsonPaint.value( isBackgroundStyle ? u
"background-opacity"_s : u
"fill-opacity"_s );
330 switch ( jsonFillOpacity.userType() )
332 case QMetaType::Type::Int:
333 case QMetaType::Type::LongLong:
334 case QMetaType::Type::Double:
335 fillOpacity = jsonFillOpacity.toDouble();
336 rasterOpacity = fillOpacity;
339 case QMetaType::Type::QVariantMap:
352 case QMetaType::Type::QVariantList:
353 case QMetaType::Type::QStringList:
368 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillOpacity.userType() ) ) ) );
374 QPointF fillTranslate;
375 if ( jsonPaint.contains( u
"fill-translate"_s ) )
377 const QVariant jsonFillTranslate = jsonPaint.value( u
"fill-translate"_s );
378 switch ( jsonFillTranslate.userType() )
380 case QMetaType::Type::QVariantMap:
384 case QMetaType::Type::QVariantList:
385 case QMetaType::Type::QStringList:
387 bool isPointValues =
false;
388 const double pointX = jsonFillTranslate.toList().value( 0 ).toDouble( &isPointValues );
402 QObject::tr(
"%1: Skipping unsupported fill-translate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillTranslate.userType() ) ) )
409 Q_ASSERT( fillSymbol );
412 symbol->setOutputUnit( context.
targetUnit() );
415 if ( !fillTranslate.isNull() )
421 if ( jsonPaint.contains( isBackgroundStyle ? u
"background-pattern"_s : u
"fill-pattern"_s ) )
425 const QVariant fillPatternJson = jsonPaint.value( isBackgroundStyle ? u
"background-pattern"_s : u
"fill-pattern"_s );
428 fillColor = QColor();
429 fillOutlineColor = QColor();
436 QString spriteProperty, spriteSizeProperty;
438 if ( !sprite.isEmpty() )
443 rasterFill->
setWidth( spriteSize.width() );
447 if ( rasterOpacity >= 0 )
452 if ( !spriteProperty.isEmpty() )
459 symbol->appendSymbolLayer( rasterFill );
465 if ( fillOpacity != -1 )
467 symbol->setOpacity( fillOpacity );
478 if ( fillOutlineColor.isValid() && ( fillOutlineColor.alpha() == 255 || fillOutlineColor != fillColor ) )
489 if ( fillColor.isValid() )
493 else if ( colorIsDataDefined )
509 if ( !jsonLayer.contains( u
"paint"_s ) )
511 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
516 QString rasterLineSprite;
518 const QVariantMap jsonPaint = jsonLayer.
value( u
"paint"_s ).toMap();
519 if ( jsonPaint.contains( u
"line-pattern"_s ) )
521 const QVariant jsonLinePattern = jsonPaint.value( u
"line-pattern"_s );
522 switch ( jsonLinePattern.userType() )
524 case QMetaType::Type::QVariantMap:
525 case QMetaType::Type::QString:
528 QString spriteProperty, spriteSizeProperty;
534 case QMetaType::Type::QVariantList:
535 case QMetaType::Type::QStringList:
540 if ( rasterLineSprite.isEmpty() )
543 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-pattern property" ).arg( context.
layerId() ) );
550 if ( jsonPaint.contains( u
"line-color"_s ) )
552 const QVariant jsonLineColor = jsonPaint.value( u
"line-color"_s );
553 switch ( jsonLineColor.userType() )
555 case QMetaType::Type::QVariantMap:
560 case QMetaType::Type::QVariantList:
561 case QMetaType::Type::QStringList:
566 case QMetaType::Type::QString:
567 lineColor =
parseColor( jsonLineColor.toString(), context );
571 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineColor.userType() ) ) ) );
578 lineColor = QColor( 0, 0, 0 );
584 if ( jsonPaint.contains( u
"line-width"_s ) )
586 const QVariant jsonLineWidth = jsonPaint.value( u
"line-width"_s );
587 switch ( jsonLineWidth.userType() )
589 case QMetaType::Type::Int:
590 case QMetaType::Type::LongLong:
591 case QMetaType::Type::Double:
595 case QMetaType::Type::QVariantMap:
607 case QMetaType::Type::QVariantList:
608 case QMetaType::Type::QStringList:
620 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineWidth.userType() ) ) ) );
625 double lineOffset = 0.0;
626 if ( jsonPaint.contains( u
"line-offset"_s ) )
628 const QVariant jsonLineOffset = jsonPaint.value( u
"line-offset"_s );
629 switch ( jsonLineOffset.userType() )
631 case QMetaType::Type::Int:
632 case QMetaType::Type::LongLong:
633 case QMetaType::Type::Double:
637 case QMetaType::Type::QVariantMap:
642 case QMetaType::Type::QVariantList:
643 case QMetaType::Type::QStringList:
648 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineOffset.userType() ) ) ) );
653 double lineOpacity = -1.0;
655 if ( jsonPaint.contains( u
"line-opacity"_s ) )
657 const QVariant jsonLineOpacity = jsonPaint.value( u
"line-opacity"_s );
658 switch ( jsonLineOpacity.userType() )
660 case QMetaType::Type::Int:
661 case QMetaType::Type::LongLong:
662 case QMetaType::Type::Double:
663 lineOpacity = jsonLineOpacity.toDouble();
666 case QMetaType::Type::QVariantMap:
669 double defaultValue = 1.0;
678 case QMetaType::Type::QVariantList:
679 case QMetaType::Type::QStringList:
682 double defaultValue = 1.0;
693 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineOpacity.userType() ) ) ) );
698 QVector< double > dashVector;
699 if ( jsonPaint.contains( u
"line-dasharray"_s ) )
701 const QVariant jsonLineDashArray = jsonPaint.value( u
"line-dasharray"_s );
702 switch ( jsonLineDashArray.userType() )
704 case QMetaType::Type::QVariantMap:
706 QString arrayExpression;
709 arrayExpression = u
"array_to_string(array_foreach(%1,@element * (%2)), ';')"_s
714 arrayExpression = u
"array_to_string(%1, ';')"_s.arg(
parseArrayStops( jsonLineDashArray.toMap().value( u
"stops"_s ).toList(), context, lineWidth ) );
718 const QVariantList dashSource = jsonLineDashArray.toMap().value( u
"stops"_s ).toList().first().toList().value( 1 ).toList();
719 for (
const QVariant &v : dashSource )
721 dashVector << v.toDouble() * lineWidth;
726 case QMetaType::Type::QVariantList:
727 case QMetaType::Type::QStringList:
729 const QVariantList dashSource = jsonLineDashArray.toList();
731 if ( !dashSource.empty() )
733 if ( dashSource.at( 0 ).userType() == QMetaType::Type::QString )
739 u
"array_to_string(array_foreach(%1,@element * (%2)), ';')"_s
740 .arg( property.asExpression(), lineWidthProperty.
asExpression() )
751 QVector< double > rawDashVectorSizes;
752 rawDashVectorSizes.reserve( dashSource.size() );
753 for (
const QVariant &v : dashSource )
755 rawDashVectorSizes << v.toDouble();
759 if ( rawDashVectorSizes.size() == 1 )
762 rawDashVectorSizes.clear();
764 else if ( rawDashVectorSizes.size() % 2 == 1 )
768 rawDashVectorSizes.append( 0 );
771 if ( !rawDashVectorSizes.isEmpty() && ( !lineWidthProperty.
asExpression().isEmpty() ) )
773 QStringList dashArrayStringParts;
774 dashArrayStringParts.reserve( rawDashVectorSizes.size() );
775 for (
double v : std::as_const( rawDashVectorSizes ) )
780 QString arrayExpression = u
"array_to_string(array_foreach(array(%1),@element * (%2)), ';')"_s
781 .arg( dashArrayStringParts.join(
',' ), lineWidthProperty.
asExpression() );
786 for (
double v : std::as_const( rawDashVectorSizes ) )
788 dashVector << v * lineWidth;
797 QObject::tr(
"%1: Skipping unsupported line-dasharray type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineDashArray.userType() ) ) )
803 Qt::PenCapStyle penCapStyle = Qt::FlatCap;
804 Qt::PenJoinStyle penJoinStyle = Qt::MiterJoin;
805 if ( jsonLayer.contains( u
"layout"_s ) )
807 const QVariantMap jsonLayout = jsonLayer.value( u
"layout"_s ).toMap();
808 if ( jsonLayout.contains( u
"line-cap"_s ) )
810 penCapStyle =
parseCapStyle( jsonLayout.value( u
"line-cap"_s ).toString() );
812 if ( jsonLayout.contains( u
"line-join"_s ) )
814 penJoinStyle =
parseJoinStyle( jsonLayout.value( u
"line-join"_s ).toString() );
818 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsLineSymbol >() );
819 symbol->setOutputUnit( context.
targetUnit() );
821 if ( !rasterLineSprite.isEmpty() )
831 if ( lineOpacity != -1 )
833 symbol->setOpacity( lineOpacity );
839 symbol->setDataDefinedProperties( ddProperties );
841 if ( lineWidth != -1 )
845 symbol->changeSymbolLayer( 0, lineSymbol );
850 Q_ASSERT( lineSymbol );
860 if ( lineOpacity != -1 )
862 symbol->setOpacity( lineOpacity );
868 symbol->setDataDefinedProperties( ddProperties );
870 if ( lineColor.isValid() )
874 if ( lineWidth != -1 )
878 if ( !dashVector.empty() )
892 if ( !jsonLayer.contains( u
"paint"_s ) )
894 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
898 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
903 QColor circleFillColor;
904 if ( jsonPaint.contains( u
"circle-color"_s ) )
906 const QVariant jsonCircleColor = jsonPaint.value( u
"circle-color"_s );
907 switch ( jsonCircleColor.userType() )
909 case QMetaType::Type::QVariantMap:
913 case QMetaType::Type::QVariantList:
914 case QMetaType::Type::QStringList:
918 case QMetaType::Type::QString:
919 circleFillColor =
parseColor( jsonCircleColor.toString(), context );
923 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleColor.userType() ) ) ) );
930 circleFillColor = QColor( 0, 0, 0 );
934 double circleDiameter = 10.0;
935 if ( jsonPaint.contains( u
"circle-radius"_s ) )
937 const QVariant jsonCircleRadius = jsonPaint.value( u
"circle-radius"_s );
938 switch ( jsonCircleRadius.userType() )
940 case QMetaType::Type::Int:
941 case QMetaType::Type::LongLong:
942 case QMetaType::Type::Double:
946 case QMetaType::Type::QVariantMap:
951 case QMetaType::Type::QVariantList:
952 case QMetaType::Type::QStringList:
958 QObject::tr(
"%1: Skipping unsupported circle-radius type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleRadius.userType() ) ) )
964 double circleOpacity = -1.0;
965 if ( jsonPaint.contains( u
"circle-opacity"_s ) )
967 const QVariant jsonCircleOpacity = jsonPaint.value( u
"circle-opacity"_s );
968 switch ( jsonCircleOpacity.userType() )
970 case QMetaType::Type::Int:
971 case QMetaType::Type::LongLong:
972 case QMetaType::Type::Double:
973 circleOpacity = jsonCircleOpacity.toDouble();
976 case QMetaType::Type::QVariantMap:
980 case QMetaType::Type::QVariantList:
981 case QMetaType::Type::QStringList:
987 QObject::tr(
"%1: Skipping unsupported circle-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleOpacity.userType() ) ) )
992 if ( ( circleOpacity != -1 ) && circleFillColor.isValid() )
994 circleFillColor.setAlphaF( circleOpacity );
998 QColor circleStrokeColor;
999 if ( jsonPaint.contains( u
"circle-stroke-color"_s ) )
1001 const QVariant jsonCircleStrokeColor = jsonPaint.value( u
"circle-stroke-color"_s );
1002 switch ( jsonCircleStrokeColor.userType() )
1004 case QMetaType::Type::QVariantMap:
1008 case QMetaType::Type::QVariantList:
1009 case QMetaType::Type::QStringList:
1013 case QMetaType::Type::QString:
1014 circleStrokeColor =
parseColor( jsonCircleStrokeColor.toString(), context );
1019 QObject::tr(
"%1: Skipping unsupported circle-stroke-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleStrokeColor.userType() ) ) )
1026 double circleStrokeWidth = -1.0;
1027 if ( jsonPaint.contains( u
"circle-stroke-width"_s ) )
1029 const QVariant circleStrokeWidthJson = jsonPaint.value( u
"circle-stroke-width"_s );
1030 switch ( circleStrokeWidthJson.userType() )
1032 case QMetaType::Type::Int:
1033 case QMetaType::Type::LongLong:
1034 case QMetaType::Type::Double:
1038 case QMetaType::Type::QVariantMap:
1039 circleStrokeWidth = -1.0;
1043 case QMetaType::Type::QVariantList:
1044 case QMetaType::Type::QStringList:
1050 QObject::tr(
"%1: Skipping unsupported circle-stroke-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( circleStrokeWidthJson.userType() ) ) )
1056 double circleStrokeOpacity = -1.0;
1057 if ( jsonPaint.contains( u
"circle-stroke-opacity"_s ) )
1059 const QVariant jsonCircleStrokeOpacity = jsonPaint.value( u
"circle-stroke-opacity"_s );
1060 switch ( jsonCircleStrokeOpacity.userType() )
1062 case QMetaType::Type::Int:
1063 case QMetaType::Type::LongLong:
1064 case QMetaType::Type::Double:
1065 circleStrokeOpacity = jsonCircleStrokeOpacity.toDouble();
1068 case QMetaType::Type::QVariantMap:
1072 case QMetaType::Type::QVariantList:
1073 case QMetaType::Type::QStringList:
1080 QObject::tr(
"%1: Skipping unsupported circle-stroke-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleStrokeOpacity.userType() ) ) )
1085 if ( ( circleStrokeOpacity != -1 ) && circleStrokeColor.isValid() )
1087 circleStrokeColor.setAlphaF( circleStrokeOpacity );
1091 QPointF circleTranslate;
1092 if ( jsonPaint.contains( u
"circle-translate"_s ) )
1094 const QVariant jsonCircleTranslate = jsonPaint.value( u
"circle-translate"_s );
1095 switch ( jsonCircleTranslate.userType() )
1097 case QMetaType::Type::QVariantMap:
1101 case QMetaType::Type::QVariantList:
1102 case QMetaType::Type::QStringList:
1109 QObject::tr(
"%1: Skipping unsupported circle-translate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleTranslate.userType() ) ) )
1115 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsMarkerSymbol >() );
1117 Q_ASSERT( markerSymbolLayer );
1120 symbol->setOutputUnit( context.
targetUnit() );
1123 if ( !circleTranslate.isNull() )
1125 markerSymbolLayer->
setOffset( circleTranslate );
1129 if ( circleFillColor.isValid() )
1133 if ( circleDiameter != -1 )
1135 markerSymbolLayer->
setSize( circleDiameter );
1138 if ( circleStrokeColor.isValid() )
1142 if ( circleStrokeWidth != -1 )
1157 hasLabeling =
false;
1158 hasRenderer =
false;
1160 if ( !jsonLayer.contains( u
"layout"_s ) )
1162 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
1165 const QVariantMap jsonLayout = jsonLayer.value( u
"layout"_s ).toMap();
1166 if ( !jsonLayout.contains( u
"text-field"_s ) )
1172 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
1178 if ( jsonLayout.contains( u
"text-size"_s ) )
1180 const QVariant jsonTextSize = jsonLayout.value( u
"text-size"_s );
1181 switch ( jsonTextSize.userType() )
1183 case QMetaType::Type::Int:
1184 case QMetaType::Type::LongLong:
1185 case QMetaType::Type::Double:
1189 case QMetaType::Type::QVariantMap:
1195 case QMetaType::Type::QVariantList:
1196 case QMetaType::Type::QStringList:
1202 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextSize.userType() ) ) ) );
1206 if ( textSizeProperty )
1213 constexpr double EM_TO_CHARS = 2.0;
1215 double textMaxWidth = -1;
1216 if ( jsonLayout.contains( u
"text-max-width"_s ) )
1218 const QVariant jsonTextMaxWidth = jsonLayout.value( u
"text-max-width"_s );
1219 switch ( jsonTextMaxWidth.userType() )
1221 case QMetaType::Type::Int:
1222 case QMetaType::Type::LongLong:
1223 case QMetaType::Type::Double:
1224 textMaxWidth = jsonTextMaxWidth.toDouble() * EM_TO_CHARS;
1227 case QMetaType::Type::QVariantMap:
1231 case QMetaType::Type::QVariantList:
1232 case QMetaType::Type::QStringList:
1238 QObject::tr(
"%1: Skipping unsupported text-max-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextMaxWidth.userType() ) ) )
1246 textMaxWidth = 10 * EM_TO_CHARS;
1249 double textLetterSpacing = -1;
1250 if ( jsonLayout.contains( u
"text-letter-spacing"_s ) )
1252 const QVariant jsonTextLetterSpacing = jsonLayout.value( u
"text-letter-spacing"_s );
1253 switch ( jsonTextLetterSpacing.userType() )
1255 case QMetaType::Type::Int:
1256 case QMetaType::Type::LongLong:
1257 case QMetaType::Type::Double:
1258 textLetterSpacing = jsonTextLetterSpacing.toDouble();
1261 case QMetaType::Type::QVariantMap:
1265 case QMetaType::Type::QVariantList:
1266 case QMetaType::Type::QStringList:
1272 QObject::tr(
"%1: Skipping unsupported text-letter-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextLetterSpacing.userType() ) ) )
1279 bool foundFont =
false;
1281 QString fontStyleName;
1283 bool allowOverlap = jsonLayout.contains( u
"text-allow-overlap"_s ) && jsonLayout.
value( u
"text-allow-overlap"_s ).toBool();
1285 if ( jsonLayout.contains( u
"text-font"_s ) )
1287 auto splitFontFamily = [](
const QString &fontName, QString &family, QString &style ) ->
bool {
1288 QString matchedFamily;
1289 const QStringList textFontParts = fontName.split(
' ' );
1290 for (
int i = textFontParts.size() - 1; i >= 1; --i )
1292 const QString candidateFontFamily = textFontParts.mid( 0, i ).join(
' ' );
1293 const QString candidateFontStyle = textFontParts.mid( i ).join(
' ' );
1298 family = processedFontFamily;
1299 style = candidateFontStyle;
1304 if ( processedFontFamily == matchedFamily )
1306 family = processedFontFamily;
1307 style = candidateFontStyle;
1311 family = matchedFamily;
1312 style = processedFontFamily;
1313 style.replace( matchedFamily, QString() );
1314 style = style.trimmed();
1315 if ( !style.isEmpty() && !candidateFontStyle.isEmpty() )
1317 style += u
" %1"_s.arg( candidateFontStyle );
1325 if ( QFontDatabase().hasFamily( processedFontFamily ) )
1328 family = processedFontFamily;
1334 family = matchedFamily;
1341 const QVariant jsonTextFont = jsonLayout.value( u
"text-font"_s );
1342 if ( jsonTextFont.userType() != QMetaType::Type::QVariantList
1343 && jsonTextFont.userType() != QMetaType::Type::QStringList
1344 && jsonTextFont.userType() != QMetaType::Type::QString
1345 && jsonTextFont.userType() != QMetaType::Type::QVariantMap )
1347 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-font type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextFont.userType() ) ) ) );
1351 switch ( jsonTextFont.userType() )
1353 case QMetaType::Type::QVariantList:
1354 case QMetaType::Type::QStringList:
1355 fontName = jsonTextFont.toList().value( 0 ).toString();
1358 case QMetaType::Type::QString:
1359 fontName = jsonTextFont.toString();
1362 case QMetaType::Type::QVariantMap:
1364 QString familyCaseString = u
"CASE "_s;
1365 QString styleCaseString = u
"CASE "_s;
1367 const QVariantList stops = jsonTextFont.toMap().value( u
"stops"_s ).toList();
1370 for (
int i = 0; i < stops.length() - 1; ++i )
1373 const QVariant bz = stops.value( i ).toList().value( 0 );
1374 const QString bv = stops.value( i ).toList().value( 1 ).userType() == QMetaType::Type::QString ? stops.value( i ).toList().value( 1 ).toString()
1375 : stops.value( i ).toList().value( 1 ).toList().value( 0 ).toString();
1376 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
1378 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1384 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
1385 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
1387 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1392 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1394 familyCaseString += QStringLiteral(
1395 "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1399 styleCaseString += QStringLiteral(
1400 "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1407 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1413 const QString bv = stops.constLast().toList().value( 1 ).userType() == QMetaType::Type::QString ? stops.constLast().toList().value( 1 ).toString()
1414 : stops.constLast().toList().value( 1 ).toList().value( 0 ).toString();
1415 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1422 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1429 fontName = fontFamily;
1439 if ( splitFontFamily( fontName, fontFamily, fontStyleName ) )
1442 if ( !fontStyleName.isEmpty() )
1443 textFont.setStyleName( fontStyleName );
1453 fontName = u
"Open Sans"_s;
1455 textFont.setStyleName( u
"Regular"_s );
1456 fontStyleName = u
"Regular"_s;
1461 fontName = u
"Arial Unicode MS"_s;
1463 textFont.setStyleName( u
"Regular"_s );
1464 fontStyleName = u
"Regular"_s;
1469 fontName = u
"Open Sans, Arial Unicode MS"_s;
1472 if ( !foundFont && !fontName.isEmpty() )
1474 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), fontName ) );
1479 if ( jsonPaint.contains( u
"text-color"_s ) )
1481 const QVariant jsonTextColor = jsonPaint.value( u
"text-color"_s );
1482 switch ( jsonTextColor.userType() )
1484 case QMetaType::Type::QVariantMap:
1488 case QMetaType::Type::QVariantList:
1489 case QMetaType::Type::QStringList:
1493 case QMetaType::Type::QString:
1494 textColor =
parseColor( jsonTextColor.toString(), context );
1498 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextColor.userType() ) ) ) );
1505 textColor = QColor( 0, 0, 0 );
1509 QColor bufferColor( 0, 0, 0, 0 );
1510 if ( jsonPaint.contains( u
"text-halo-color"_s ) )
1512 const QVariant jsonBufferColor = jsonPaint.value( u
"text-halo-color"_s );
1513 switch ( jsonBufferColor.userType() )
1515 case QMetaType::Type::QVariantMap:
1519 case QMetaType::Type::QVariantList:
1520 case QMetaType::Type::QStringList:
1524 case QMetaType::Type::QString:
1525 bufferColor =
parseColor( jsonBufferColor.toString(), context );
1530 QObject::tr(
"%1: Skipping unsupported text-halo-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonBufferColor.userType() ) ) )
1536 double bufferSize = 0.0;
1540 constexpr double BUFFER_SIZE_SCALE = 2.0;
1541 if ( jsonPaint.contains( u
"text-halo-width"_s ) )
1543 const QVariant jsonHaloWidth = jsonPaint.value( u
"text-halo-width"_s );
1544 QString bufferSizeDataDefined;
1545 switch ( jsonHaloWidth.userType() )
1547 case QMetaType::Type::Int:
1548 case QMetaType::Type::LongLong:
1549 case QMetaType::Type::Double:
1553 case QMetaType::Type::QVariantMap:
1558 case QMetaType::Type::QVariantList:
1559 case QMetaType::Type::QStringList:
1565 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-halo-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonHaloWidth.userType() ) ) ) );
1571 if ( bufferSize > 0 )
1573 if ( textSize > 0 && bufferSizeDataDefined.isEmpty() )
1575 bufferSize = std::min( bufferSize, textSize * BUFFER_SIZE_SCALE / 4 );
1577 else if ( textSize > 0 && !bufferSizeDataDefined.isEmpty() )
1579 bufferSizeDataDefined = u
"min(%1/4, %2)"_s.arg( textSize * BUFFER_SIZE_SCALE ).arg( bufferSizeDataDefined );
1582 else if ( !bufferSizeDataDefined.isEmpty() )
1584 bufferSizeDataDefined = u
"min(%1*%2/4, %3)"_s.arg( textSizeProperty.
asExpression() ).arg( BUFFER_SIZE_SCALE ).arg( bufferSizeDataDefined );
1587 else if ( bufferSizeDataDefined.isEmpty() )
1589 bufferSizeDataDefined = u
"min(%1*%2/4, %3)"_s.arg( textSizeProperty.
asExpression() ).arg( BUFFER_SIZE_SCALE ).arg( bufferSize );
1595 double haloBlurSize = 0;
1596 if ( jsonPaint.contains( u
"text-halo-blur"_s ) )
1598 const QVariant jsonTextHaloBlur = jsonPaint.value( u
"text-halo-blur"_s );
1599 switch ( jsonTextHaloBlur.userType() )
1601 case QMetaType::Type::Int:
1602 case QMetaType::Type::LongLong:
1603 case QMetaType::Type::Double:
1611 QObject::tr(
"%1: Skipping unsupported text-halo-blur type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextHaloBlur.userType() ) ) )
1619 if ( textColor.isValid() )
1621 if ( textSize >= 0 )
1626 if ( !fontStyleName.isEmpty() )
1629 if ( textLetterSpacing > 0 )
1631 QFont f = format.
font();
1632 f.setLetterSpacing( QFont::AbsoluteSpacing, textLetterSpacing );
1636 if ( bufferSize > 0 )
1639 const double opacity = bufferColor.alphaF();
1640 bufferColor.setAlphaF( 1.0 );
1648 if ( haloBlurSize > 0 )
1671 if ( textMaxWidth > 0 )
1677 if ( jsonLayout.contains( u
"text-field"_s ) )
1679 const QVariant jsonTextField = jsonLayout.value( u
"text-field"_s );
1680 switch ( jsonTextField.userType() )
1682 case QMetaType::Type::QString:
1684 labelSettings.
fieldName = processLabelField( jsonTextField.toString(), labelSettings.
isExpression );
1688 case QMetaType::Type::QVariantList:
1689 case QMetaType::Type::QStringList:
1691 const QVariantList textFieldList = jsonTextField.toList();
1699 if ( textFieldList.size() > 2 && textFieldList.at( 0 ).toString() ==
"format"_L1 )
1702 for (
int i = 1; i < textFieldList.size(); ++i )
1704 bool isExpression =
false;
1705 const QString part = processLabelField( textFieldList.at( i ).toString(), isExpression );
1706 if ( !isExpression )
1713 labelSettings.
fieldName = u
"concat(%1)"_s.arg( parts.join(
',' ) );
1728 case QMetaType::Type::QVariantMap:
1730 const QVariantList stops = jsonTextField.toMap().value( u
"stops"_s ).toList();
1731 if ( !stops.empty() )
1738 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-field dictionary" ).arg( context.
layerId() ) );
1744 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-field type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextField.userType() ) ) ) );
1749 if ( jsonLayout.contains( u
"text-rotate"_s ) )
1751 const QVariant jsonTextRotate = jsonLayout.value( u
"text-rotate"_s );
1752 switch ( jsonTextRotate.userType() )
1754 case QMetaType::Type::Double:
1755 case QMetaType::Type::Int:
1757 labelSettings.
angleOffset = jsonTextRotate.toDouble();
1761 case QMetaType::Type::QVariantList:
1762 case QMetaType::Type::QStringList:
1769 case QMetaType::Type::QVariantMap:
1771 QVariantMap rotateMap = jsonTextRotate.toMap();
1772 if ( rotateMap.contains( u
"property"_s ) && rotateMap[u
"type"_s].toString() ==
"identity"_L1 )
1778 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-rotate map content (%2)" ).arg( context.
layerId(), QString( QJsonDocument::fromVariant( rotateMap ).toJson() ) ) );
1783 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextRotate.userType() ) ) ) );
1788 if ( jsonLayout.contains( u
"text-transform"_s ) )
1790 const QString textTransform = jsonLayout.value( u
"text-transform"_s ).toString();
1791 if ( textTransform ==
"uppercase"_L1 )
1795 else if ( textTransform ==
"lowercase"_L1 )
1804 if ( jsonLayout.contains( u
"symbol-placement"_s ) )
1806 const QString symbolPlacement = jsonLayout.value( u
"symbol-placement"_s ).toString();
1807 if ( symbolPlacement ==
"line"_L1 )
1813 if ( jsonLayout.contains( u
"text-rotation-alignment"_s ) )
1815 const QString textRotationAlignment = jsonLayout.value( u
"text-rotation-alignment"_s ).toString();
1816 if ( textRotationAlignment ==
"viewport"_L1 )
1826 if ( jsonLayout.contains( u
"text-offset"_s ) )
1828 const QVariant jsonTextOffset = jsonLayout.value( u
"text-offset"_s );
1831 switch ( jsonTextOffset.userType() )
1833 case QMetaType::Type::QVariantMap:
1834 textOffsetProperty =
parseInterpolatePointByZoom( jsonTextOffset.toMap(), context, !textSizeProperty ? textSize : 1.0, &textOffset );
1835 if ( !textSizeProperty )
1843 u
"with_variable('text_size',%2,abs(array_get(%1,1))*@text_size-@text_size)"_s.arg( textOffsetProperty.
asExpression(), textSizeProperty.
asExpression() )
1849 case QMetaType::Type::QVariantList:
1850 case QMetaType::Type::QStringList:
1851 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize, jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1856 QObject::tr(
"%1: Skipping unsupported text-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextOffset.userType() ) ) )
1861 if ( !textOffset.isNull() )
1864 labelSettings.
dist = std::abs( textOffset.y() ) - textSize;
1866 if ( textSizeProperty && !textOffsetProperty )
1870 u
"with_variable('text_size',%2,%1*@text_size-@text_size)"_s.arg( std::abs( textOffset.y() / textSize ) ).arg( textSizeProperty.
asExpression() )
1876 if ( textOffset.isNull() )
1884 if ( jsonLayout.contains( u
"text-justify"_s ) )
1886 const QVariant jsonTextJustify = jsonLayout.value( u
"text-justify"_s );
1889 QString textAlign = u
"center"_s;
1891 const QVariantMap conversionMap { { u
"left"_s, u
"left"_s }, { u
"center"_s, u
"center"_s }, { u
"right"_s, u
"right"_s }, { u
"auto"_s, u
"follow"_s } };
1893 switch ( jsonTextJustify.userType() )
1895 case QMetaType::Type::QString:
1896 textAlign = jsonTextJustify.toString();
1899 case QMetaType::Type::QVariantList:
1903 case QMetaType::Type::QVariantMap:
1908 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-justify type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextJustify.userType() ) ) ) );
1912 if ( textAlign ==
"left"_L1 )
1914 else if ( textAlign ==
"right"_L1 )
1916 else if ( textAlign ==
"center"_L1 )
1918 else if ( textAlign ==
"follow"_L1 )
1928 if ( jsonLayout.contains( u
"text-anchor"_s ) )
1930 const QVariant jsonTextAnchor = jsonLayout.value( u
"text-anchor"_s );
1933 const QVariantMap conversionMap {
1939 { u
"top-left"_s, 8 },
1940 { u
"top-right"_s, 6 },
1941 { u
"bottom-left"_s, 2 },
1942 { u
"bottom-right"_s, 0 },
1945 switch ( jsonTextAnchor.userType() )
1947 case QMetaType::Type::QString:
1948 textAnchor = jsonTextAnchor.toString();
1951 case QMetaType::Type::QVariantList:
1955 case QMetaType::Type::QVariantMap:
1960 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-anchor type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextAnchor.userType() ) ) ) );
1964 if ( textAnchor ==
"center"_L1 )
1966 else if ( textAnchor ==
"left"_L1 )
1968 else if ( textAnchor ==
"right"_L1 )
1970 else if ( textAnchor ==
"top"_L1 )
1972 else if ( textAnchor ==
"bottom"_L1 )
1974 else if ( textAnchor ==
"top-left"_L1 )
1976 else if ( textAnchor ==
"top-right"_L1 )
1978 else if ( textAnchor ==
"bottom-left"_L1 )
1980 else if ( textAnchor ==
"bottom-right"_L1 )
1985 if ( jsonLayout.contains( u
"text-offset"_s ) )
1987 const QVariant jsonTextOffset = jsonLayout.value( u
"text-offset"_s );
1990 switch ( jsonTextOffset.userType() )
1992 case QMetaType::Type::QVariantMap:
1996 case QMetaType::Type::QVariantList:
1997 case QMetaType::Type::QStringList:
1998 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize, jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
2002 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextOffset.userType() ) ) ) );
2006 if ( !textOffset.isNull() )
2009 labelSettings.
xOffset = textOffset.x();
2010 labelSettings.
yOffset = textOffset.y();
2018 QString spriteProperty, spriteSizeProperty;
2020 if ( !sprite.isEmpty() )
2023 if ( jsonLayout.contains( u
"icon-size"_s ) )
2026 const QVariant jsonIconSize = jsonLayout.
value( u
"icon-size"_s );
2027 switch ( jsonIconSize.userType() )
2029 case QMetaType::Type::Int:
2030 case QMetaType::Type::LongLong:
2031 case QMetaType::Type::Double:
2033 size = jsonIconSize.toDouble();
2034 if ( !spriteSizeProperty.isEmpty() )
2041 case QMetaType::Type::QVariantMap:
2045 case QMetaType::Type::QVariantList:
2046 case QMetaType::Type::QStringList:
2051 QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) )
2058 if ( !spriteSizeProperty.isEmpty() )
2071 markerLayer->
setPath( sprite );
2072 markerLayer->
setSize( spriteSize.width() );
2075 if ( !spriteProperty.isEmpty() )
2085 backgroundSettings.
setSize( spriteSize * size );
2095 if ( jsonLayout.contains( u
"symbol-spacing"_s ) )
2098 const QVariant jsonSpacing = jsonLayout.value( u
"symbol-spacing"_s );
2108 switch ( jsonSpacing.userType() )
2110 case QMetaType::Type::Int:
2111 case QMetaType::Type::LongLong:
2112 case QMetaType::Type::Double:
2119 case QMetaType::Type::QVariantMap:
2125 case QMetaType::Type::QVariantList:
2126 case QMetaType::Type::QStringList:
2133 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported symbol-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonSpacing.userType() ) ) ) );
2142 if ( textSize >= 0 )
2165 if ( !jsonLayer.contains( u
"layout"_s ) )
2167 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
2170 const QVariantMap jsonLayout = jsonLayer.value( u
"layout"_s ).toMap();
2172 if ( jsonLayout.value( u
"symbol-placement"_s ).toString() ==
"line"_L1 && !jsonLayout.contains( u
"text-field"_s ) )
2176 double spacing = -1.0;
2177 if ( jsonLayout.contains( u
"symbol-spacing"_s ) )
2179 const QVariant jsonSpacing = jsonLayout.value( u
"symbol-spacing"_s );
2180 switch ( jsonSpacing.userType() )
2182 case QMetaType::Type::Int:
2183 case QMetaType::Type::LongLong:
2184 case QMetaType::Type::Double:
2188 case QMetaType::Type::QVariantMap:
2192 case QMetaType::Type::QVariantList:
2193 case QMetaType::Type::QStringList:
2198 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported symbol-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonSpacing.userType() ) ) ) );
2208 bool rotateMarkers =
true;
2209 if ( jsonLayout.contains( u
"icon-rotation-alignment"_s ) )
2211 const QString alignment = jsonLayout.value( u
"icon-rotation-alignment"_s ).toString();
2212 if ( alignment ==
"map"_L1 || alignment ==
"auto"_L1 )
2214 rotateMarkers =
true;
2216 else if ( alignment ==
"viewport"_L1 )
2218 rotateMarkers =
false;
2223 double rotation = 0.0;
2224 if ( jsonLayout.contains( u
"icon-rotate"_s ) )
2226 const QVariant jsonIconRotate = jsonLayout.value( u
"icon-rotate"_s );
2227 switch ( jsonIconRotate.userType() )
2229 case QMetaType::Type::Int:
2230 case QMetaType::Type::LongLong:
2231 case QMetaType::Type::Double:
2232 rotation = jsonIconRotate.toDouble();
2235 case QMetaType::Type::QVariantMap:
2239 case QMetaType::Type::QVariantList:
2240 case QMetaType::Type::QStringList:
2245 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconRotate.userType() ) ) ) );
2259 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
2263 QString spriteProperty, spriteSizeProperty;
2265 if ( !sprite.isNull() )
2267 markerLayer->
setPath( sprite );
2268 markerLayer->
setSize( spriteSize.width() );
2271 if ( !spriteProperty.isEmpty() )
2278 if ( jsonLayout.contains( u
"icon-size"_s ) )
2280 const QVariant jsonIconSize = jsonLayout.value( u
"icon-size"_s );
2283 switch ( jsonIconSize.userType() )
2285 case QMetaType::Type::Int:
2286 case QMetaType::Type::LongLong:
2287 case QMetaType::Type::Double:
2289 size = jsonIconSize.toDouble();
2290 if ( !spriteSizeProperty.isEmpty() )
2297 case QMetaType::Type::QVariantMap:
2301 case QMetaType::Type::QVariantList:
2302 case QMetaType::Type::QStringList:
2306 context.
pushWarning( QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) ) );
2309 markerLayer->
setSize( size * spriteSize.width() );
2312 if ( !spriteSizeProperty.isEmpty() )
2324 double iconOpacity = -1.0;
2325 if ( jsonPaint.contains( u
"icon-opacity"_s ) )
2327 const QVariant jsonIconOpacity = jsonPaint.value( u
"icon-opacity"_s );
2328 switch ( jsonIconOpacity.userType() )
2330 case QMetaType::Type::Int:
2331 case QMetaType::Type::LongLong:
2332 case QMetaType::Type::Double:
2333 iconOpacity = jsonIconOpacity.toDouble();
2336 case QMetaType::Type::QVariantMap:
2340 case QMetaType::Type::QVariantList:
2341 case QMetaType::Type::QStringList:
2347 QObject::tr(
"%1: Skipping unsupported icon-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconOpacity.userType() ) ) )
2355 if ( iconOpacity >= 0 )
2362 std::unique_ptr< QgsSymbol > symbol = std::make_unique< QgsLineSymbol >(
QgsSymbolLayerList() << lineSymbol );
2365 symbol->setOutputUnit( context.
targetUnit() );
2369 rendererStyle.
setSymbol( symbol.release() );
2372 else if ( jsonLayout.contains( u
"icon-image"_s ) )
2374 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
2377 QString spriteProperty, spriteSizeProperty;
2379 if ( !sprite.isEmpty() || !spriteProperty.isEmpty() )
2382 rasterMarker->
setPath( sprite );
2383 rasterMarker->
setSize( spriteSize.width() );
2387 if ( !spriteProperty.isEmpty() )
2393 if ( jsonLayout.contains( u
"icon-size"_s ) )
2395 const QVariant jsonIconSize = jsonLayout.value( u
"icon-size"_s );
2398 switch ( jsonIconSize.userType() )
2400 case QMetaType::Type::Int:
2401 case QMetaType::Type::LongLong:
2402 case QMetaType::Type::Double:
2404 size = jsonIconSize.toDouble();
2405 if ( !spriteSizeProperty.isEmpty() )
2412 case QMetaType::Type::QVariantMap:
2416 case QMetaType::Type::QVariantList:
2417 case QMetaType::Type::QStringList:
2422 QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) )
2426 rasterMarker->
setSize( size * spriteSize.width() );
2429 if ( !spriteSizeProperty.isEmpty() )
2441 double rotation = 0.0;
2442 if ( jsonLayout.contains( u
"icon-rotate"_s ) )
2444 const QVariant jsonIconRotate = jsonLayout.value( u
"icon-rotate"_s );
2445 switch ( jsonIconRotate.userType() )
2447 case QMetaType::Type::Int:
2448 case QMetaType::Type::LongLong:
2449 case QMetaType::Type::Double:
2450 rotation = jsonIconRotate.toDouble();
2453 case QMetaType::Type::QVariantMap:
2457 case QMetaType::Type::QVariantList:
2458 case QMetaType::Type::QStringList:
2464 QObject::tr(
"%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconRotate.userType() ) ) )
2470 double iconOpacity = -1.0;
2471 if ( jsonPaint.contains( u
"icon-opacity"_s ) )
2473 const QVariant jsonIconOpacity = jsonPaint.value( u
"icon-opacity"_s );
2474 switch ( jsonIconOpacity.userType() )
2476 case QMetaType::Type::Int:
2477 case QMetaType::Type::LongLong:
2478 case QMetaType::Type::Double:
2479 iconOpacity = jsonIconOpacity.toDouble();
2482 case QMetaType::Type::QVariantMap:
2486 case QMetaType::Type::QVariantList:
2487 case QMetaType::Type::QStringList:
2493 QObject::tr(
"%1: Skipping unsupported icon-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconOpacity.userType() ) ) )
2500 rasterMarker->
setAngle( rotation );
2501 if ( iconOpacity >= 0 )
2505 rendererStyle.
setSymbol( markerSymbol );
2516 const double base = json.
value( u
"base"_s, u
"1"_s ).toDouble();
2517 const QVariantList stops = json.value( u
"stops"_s ).toList();
2518 if ( stops.empty() )
2521 QString caseString = u
"CASE "_s;
2522 const QString colorComponent(
"color_part(%1,'%2')" );
2524 for (
int i = 0; i < stops.length() - 1; ++i )
2527 const QString bz = stops.at( i ).toList().value( 0 ).toString();
2529 const QString tz = stops.at( i + 1 ).toList().value( 0 ).toString();
2531 const QVariant bcVariant = stops.at( i ).toList().value( 1 );
2532 const QVariant tcVariant = stops.at( i + 1 ).toList().value( 1 );
2534 const QColor bottomColor =
parseColor( bcVariant.toString(), context );
2535 const QColor topColor =
parseColor( tcVariant.toString(), context );
2537 if ( i == 0 && bottomColor.isValid() )
2544 caseString += u
"WHEN @vector_tile_zoom < %1 THEN color_hsla(%2, %3, %4, %5) "_s.arg( bz ).arg( bcHue ).arg( bcSat ).arg( bcLight ).arg( bcAlpha );
2547 if ( bottomColor.isValid() && topColor.isValid() )
2559 caseString += QStringLiteral(
2560 "WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2573 json.value( u
"x1"_s ).toDouble(),
2574 json.value( u
"y1"_s ).toDouble(),
2575 json.value( u
"x2"_s ).toDouble(),
2576 json.value( u
"y2"_s ).toDouble(),
2587 json.value( u
"x1"_s ).toDouble(),
2588 json.value( u
"y1"_s ).toDouble(),
2589 json.value( u
"x2"_s ).toDouble(),
2590 json.value( u
"y2"_s ).toDouble(),
2601 json.value( u
"x1"_s ).toDouble(),
2602 json.value( u
"y1"_s ).toDouble(),
2603 json.value( u
"x2"_s ).toDouble(),
2604 json.value( u
"y2"_s ).toDouble(),
2615 json.value( u
"x1"_s ).toDouble(),
2616 json.value( u
"y1"_s ).toDouble(),
2617 json.value( u
"x2"_s ).toDouble(),
2618 json.value( u
"y2"_s ).toDouble(),
2629 caseString += QStringLiteral(
2630 "WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2639 colorComponent.arg( bottomColorExpr ).arg(
"hsl_hue" ),
2640 colorComponent.arg( topColorExpr ).arg(
"hsl_hue" ),
2643 json.value( u
"x1"_s ).toDouble(),
2644 json.value( u
"y1"_s ).toDouble(),
2645 json.value( u
"x2"_s ).toDouble(),
2646 json.value( u
"y2"_s ).toDouble(),
2653 colorComponent.arg( bottomColorExpr ).arg(
"hsl_saturation" ),
2654 colorComponent.arg( topColorExpr ).arg(
"hsl_saturation" ),
2657 json.value( u
"x1"_s ).toDouble(),
2658 json.value( u
"y1"_s ).toDouble(),
2659 json.value( u
"x2"_s ).toDouble(),
2660 json.value( u
"y2"_s ).toDouble(),
2667 colorComponent.arg( bottomColorExpr ).arg(
"lightness" ),
2668 colorComponent.arg( topColorExpr ).arg(
"lightness" ),
2671 json.value( u
"x1"_s ).toDouble(),
2672 json.value( u
"y1"_s ).toDouble(),
2673 json.value( u
"x2"_s ).toDouble(),
2674 json.value( u
"y2"_s ).toDouble(),
2681 colorComponent.arg( bottomColorExpr ).arg(
"alpha" ),
2682 colorComponent.arg( topColorExpr ).arg(
"alpha" ),
2685 json.value( u
"x1"_s ).toDouble(),
2686 json.value( u
"y1"_s ).toDouble(),
2687 json.value( u
"x2"_s ).toDouble(),
2688 json.value( u
"y2"_s ).toDouble(),
2697 const QString tz = stops.last().toList().value( 0 ).toString();
2698 const QVariant tcVariant = stops.last().toList().value( 1 );
2700 if ( tcVariant.userType() == QMetaType::Type::QString )
2703 if ( topColor.isValid() )
2710 caseString += QStringLiteral(
2711 "WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2712 "ELSE color_hsla(%2, %3, %4, %5) END"
2721 else if ( tcVariant.userType() == QMetaType::QVariantList )
2725 caseString += QStringLiteral(
2726 "WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2727 "ELSE color_hsla(%2, %3, %4, %5) END"
2730 .arg( colorComponent.arg( topColorExpr ).arg(
"hsl_hue" ) )
2731 .arg( colorComponent.arg( topColorExpr ).arg(
"hsl_saturation" ) )
2732 .arg( colorComponent.arg( topColorExpr ).arg(
"lightness" ) )
2733 .arg( colorComponent.arg( topColorExpr ).arg(
"alpha" ) );
2736 if ( !stops.empty() && defaultColor )
2737 *defaultColor =
parseColor( stops.value( 0 ).toList().value( 1 ).toString(), context );
2744 const double base = json.
value( u
"base"_s, u
"1"_s ).toDouble();
2745 const QVariantList stops = json.value( u
"stops"_s ).toList();
2746 if ( stops.empty() )
2749 QString scaleExpression;
2750 if ( stops.size() <= 2 )
2753 stops.value( 0 ).toList().value( 0 ).toDouble(),
2754 stops.last().toList().value( 0 ).toDouble(),
2755 stops.value( 0 ).toList().value( 1 ),
2756 stops.last().toList().value( 1 ),
2759 json.value( u
"x1"_s ).toDouble(),
2760 json.value( u
"y1"_s ).toDouble(),
2761 json.value( u
"x2"_s ).toDouble(),
2762 json.value( u
"y2"_s ).toDouble(),
2770 =
parseStops( base, stops, multiplier, context, type, json.value( u
"x1"_s ).toDouble(), json.value( u
"y1"_s ).toDouble(), json.value( u
"x2"_s ).toDouble(), json.value( u
"y2"_s ).toDouble() );
2773 if ( !stops.empty() && defaultNumber )
2774 *defaultNumber = stops.value( 0 ).toList().value( 1 ).toDouble() * multiplier;
2784 context = *contextPtr;
2786 const double base = json.value( u
"base"_s, u
"1"_s ).toDouble();
2787 const QVariantList stops = json.value( u
"stops"_s ).toList();
2788 if ( stops.empty() )
2791 QString scaleExpression;
2792 if ( stops.length() <= 2 )
2794 const QVariant bv = stops.value( 0 ).toList().value( 1 );
2795 const QVariant tv = stops.last().toList().value( 1 );
2796 double bottom = 0.0;
2798 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2800 stops.value( 0 ).toList().value( 0 ).toDouble(),
2801 stops.last().toList().value( 0 ).toDouble(),
2802 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2803 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ),
2806 json.value( u
"x1"_s ).toDouble(),
2807 json.value( u
"y1"_s ).toDouble(),
2808 json.value( u
"x2"_s ).toDouble(),
2809 json.value( u
"y2"_s ).toDouble(),
2817 =
parseOpacityStops( base, stops, maxOpacity, context, type, json.value( u
"x1"_s ).toDouble(), json.value( u
"y1"_s ).toDouble(), json.value( u
"x2"_s ).toDouble(), json.value( u
"y2"_s ).toDouble() );
2826 QString caseString = u
"CASE WHEN @vector_tile_zoom < %1 THEN set_color_part(@symbol_color, 'alpha', %2)"_s.arg( stops.value( 0 ).toList().value( 0 ).toString() )
2827 .arg( stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity );
2829 for (
int i = 0; i < stops.size() - 1; ++i )
2831 const QVariant bv = stops.value( i ).toList().value( 1 );
2832 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2833 double bottom = 0.0;
2835 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2837 caseString += QStringLiteral(
2838 " WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
2839 "THEN set_color_part(@symbol_color, 'alpha', %3)"
2842 stops.value( i ).toList().value( 0 ).toString(),
2843 stops.value( i + 1 ).toList().value( 0 ).toString(),
2845 stops.value( i ).toList().value( 0 ).toDouble(),
2846 stops.value( i + 1 ).toList().value( 0 ).toDouble(),
2847 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2848 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ),
2862 bool numeric =
false;
2863 const QVariant vv = stops.last().toList().value( 1 );
2864 double dv = vv.toDouble( &numeric );
2866 caseString += QStringLiteral(
2867 " WHEN @vector_tile_zoom >= %1 "
2868 "THEN set_color_part(@symbol_color, 'alpha', %2) END"
2870 .arg( stops.last().toList().value( 0 ).toString(), numeric ? QString::number( dv * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( vv, context ) ).arg( maxOpacity ) );
2876 const double base = json.
value( u
"base"_s, u
"1"_s ).toDouble();
2877 const QVariantList stops = json.value( u
"stops"_s ).toList();
2878 if ( stops.empty() )
2883 QString scaleExpression;
2884 if ( stops.size() <= 2 )
2886 const bool isLiteralRepresentation = stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toString() ==
"literal"_L1;
2887 scaleExpression = u
"array(%1,%2)"_s.arg(
2889 stops.value( 0 ).toList().value( 0 ).toDouble(),
2890 stops.last().toList().value( 0 ).toDouble(),
2891 isLiteralRepresentation ? stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toList().value( 0 ) : stops.value( 0 ).toList().value( 1 ).toList().value( 0 ),
2892 isLiteralRepresentation ? stops.last().toList().value( 1 ).toList().value( 1 ).toList().value( 0 ) : stops.last().toList().value( 1 ).toList().value( 0 ),
2895 json.value( u
"x1"_s ).toDouble(),
2896 json.value( u
"y1"_s ).toDouble(),
2897 json.value( u
"x2"_s ).toDouble(),
2898 json.value( u
"y2"_s ).toDouble(),
2903 stops.value( 0 ).toList().value( 0 ).toDouble(),
2904 stops.last().toList().value( 0 ).toDouble(),
2905 isLiteralRepresentation ? stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toList().value( 1 ) : stops.value( 0 ).toList().value( 1 ).toList().value( 1 ),
2906 isLiteralRepresentation ? stops.last().toList().value( 1 ).toList().value( 1 ).toList().value( 1 ) : stops.last().toList().value( 1 ).toList().value( 1 ),
2909 json.value( u
"x1"_s ).toDouble(),
2910 json.value( u
"y1"_s ).toDouble(),
2911 json.value( u
"x2"_s ).toDouble(),
2912 json.value( u
"y2"_s ).toDouble(),
2921 =
parsePointStops( base, stops, context, multiplier, type, json.value( u
"x1"_s ).toDouble(), json.value( u
"y1"_s ).toDouble(), json.value( u
"x2"_s ).toDouble(), json.value( u
"y2"_s ).toDouble() );
2924 if ( !stops.empty() && defaultPoint )
2925 *defaultPoint = QPointF( stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble() * multiplier, stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble() * multiplier );
2932 const QVariantList stops = json.
value( u
"stops"_s ).toList();
2933 if ( stops.empty() )
2936 const QString scaleExpression =
parseStringStops( stops, context, conversionMap, defaultString );
2945 QString caseString = u
"CASE "_s;
2947 for (
int i = 0; i < stops.length() - 1; ++i )
2950 const QVariant bz = stops.value( i ).toList().value( 0 );
2951 const QVariant bv = stops.value( i ).toList().value( 1 );
2952 if ( bv.userType() != QMetaType::Type::QVariantList && bv.userType() != QMetaType::Type::QStringList )
2954 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( bz.userType() ) ) ) );
2959 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2960 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2961 if ( tv.userType() != QMetaType::Type::QVariantList && tv.userType() != QMetaType::Type::QStringList )
2963 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( tz.userType() ) ) ) );
2967 const bool isLiteralRepresentation = bv.toList().value( 0 ).toString() ==
"literal"_L1;
2968 caseString += QStringLiteral(
2969 "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2978 isLiteralRepresentation ? bv.toList().value( 1 ).toList().value( 0 ) : bv.toList().value( 0 ),
2979 isLiteralRepresentation ? tv.toList().value( 1 ).toList().value( 0 ) : tv.toList().value( 0 ),
2992 isLiteralRepresentation ? bv.toList().value( 1 ).toList().value( 1 ) : bv.toList().value( 1 ),
2993 isLiteralRepresentation ? tv.toList().value( 1 ).toList().value( 1 ) : tv.toList().value( 1 ),
3005 caseString +=
"END"_L1;
3011 if ( stops.length() < 2 )
3014 QString caseString = u
"CASE"_s;
3016 for (
int i = 0; i < stops.length(); ++i )
3018 caseString +=
" WHEN "_L1;
3019 QStringList conditions;
3022 const QVariant bottomZoom = stops.value( i ).toList().value( 0 );
3023 conditions << u
"@vector_tile_zoom > %1"_s.arg( bottomZoom.toString() );
3025 if ( i < stops.length() - 1 )
3027 const QVariant topZoom = stops.value( i + 1 ).toList().value( 0 );
3028 conditions << u
"@vector_tile_zoom <= %1"_s.arg( topZoom.toString() );
3031 const QVariantList values = stops.value( i ).toList().value( 1 ).toList();
3032 QStringList valuesFixed;
3034 for (
const QVariant &value : values )
3036 const double number = value.toDouble( &ok );
3038 valuesFixed << QString::number( number * multiplier );
3042 caseString += u
"%1 THEN array(%3)"_s.arg( conditions.join(
" AND "_L1 ), valuesFixed.join(
',' ) );
3044 caseString +=
" END"_L1;
3052 QString caseString = u
"CASE "_s;
3054 for (
int i = 0; i < stops.length() - 1; ++i )
3057 const QVariant bz = stops.value( i ).toList().value( 0 );
3058 const QVariant bv = stops.value( i ).toList().value( 1 );
3059 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
3061 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
3066 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
3067 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
3068 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
3070 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
3074 const QString lowerComparator = i == 0 ? u
">="_s : u
">"_s;
3076 caseString += QStringLiteral(
3077 "WHEN @vector_tile_zoom %1 %2 AND @vector_tile_zoom <= %3 "
3080 .arg( lowerComparator, bz.toString(), tz.toString(),
interpolateExpression( bz.toDouble(), tz.toDouble(), bv, tv, base, multiplier, x1, y1, x2, y2, type, &context ) );
3083 const QVariant z = stops.last().toList().value( 0 );
3084 const QVariant v = stops.last().toList().value( 1 );
3085 QString vStr = v.toString();
3086 if ( ( QMetaType::Type ) v.userType() == QMetaType::QVariantList )
3089 caseString += QStringLiteral(
3090 "WHEN @vector_tile_zoom > %1 "
3091 "THEN ( ( %2 ) * %3 ) END"
3093 .arg( z.toString() )
3099 caseString += QStringLiteral(
3100 "WHEN @vector_tile_zoom > %1 "
3103 .arg( z.toString() )
3104 .arg( v.toDouble() * multiplier );
3112 QString caseString = u
"CASE "_s;
3114 for (
int i = 0; i < stops.length() - 1; ++i )
3117 const QVariant bz = stops.value( i ).toList().value( 0 );
3118 const QString bv = stops.value( i ).toList().value( 1 ).toString();
3119 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
3121 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
3126 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
3127 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
3129 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
3133 caseString += QStringLiteral(
3134 "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
3139 caseString += u
"ELSE %1 END"_s.arg(
QgsExpression::quotedValue( conversionMap.value( stops.constLast().toList().value( 1 ).toString(), stops.constLast().toList().value( 1 ) ) ) );
3140 if ( defaultString )
3141 *defaultString = stops.constLast().toList().value( 1 ).toString();
3147 QString caseString = u
"CASE "_s;
3149 bool isExpression =
false;
3150 for (
int i = 0; i < stops.length() - 1; ++i )
3153 const QVariant bz = stops.value( i ).toList().value( 0 );
3154 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
3156 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
3161 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
3162 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
3164 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
3168 QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
3169 if ( fieldPart.isEmpty() )
3170 fieldPart = u
"''"_s;
3171 else if ( !isExpression )
3174 caseString += QStringLiteral(
3175 "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom < %2 "
3178 .arg( bz.toString(), tz.toString(), fieldPart );
3182 const QVariant bz = stops.constLast().toList().value( 0 );
3183 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
3185 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
3189 QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
3190 if ( fieldPart.isEmpty() )
3191 fieldPart = u
"''"_s;
3192 else if ( !isExpression )
3195 caseString += QStringLiteral(
3196 "WHEN @vector_tile_zoom >= %1 "
3199 .arg( bz.toString(), fieldPart );
3202 QString defaultPart = processLabelField( stops.constFirst().toList().value( 1 ).toString(), isExpression );
3203 if ( defaultPart.isEmpty() )
3204 defaultPart = u
"''"_s;
3205 else if ( !isExpression )
3207 caseString += u
"ELSE %1 END"_s.arg( defaultPart );
3216 const QString method = json.
value( 0 ).toString();
3217 if ( method ==
"interpolate"_L1 )
3221 else if ( method ==
"match"_L1 )
3223 return parseMatchList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
3225 else if ( method ==
"step"_L1 )
3227 return parseStepList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
3239 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
3240 if ( attribute.isEmpty() )
3242 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
3246 QString caseString = u
"CASE "_s;
3248 for (
int i = 2; i < json.length() - 1; i += 2 )
3251 QVariant variantKeys = json.value( i );
3252 if ( variantKeys.userType() == QMetaType::Type::QVariantList || variantKeys.userType() == QMetaType::Type::QStringList )
3253 keys = variantKeys.toList();
3255 keys = { variantKeys };
3257 QStringList matchString;
3258 for (
const QVariant &key : keys )
3263 const QVariant value = json.value( i + 1 );
3265 QString valueString;
3270 if ( value.userType() == QMetaType::Type::QVariantList || value.userType() == QMetaType::Type::QStringList )
3276 const QColor color =
parseColor( value, context );
3284 const double v = value.toDouble() * multiplier;
3285 valueString = QString::number( v );
3291 const double v = value.toDouble() * maxOpacity;
3292 valueString = QString::number( v );
3298 valueString = u
"array(%1,%2)"_s.arg( value.toList().value( 0 ).toDouble() * multiplier, value.toList().value( 0 ).toDouble() * multiplier );
3304 if ( value.toList().count() == 2 && value.toList().first().toString() ==
"literal"_L1 )
3306 valueString = u
"array(%1)"_s.arg( value.toList().at( 1 ).toStringList().join(
',' ) );
3310 valueString = u
"array(%1)"_s.arg( value.toStringList().join(
',' ) );
3317 if ( value.toList().count() == 2 && value.toList().first().toString() ==
"literal"_L1 )
3319 QStringList dashValues = value.toList().at( 1 ).toStringList();
3320 if ( dashValues.length() % 2 == 1 )
3322 dashValues << u
"0"_s;
3324 valueString = u
"array(%1)"_s.arg( dashValues.join(
',' ) );
3328 QStringList dashValues = value.toStringList();
3329 if ( dashValues.length() % 2 == 1 )
3331 dashValues << u
"0"_s;
3333 valueString = u
"array(%1)"_s.arg( dashValues.join(
',' ) );
3339 if ( matchString.count() == 1 )
3341 caseString += u
"WHEN %1 IS %2 THEN %3 "_s.arg( attribute, matchString.at( 0 ), valueString );
3345 caseString += u
"WHEN %1 IN (%2) THEN %3 "_s.arg( attribute, matchString.join(
',' ), valueString );
3349 QVariant lastValue = json.constLast();
3352 switch ( lastValue.userType() )
3354 case QMetaType::Type::QVariantList:
3355 case QMetaType::Type::QStringList:
3356 elseValue =
parseValueList( lastValue.toList(), type, context, multiplier, maxOpacity, defaultColor, defaultNumber ).
asExpression();
3365 const QColor color =
parseColor( lastValue, context );
3367 *defaultColor = color;
3375 const double v = json.constLast().toDouble() * multiplier;
3376 if ( defaultNumber )
3378 elseValue = QString::number( v );
3384 const double v = json.constLast().toDouble() * maxOpacity;
3385 if ( defaultNumber )
3387 elseValue = QString::number( v );
3393 elseValue = u
"array(%1,%2)"_s.arg( json.constLast().toList().value( 0 ).toDouble() * multiplier ).arg( json.constLast().toList().value( 0 ).toDouble() * multiplier );
3399 if ( json.constLast().toList().count() == 2 && json.constLast().toList().first().toString() ==
"literal"_L1 )
3401 elseValue = u
"array(%1)"_s.arg( json.constLast().toList().at( 1 ).toStringList().join(
',' ) );
3405 elseValue = u
"array(%1)"_s.arg( json.constLast().toStringList().join(
',' ) );
3412 if ( json.constLast().toList().count() == 2 && json.constLast().toList().first().toString() ==
"literal"_L1 )
3414 QStringList dashValues = json.constLast().toList().at( 1 ).toStringList();
3415 if ( dashValues.length() % 2 == 1 )
3417 dashValues << u
"0"_s;
3419 elseValue = u
"array(%1)"_s.arg( dashValues.join(
',' ) );
3423 QStringList dashValues = json.constLast().toStringList();
3424 if ( dashValues.length() % 2 == 1 )
3426 dashValues << u
"0"_s;
3428 elseValue = u
"array(%1)"_s.arg( dashValues.join(
',' ) );
3437 caseString += u
"ELSE %1 END"_s.arg( elseValue );
3445 const QString expression =
parseExpression( json.value( 1 ).toList(), context );
3446 if ( expression.isEmpty() )
3448 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3452 QString caseString = u
"CASE "_s;
3455 for (
int i = json.length() - 2; i > 0; i -= 2 )
3457 const QVariant stepValue = json.value( i + 1 );
3459 QString valueString;
3470 const QColor color =
parseColor( stepValue, context );
3477 const double v = stepValue.toDouble() * multiplier;
3478 valueString = QString::number( v );
3484 const double v = stepValue.toDouble() * maxOpacity;
3485 valueString = QString::number( v );
3491 valueString = u
"array(%1,%2)"_s.arg( stepValue.toList().value( 0 ).toDouble() * multiplier ).arg( stepValue.toList().value( 0 ).toDouble() * multiplier );
3497 if ( stepValue.toList().count() == 2 && stepValue.toList().first().toString() ==
"literal"_L1 )
3499 valueString = u
"array(%1)"_s.arg( stepValue.toList().at( 1 ).toStringList().join(
',' ) );
3503 valueString = u
"array(%1)"_s.arg( stepValue.toStringList().join(
',' ) );
3510 if ( stepValue.toList().count() == 2 && stepValue.toList().first().toString() ==
"literal"_L1 )
3512 QStringList dashValues = stepValue.toList().at( 1 ).toStringList();
3513 if ( dashValues.length() % 2 == 1 )
3515 dashValues << u
"0"_s;
3517 valueString = u
"array(%1)"_s.arg( dashValues.join(
',' ) );
3521 QStringList dashValues = stepValue.toStringList();
3522 if ( dashValues.length() % 2 == 1 )
3524 dashValues << u
"0"_s;
3526 valueString = u
"array(%1)"_s.arg( dashValues.join(
',' ) );
3536 caseString += u
" WHEN %1 >= %2 THEN (%3) "_s.arg( expression, stepKey, valueString );
3540 caseString += u
"ELSE (%1) END"_s.arg( valueString );
3550 if ( json.value( 0 ).toString() !=
"interpolate"_L1 )
3552 context.
pushWarning( QObject::tr(
"%1: Could not interpret value list" ).arg( context.
layerId() ) );
3556 const QVariantList parts = json.
value( 1 ).toList();
3557 const QString technique = parts.value( 0 ).toString();
3561 if ( technique ==
"linear"_L1 )
3563 props.insert( u
"base"_s, 1 );
3566 else if ( technique ==
"exponential"_L1 )
3568 props.insert( u
"base"_s, parts.value( 1 ).toDouble() );
3571 else if ( technique ==
"cubic-bezier"_L1 )
3575 props.insert( u
"x1"_s, parts.value( 1 ).toDouble() );
3576 props.insert( u
"y1"_s, parts.value( 2 ).toDouble() );
3577 props.insert( u
"x2"_s, parts.value( 3 ).toDouble() );
3578 props.insert( u
"y2"_s, parts.value( 4 ).toDouble() );
3582 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation method %2" ).arg( context.
layerId(), technique ) );
3586 if ( json.value( 2 ).toList().value( 0 ).toString() !=
"zoom"_L1 )
3588 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation input %2" ).arg( context.
layerId(), json.value( 2 ).toString() ) );
3594 for (
int i = 3; i < json.length(); i += 2 )
3596 stops.push_back( QVariantList() << json.value( i ).toString() << json.value( i + 1 ) );
3599 props.insert( u
"stops"_s, stops );
3617 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported numeric array in interpolate" ).arg( context.
layerId() ) );
3625 if ( ( QMetaType::Type ) colorExpression.userType() == QMetaType::QVariantList )
3629 return parseValue( colorExpression, context,
true );
3634 if ( color.userType() != QMetaType::Type::QString )
3636 context.
pushWarning( QObject::tr(
"%1: Could not parse non-string color %2, skipping" ).arg( context.
layerId(), color.toString() ) );
3645 hue = std::max( 0, color.hslHue() );
3646 saturation = color.hslSaturation() / 255.0 * 100;
3647 lightness = color.lightness() / 255.0 * 100;
3648 alpha = color.alpha();
3652 double zoomMin,
double zoomMax, QVariant valueMin, QVariant valueMax,
double base,
double multiplier,
double x1,
double y1,
double x2,
double y2,
InterpolationType type,
QgsMapBoxGlStyleConversionContext *contextPtr
3658 context = *contextPtr;
3662 if ( valueMin.canConvert( QMetaType::Double ) && valueMax.canConvert( QMetaType::Double ) )
3664 bool minDoubleOk =
true;
3665 const double min = valueMin.toDouble( &minDoubleOk );
3666 bool maxDoubleOk =
true;
3667 const double max = valueMax.toDouble( &maxDoubleOk );
3668 if ( minDoubleOk && maxDoubleOk &&
qgsDoubleNear( min, max ) )
3670 return QString::number( min * multiplier );
3674 QString minValueExpr = valueMin.toString();
3675 QString maxValueExpr = valueMax.toString();
3676 if ( valueMin.userType() == QMetaType::Type::QVariantList )
3680 if ( valueMax.userType() == QMetaType::Type::QVariantList )
3686 if ( minValueExpr == maxValueExpr )
3688 expression = minValueExpr;
3695 expression = u
"scale_linear(@vector_tile_zoom,%1,%2,%3,%4)"_s.arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr );
3700 expression = u
"scale_linear(@vector_tile_zoom,%1,%2,%3,%4)"_s.arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr );
3704 expression = u
"scale_exponential(@vector_tile_zoom,%1,%2,%3,%4,%5)"_s.arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr ).arg( base );
3709 expression = u
"scale_cubic_bezier(@vector_tile_zoom,%1,%2,%3,%4,%5,%6,%7,%8)"_s.arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr ).arg( x1 ).arg( y1 ).arg( x2 ).arg( y2 );
3714 if ( multiplier != 1 )
3715 return u
"(%1) * %2"_s.arg( expression ).arg( multiplier );
3722 if ( style ==
"round"_L1 )
3723 return Qt::RoundCap;
3724 else if ( style ==
"square"_L1 )
3725 return Qt::SquareCap;
3732 if ( style ==
"bevel"_L1 )
3733 return Qt::BevelJoin;
3734 else if ( style ==
"round"_L1 )
3735 return Qt::RoundJoin;
3737 return Qt::MiterJoin;
3742 QString op = expression.value( 0 ).toString();
3743 if ( ( op ==
"%"_L1 || op ==
"/"_L1 || op ==
"-"_L1 || op ==
"^"_L1 ) && expression.size() >= 3 )
3745 if ( expression.size() != 3 )
3747 context.
pushWarning( QObject::tr(
"%1: Operator %2 requires exactly two operands, skipping extra operands" ).arg( context.
layerId() ).arg( op ) );
3749 QString v1 = parseValue( expression.value( 1 ), context, colorExpected );
3750 QString v2 = parseValue( expression.value( 2 ), context, colorExpected );
3751 return u
"(%1 %2 %3)"_s.arg( v1, op, v2 );
3753 else if ( ( op ==
"*"_L1 || op ==
"+"_L1 ) && expression.size() >= 3 )
3755 QStringList operands;
3756 std::transform( std::next( expression.begin() ), expression.end(), std::back_inserter( operands ), [&context, colorExpected](
const QVariant &val ) {
3757 return parseValue( val, context, colorExpected );
3759 return u
"(%1)"_s.arg( operands.join( u
" %1 "_s.arg( op ) ) );
3761 else if ( op ==
"to-number"_L1 )
3763 return u
"to_real(%1)"_s.arg( parseValue( expression.value( 1 ), context ) );
3765 else if ( op ==
"sqrt"_L1 )
3767 return u
"sqrt(%1)"_s.arg( parseValue( expression.value( 1 ), context ) );
3769 else if ( op ==
"literal"_L1 )
3771 return expression.value( 1 ).toString();
3773 else if ( op ==
"all"_L1 || op ==
"any"_L1 || op ==
"none"_L1 )
3776 for (
int i = 1; i < expression.size(); ++i )
3778 const QString part = parseValue( expression.at( i ), context );
3779 if ( part.isEmpty() )
3781 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3787 if ( op ==
"none"_L1 )
3788 return u
"NOT (%1)"_s.arg( parts.join(
") AND NOT ("_L1 ) );
3790 QString operatorString;
3791 if ( op ==
"all"_L1 )
3792 operatorString = u
") AND ("_s;
3793 else if ( op ==
"any"_L1 )
3794 operatorString = u
") OR ("_s;
3796 return u
"(%1)"_s.arg( parts.join( operatorString ) );
3798 else if ( op ==
'!' )
3801 QVariantList contraJsonExpr = expression.value( 1 ).toList();
3802 contraJsonExpr[0] = QString( op + contraJsonExpr[0].toString() );
3804 return parseKey( contraJsonExpr, context );
3806 else if ( op ==
"=="_L1 || op ==
"!="_L1 || op ==
">="_L1 || op ==
'>' || op ==
"<="_L1 || op ==
'<' )
3809 if ( op ==
"=="_L1 )
3811 else if ( op ==
"!="_L1 )
3813 return u
"%1 %2 %3"_s.arg( parseKey( expression.value( 1 ), context ), op, parseValue( expression.value( 2 ), context ) );
3815 else if ( op ==
"has"_L1 )
3817 return parseKey( expression.value( 1 ), context ) + u
" IS NOT NULL"_s;
3819 else if ( op ==
"!has"_L1 )
3821 return parseKey( expression.value( 1 ), context ) + u
" IS NULL"_s;
3823 else if ( op ==
"in"_L1 || op ==
"!in"_L1 )
3825 const QString key = parseKey( expression.value( 1 ), context );
3828 QVariantList values = expression.mid( 2 );
3829 if ( expression.size() == 3 && expression.at( 2 ).userType() == QMetaType::Type::QVariantList && expression.at( 2 ).toList().count() > 1 && expression.at( 2 ).toList().at( 0 ).toString() ==
"literal"_L1 )
3831 values = expression.at( 2 ).toList().at( 1 ).toList();
3834 for (
const QVariant &value : std::as_const( values ) )
3836 const QString part = parseValue( value, context );
3837 if ( part.isEmpty() )
3839 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3845 if ( parts.size() == 1 )
3847 if ( op ==
"in"_L1 )
3848 return u
"%1 IS %2"_s.arg( key, parts.at( 0 ) );
3850 return u
"(%1 IS NULL OR %1 IS NOT %2)"_s.arg( key, parts.at( 0 ) );
3854 if ( op ==
"in"_L1 )
3855 return u
"%1 IN (%2)"_s.arg( key, parts.join(
", "_L1 ) );
3857 return u
"(%1 IS NULL OR %1 NOT IN (%2))"_s.arg( key, parts.join(
", "_L1 ) );
3860 else if ( op ==
"get"_L1 )
3862 return parseKey( expression.value( 1 ), context );
3864 else if ( op ==
"match"_L1 )
3866 const QVariantList attributeList = expression.value( 1 ).toList();
3867 QString attribute = attributeList.size() > 1 ? expression.value( 1 ).toList().value( 1 ).toString() : expression.value( 1 ).toList().value( 0 ).toString();
3868 if ( attribute ==
"geometry-type"_L1 )
3870 attribute = u
"_geom_type"_s;
3873 if ( expression.size() == 5
3874 && expression.at( 3 ).userType() == QMetaType::Type::Bool
3875 && expression.at( 3 ).toBool() ==
true
3876 && expression.at( 4 ).userType() == QMetaType::Type::Bool
3877 && expression.at( 4 ).toBool() ==
false )
3880 if ( expression.at( 2 ).userType() == QMetaType::Type::QVariantList || expression.at( 2 ).userType() == QMetaType::Type::QStringList )
3883 for (
const QVariant &p : expression.at( 2 ).toList() )
3885 parts << parseValue( p, context );
3888 if ( parts.size() > 1 )
3893 else if ( expression.at( 2 ).userType() == QMetaType::Type::QString
3894 || expression.at( 2 ).userType() == QMetaType::Type::Int
3895 || expression.at( 2 ).userType() == QMetaType::Type::Double
3896 || expression.at( 2 ).userType() == QMetaType::Type::LongLong )
3902 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3908 QString caseString = u
"CASE "_s;
3909 for (
int i = 2; i < expression.size() - 2; i += 2 )
3911 if ( expression.at( i ).userType() == QMetaType::Type::QVariantList || expression.at( i ).userType() == QMetaType::Type::QStringList )
3914 for (
const QVariant &p : expression.at( i ).toList() )
3919 if ( parts.size() > 1 )
3924 else if ( expression.at( i ).userType() == QMetaType::Type::QString
3925 || expression.at( i ).userType() == QMetaType::Type::Int
3926 || expression.at( i ).userType() == QMetaType::Type::Double
3927 || expression.at( i ).userType() == QMetaType::Type::LongLong )
3932 caseString += u
"THEN %1 "_s.arg( parseValue( expression.at( i + 1 ), context, colorExpected ) );
3934 caseString += u
"ELSE %1 END"_s.arg( parseValue( expression.last(), context, colorExpected ) );
3938 else if ( op ==
"to-string"_L1 )
3940 return u
"to_string(%1)"_s.arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3942 else if ( op ==
"to-boolean"_L1 )
3944 return u
"to_bool(%1)"_s.arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3946 else if ( op ==
"case"_L1 )
3948 QString caseString = u
"CASE"_s;
3949 for (
int i = 1; i < expression.size() - 2; i += 2 )
3951 const QString condition =
parseExpression( expression.value( i ).toList(), context );
3952 const QString value = parseValue( expression.value( i + 1 ), context, colorExpected );
3953 caseString += u
" WHEN (%1) THEN %2"_s.arg( condition, value );
3955 const QString value = parseValue( expression.constLast(), context, colorExpected );
3956 caseString += u
" ELSE %1 END"_s.arg( value );
3959 else if ( op ==
"zoom"_L1 && expression.count() == 1 )
3961 return u
"@vector_tile_zoom"_s;
3963 else if ( op ==
"coalesce"_L1 )
3965 QString coalesceString = u
"coalesce("_s;
3966 for (
int i = 1; i < expression.size(); i++ )
3969 coalesceString +=
", "_L1;
3970 coalesceString += parseValue( expression.value( i ), context );
3972 coalesceString +=
')'_L1;
3973 return coalesceString;
3975 else if ( op ==
"concat"_L1 )
3977 QString concatString = u
"concat("_s;
3978 for (
int i = 1; i < expression.size(); i++ )
3981 concatString +=
", "_L1;
3982 concatString += parseValue( expression.value( i ), context );
3984 concatString +=
')'_L1;
3985 return concatString;
3987 else if ( op ==
"length"_L1 )
3989 return u
"length(%1)"_s.arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3991 else if ( op ==
"step"_L1 )
3993 const QString stepExpression =
parseExpression( expression.value( 1 ).toList(), context );
3994 if ( stepExpression.isEmpty() )
3996 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
4000 QString caseString = u
"CASE "_s;
4002 for (
int i = expression.length() - 2; i > 0; i -= 2 )
4004 const QString stepValue = parseValue( expression.value( i + 1 ), context, colorExpected );
4008 caseString += u
" WHEN %1 >= %2 THEN (%3) "_s.arg( stepExpression, stepKey, stepValue );
4012 caseString += u
"ELSE (%1) END"_s.arg( stepValue );
4017 else if ( op ==
"pitch"_L1 )
4021 else if ( op ==
"slice"_L1 )
4026 const QString inputExpression = parseValue( expression.value( 1 ), context );
4027 if ( inputExpression.isEmpty() )
4029 context.
pushWarning( QObject::tr(
"%1: Could not interpret slice list" ).arg( context.
layerId() ) );
4035 const auto constantInt = [](
const QVariant &value,
int &result ) ->
bool {
4036 switch ( value.userType() )
4038 case QMetaType::Int:
4039 case QMetaType::UInt:
4040 case QMetaType::LongLong:
4041 case QMetaType::ULongLong:
4044 result = value.toInt( &ok );
4053 const bool startIsConstant = constantInt( expression.value( 2 ), startValue );
4054 const QString startExpression = parseValue( expression.value( 2 ), context );
4055 const QString startOffset = startIsConstant ? QString::number( startValue + 1 ) : u
"(%1) + 1"_s.arg( startExpression );
4057 if ( expression.size() > 3 )
4060 const bool endIsConstant = constantInt( expression.value( 3 ), endValue );
4061 const QString endExpression = parseValue( expression.value( 3 ), context );
4062 const QString length = ( startIsConstant && endIsConstant ) ? QString::number( endValue - startValue ) : u
"(%1) - (%2)"_s.arg( endExpression, startExpression );
4063 return u
"substr(%1, %2, %3)"_s.arg( inputExpression, startOffset, length );
4067 return u
"substr(%1, %2)"_s.arg( inputExpression, startOffset );
4072 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression \"%2\"" ).arg( context.
layerId(), op ) );
4081 if ( name.isEmpty() )
4087 QString actualName = name;
4088 const int categorySeparator = name.indexOf(
':' );
4089 if ( categorySeparator > 0 )
4091 category = name.left( categorySeparator );
4094 actualName = name.mid( categorySeparator + 1 );
4103 if ( category.isEmpty() )
4108 category = u
"default"_s;
4112 if ( spriteImage.isNull() )
4114 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
4118 const QVariantMap spriteDefinition = context.
spriteDefinitions( category ).value( actualName ).toMap();
4119 if ( spriteDefinition.size() == 0 )
4121 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
4126 = spriteImage.copy( spriteDefinition.value( u
"x"_s ).toInt(), spriteDefinition.value( u
"y"_s ).toInt(), spriteDefinition.value( u
"width"_s ).toInt(), spriteDefinition.value( u
"height"_s ).toInt() );
4127 if ( sprite.isNull() )
4129 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
4133 spriteSize = sprite.size() / spriteDefinition.value( u
"pixelRatio"_s ).toDouble() * context.
pixelSizeConversionFactor();
4143 auto prepareBase64 = [](
const QImage &sprite ) {
4145 if ( !sprite.isNull() )
4148 QBuffer buffer( &blob );
4149 buffer.open( QIODevice::WriteOnly );
4150 sprite.save( &buffer,
"PNG" );
4152 const QByteArray encoded = blob.toBase64();
4153 path = QString( encoded );
4154 path.prepend(
"base64:"_L1 );
4159 switch ( value.userType() )
4161 case QMetaType::Type::QString:
4163 QString spriteName = value.toString();
4164 const thread_local QRegularExpression fieldNameMatch( u
"{([^}]+)}"_s );
4165 QRegularExpressionMatch match = fieldNameMatch.match( spriteName );
4166 if ( match.hasMatch() )
4168 const QString fieldName = match.captured( 1 );
4169 spriteProperty = u
"CASE"_s;
4170 spriteSizeProperty = u
"CASE"_s;
4172 spriteName.replace(
"(",
"\\("_L1 );
4173 spriteName.replace(
")",
"\\)"_L1 );
4174 spriteName.replace( fieldNameMatch, u
"([^\\/\\\\]+)"_s );
4175 const QRegularExpression fieldValueMatch( spriteName );
4177 for (
const QString &name : spriteNames )
4179 match = fieldValueMatch.match( name );
4180 if ( match.hasMatch() )
4184 const QString fieldValue = match.captured( 1 );
4186 path = prepareBase64( sprite );
4187 if ( spritePath.isEmpty() && !path.isEmpty() )
4193 spriteProperty += u
" WHEN \"%1\" = '%2' THEN '%3'"_s.arg( fieldName, fieldValue, path );
4194 spriteSizeProperty += u
" WHEN \"%1\" = '%2' THEN %3"_s.arg( fieldName ).arg( fieldValue ).arg( size.width() );
4198 spriteProperty +=
" END"_L1;
4199 spriteSizeProperty +=
" END"_L1;
4203 spriteProperty.clear();
4204 spriteSizeProperty.clear();
4205 const QImage sprite =
retrieveSprite( spriteName, context, spriteSize );
4206 spritePath = prepareBase64( sprite );
4211 case QMetaType::Type::QVariantMap:
4213 const QVariantList stops = value.toMap().value( u
"stops"_s ).toList();
4214 if ( stops.size() == 0 )
4221 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, spriteSize );
4222 spritePath = prepareBase64( sprite );
4224 spriteProperty = u
"CASE WHEN @vector_tile_zoom < %1 THEN '%2'"_s.arg( stops.value( 0 ).toList().value( 0 ).toString() ).arg( spritePath );
4225 spriteSizeProperty = u
"CASE WHEN @vector_tile_zoom < %1 THEN %2"_s.arg( stops.value( 0 ).toList().value( 0 ).toString() ).arg( spriteSize.width() );
4227 for (
int i = 0; i < stops.size() - 1; ++i )
4230 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, size );
4231 path = prepareBase64( sprite );
4233 spriteProperty += QStringLiteral(
4234 " WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
4237 .arg( stops.value( i ).toList().value( 0 ).toString(), stops.value( i + 1 ).toList().value( 0 ).toString(), path );
4238 spriteSizeProperty += QStringLiteral(
4239 " WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
4242 .arg( stops.value( i ).toList().value( 0 ).toString(), stops.value( i + 1 ).toList().value( 0 ).toString() )
4243 .arg( size.width() );
4245 sprite =
retrieveSprite( stops.last().toList().value( 1 ).toString(), context, size );
4246 path = prepareBase64( sprite );
4248 spriteProperty += QStringLiteral(
4249 " WHEN @vector_tile_zoom >= %1 "
4252 .arg( stops.last().toList().value( 0 ).toString() )
4254 spriteSizeProperty += QStringLiteral(
4255 " WHEN @vector_tile_zoom >= %1 "
4258 .arg( stops.last().toList().value( 0 ).toString() )
4259 .arg( size.width() );
4263 case QMetaType::Type::QVariantList:
4265 const QVariantList json = value.toList();
4266 const QString method = json.value( 0 ).toString();
4268 if ( method ==
"match"_L1 )
4270 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
4271 if ( attribute.isEmpty() )
4273 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
4277 spriteProperty = u
"CASE"_s;
4278 spriteSizeProperty = u
"CASE"_s;
4280 for (
int i = 2; i < json.length() - 1; i += 2 )
4282 const QVariant matchKey = json.value( i );
4283 const QVariant matchValue = json.value( i + 1 );
4284 QString matchString;
4285 switch ( matchKey.userType() )
4287 case QMetaType::Type::QVariantList:
4288 case QMetaType::Type::QStringList:
4290 const QVariantList keys = matchKey.toList();
4291 QStringList matchStringList;
4292 for (
const QVariant &key : keys )
4296 matchString = matchStringList.join(
',' );
4300 case QMetaType::Type::Bool:
4301 case QMetaType::Type::QString:
4302 case QMetaType::Type::Int:
4303 case QMetaType::Type::LongLong:
4304 case QMetaType::Type::Double:
4311 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported sprite type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( value.userType() ) ) ) );
4315 QString valuePathExpression;
4316 QString valueSizeExpression;
4317 if ( matchValue.userType() == QMetaType::Type::QVariantList || matchValue.userType() == QMetaType::Type::QStringList )
4321 QString nestedProperty;
4322 QString nestedSizeProperty;
4324 valuePathExpression = nestedProperty.isEmpty() ? u
"'%1'"_s.arg( nestedPath ) : nestedProperty;
4325 valueSizeExpression = nestedSizeProperty.isEmpty() ? QString::number( nestedSize.width() ) : nestedSizeProperty;
4326 spritePath = nestedPath;
4327 spriteSize = nestedSize;
4331 const QImage sprite =
retrieveSprite( matchValue.toString(), context, spriteSize );
4332 spritePath = prepareBase64( sprite );
4333 valuePathExpression = u
"'%1'"_s.arg( spritePath );
4334 valueSizeExpression = QString::number( spriteSize.width() );
4337 spriteProperty += u
" WHEN %1 IN (%2) THEN %3"_s.arg( attribute, matchString, valuePathExpression );
4338 spriteSizeProperty += u
" WHEN %1 IN (%2) THEN %3"_s.arg( attribute, matchString, valueSizeExpression );
4341 const QVariant defaultValue = json.constLast();
4342 if ( defaultValue.userType() == QMetaType::Type::QVariantList || defaultValue.userType() == QMetaType::Type::QStringList )
4346 QString nestedProperty;
4347 QString nestedSizeProperty;
4349 spriteProperty += u
" ELSE %1 END"_s.arg( nestedProperty.isEmpty() ? u
"'%1'"_s.arg( nestedPath ) : nestedProperty );
4350 spriteSizeProperty += u
" ELSE %1 END"_s.arg( nestedSizeProperty.isEmpty() ? QString::number( nestedSize.width() ) : nestedSizeProperty );
4351 spritePath = nestedPath;
4352 spriteSize = nestedSize;
4356 if ( !defaultValue.toString().isEmpty() )
4358 const QImage sprite =
retrieveSprite( defaultValue.toString(), context, spriteSize );
4359 spritePath = prepareBase64( sprite );
4363 spritePath = QString();
4366 spriteProperty += u
" ELSE '%1' END"_s.arg( spritePath );
4367 spriteSizeProperty += u
" ELSE %3 END"_s.arg( spriteSize.width() );
4371 else if ( method ==
"step"_L1 )
4373 const QString expression =
parseExpression( json.value( 1 ).toList(), context );
4374 if ( expression.isEmpty() )
4376 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
4380 spriteProperty = u
"CASE"_s;
4381 spriteSizeProperty = u
"CASE"_s;
4382 for (
int i = json.length() - 2; i > 2; i -= 2 )
4385 const QString stepValue = json.value( i + 1 ).toString();
4387 const QImage sprite =
retrieveSprite( stepValue, context, spriteSize );
4388 spritePath = prepareBase64( sprite );
4390 spriteProperty += u
" WHEN %1 >= %2 THEN '%3' "_s.arg( expression, stepKey, spritePath );
4391 spriteSizeProperty += u
" WHEN %1 >= %2 THEN %3 "_s.arg( expression ).arg( stepKey ).arg( spriteSize.width() );
4394 const QImage sprite =
retrieveSprite( json.at( 2 ).toString(), context, spriteSize );
4395 spritePath = prepareBase64( sprite );
4397 spriteProperty += u
"ELSE '%1' END"_s.arg( spritePath );
4398 spriteSizeProperty += u
"ELSE %3 END"_s.arg( spriteSize.width() );
4401 else if ( method ==
"concat"_L1 )
4403 QString concatString = u
"concat("_s;
4404 QStringList partialStringMatches;
4405 for (
int i = 1; i < json.size(); i++ )
4409 concatString +=
", "_L1;
4411 concatString += parseValue( json.value( i ), context );
4413 if ( json.value( i ).userType() == QMetaType::Type::QString )
4415 partialStringMatches << json.value( i ).toString();
4418 concatString +=
')'_L1;
4420 spriteProperty = u
"CASE"_s;
4421 spriteSizeProperty = u
"CASE"_s;
4424 for (
const QString &category : spriteCategories )
4427 for (
const QString &name : spriteNames )
4431 const QString spriteName = category.isEmpty() ? name : u
"%1:%2"_s.arg( category, name );
4432 if ( !partialStringMatches.isEmpty() )
4434 if ( !std::all_of( partialStringMatches.begin(), partialStringMatches.end(), [&spriteName](
const QString &
string ) { return spriteName.contains( string ); } ) )
4440 const QImage sprite =
retrieveSprite( spriteName, context, size );
4441 path = prepareBase64( sprite );
4442 if ( spritePath.isEmpty() && !path.isEmpty() )
4448 spriteProperty += u
" WHEN %1 = '%2' THEN '%3'"_s.arg( concatString, spriteName, path );
4449 spriteSizeProperty += u
" WHEN %1 = '%2' THEN %3"_s.arg( concatString ).arg( spriteName ).arg( size.width() );
4452 spriteProperty +=
" ELSE '' END"_L1;
4453 spriteSizeProperty +=
" ELSE 0 END"_L1;
4457 else if ( method ==
"case"_L1 )
4459 spriteProperty = u
"CASE"_s;
4460 spriteSizeProperty = u
"CASE"_s;
4461 for (
int i = 1; i < json.length() - 2; i += 2 )
4463 const QString caseExpression =
parseExpression( json.value( i ).toList(), context );
4464 const QString caseValue = json.value( i + 1 ).toString();
4466 const QImage sprite =
retrieveSprite( caseValue, context, spriteSize );
4467 spritePath = prepareBase64( sprite );
4469 spriteProperty += u
" WHEN %1 THEN '%2' "_s.arg( caseExpression, spritePath );
4470 spriteSizeProperty += u
" WHEN %1 THEN %2 "_s.arg( caseExpression ).arg( spriteSize.width() );
4472 const QImage sprite =
retrieveSprite( json.last().toString(), context, spriteSize );
4473 spritePath = prepareBase64( sprite );
4475 spriteProperty += u
"ELSE '%1' END"_s.arg( spritePath );
4476 spriteSizeProperty += u
"ELSE %3 END"_s.arg( spriteSize.width() );
4479 else if ( method ==
"get"_L1 )
4481 const QString attribute = parseKey( json.value( 1 ), context );
4483 spriteProperty = u
"CASE"_s;
4484 spriteSizeProperty = u
"CASE"_s;
4487 for (
const QString &category : spriteCategories )
4490 for (
const QString &name : spriteNames )
4494 const QString spriteName = category.isEmpty() ? name : u
"%1:%2"_s.arg( category, name );
4495 const QImage sprite =
retrieveSprite( spriteName, context, size );
4496 path = prepareBase64( sprite );
4497 if ( spritePath.isEmpty() && !path.isEmpty() )
4503 spriteProperty += u
" WHEN %1 = '%2' THEN '%3'"_s.arg( attribute, spriteName, path );
4504 spriteSizeProperty += u
" WHEN %1 = '%2' THEN %3"_s.arg( attribute ).arg( spriteName ).arg( size.width() );
4507 spriteProperty +=
" ELSE '' END"_L1;
4508 spriteSizeProperty +=
" ELSE 0 END"_L1;
4514 context.
pushWarning( QObject::tr(
"%1: Could not interpret sprite value list with method %2" ).arg( context.
layerId(), method ) );
4520 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported sprite type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( value.userType() ) ) ) );
4530 switch ( value.userType() )
4532 case QMetaType::Type::QVariantList:
4533 case QMetaType::Type::QStringList:
4536 case QMetaType::Type::Bool:
4537 case QMetaType::Type::QString:
4538 if ( colorExpected )
4543 return parseValue(
c, context );
4548 case QMetaType::Type::Int:
4549 case QMetaType::Type::LongLong:
4550 case QMetaType::Type::Double:
4551 return value.toString();
4553 case QMetaType::Type::QColor:
4554 c = value.value<QColor>();
4555 return QString(
"color_rgba(%1,%2,%3,%4)" ).arg(
c.red() ).arg(
c.green() ).arg(
c.blue() ).arg(
c.alpha() );
4558 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression part" ).arg( context.
layerId() ) );
4566 if ( value.toString() ==
"$type"_L1 )
4568 return u
"_geom_type"_s;
4570 if ( value.toString() ==
"level"_L1 )
4574 else if ( ( value.userType() == QMetaType::Type::QVariantList && value.toList().size() == 1 ) || value.userType() == QMetaType::Type::QStringList )
4576 if ( value.toList().size() > 1 )
4577 return value.toList().at( 1 ).toString();
4580 QString valueString = value.toList().value( 0 ).toString();
4581 if ( valueString ==
"geometry-type"_L1 )
4583 return u
"_geom_type"_s;
4588 else if ( value.userType() == QMetaType::Type::QVariantList && value.toList().size() > 1 )
4595QString QgsMapBoxGlStyleConverter::processLabelField(
const QString &
string,
bool &isExpression )
4599 const thread_local QRegularExpression singleFieldRx( u
"^{([^}]+)}$"_s );
4600 const QRegularExpressionMatch match = singleFieldRx.match(
string );
4601 if ( match.hasMatch() )
4603 isExpression =
false;
4604 return match.captured( 1 );
4607 const thread_local QRegularExpression multiFieldRx( u
"(?={[^}]+})"_s );
4608 const QStringList parts =
string.split( multiFieldRx );
4609 if ( parts.size() > 1 )
4611 isExpression =
true;
4614 for (
const QString &part : parts )
4616 if ( part.isEmpty() )
4619 if ( !part.contains(
'{' ) )
4626 const QStringList split = part.split(
'}' );
4628 if ( !split.at( 1 ).isEmpty() )
4631 return u
"concat(%1)"_s.arg( res.join(
',' ) );
4635 isExpression =
false;
4642 return mRenderer ? std::unique_ptr<QgsVectorTileRenderer>( mRenderer->clone() ) :
nullptr;
4647 return mLabeling ? std::unique_ptr<QgsVectorTileLabeling>( mLabeling->clone() ) :
nullptr;
4657 return mRasterSubLayers;
4662 QList<QgsMapLayer *> subLayers;
4665 const QString sourceName = subLayer.source();
4666 std::unique_ptr< QgsRasterLayer > rl;
4673 rl->pipe()->setDataDefinedProperties( subLayer.dataDefinedProperties() );
4680 subLayers.append( rl.release() );
4689 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
4692 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
4693 context = tmpContext.get();
4697 if (
string.compare(
"vector"_L1, Qt::CaseInsensitive ) == 0 )
4699 else if (
string.compare(
"raster"_L1, Qt::CaseInsensitive ) == 0 )
4701 else if (
string.compare(
"raster-dem"_L1, Qt::CaseInsensitive ) == 0 )
4703 else if (
string.compare(
"geojson"_L1, Qt::CaseInsensitive ) == 0 )
4705 else if (
string.compare(
"image"_L1, Qt::CaseInsensitive ) == 0 )
4707 else if (
string.compare(
"video"_L1, Qt::CaseInsensitive ) == 0 )
4709 context->
pushWarning( QObject::tr(
"Invalid source type \"%1\" for source \"%2\"" ).arg(
string, name ) );
4715 const QString name = it.key();
4716 const QVariantMap jsonSource = it.value().toMap();
4717 const QString typeString = jsonSource.value( u
"type"_s ).toString();
4740 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
4743 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
4744 context = tmpContext.get();
4747 auto raster = std::make_unique< QgsMapBoxGlStyleRasterSource >( name );
4748 if ( raster->setFromJson( source, context ) )
4749 mSources.append( raster.release() );
4752bool QgsMapBoxGlStyleConverter::numericArgumentsOnly(
const QVariant &bottomVariant,
const QVariant &topVariant,
double &bottom,
double &top )
4754 if ( bottomVariant.canConvert( QMetaType::Double ) && topVariant.canConvert( QMetaType::Double ) )
4756 bool bDoubleOk, tDoubleOk;
4757 bottom = bottomVariant.toDouble( &bDoubleOk );
4758 top = topVariant.toDouble( &tDoubleOk );
4759 return ( bDoubleOk && tDoubleOk );
4770 mWarnings << warning;
4785 return mSizeConversionFactor;
4790 mSizeConversionFactor = sizeConversionFactor;
4795 return mSpriteImage.keys();
4800 return mSpriteImage.contains( category ) ? mSpriteImage[category] : QImage();
4805 return mSpriteDefinitions.contains( category ) ? mSpriteDefinitions[category] : QVariantMap();
4810 mSpriteImage[category] = image;
4811 mSpriteDefinitions[category] = definitions;
4858 mAttribution = json.value( u
"attribution"_s ).toString();
4860 const QString scheme = json.value( u
"scheme"_s, u
"xyz"_s ).toString();
4861 if ( scheme.compare(
"xyz"_L1 ) == 0 )
4867 context->
pushWarning( QObject::tr(
"%1 scheme is not supported for raster source %2" ).arg( scheme,
name() ) );
4871 mMinZoom = json.value( u
"minzoom"_s, u
"0"_s ).toInt();
4872 mMaxZoom = json.value( u
"maxzoom"_s, u
"22"_s ).toInt();
4873 mTileSize = json.value( u
"tileSize"_s, u
"512"_s ).toInt();
4875 const QVariantList
tiles = json.value( u
"tiles"_s ).toList();
4876 for (
const QVariant &tile :
tiles )
4878 mTiles.append( tile.toString() );
4887 parts.insert( u
"type"_s, u
"xyz"_s );
4888 parts.insert( u
"url"_s, mTiles.value( 0 ) );
4890 if ( mTileSize == 256 )
4891 parts.insert( u
"tilePixelRation"_s, u
"1"_s );
4892 else if ( mTileSize == 512 )
4893 parts.insert( u
"tilePixelRation"_s, u
"2"_s );
4895 parts.insert( u
"zmax"_s, QString::number( mMaxZoom ) );
4896 parts.insert( u
"zmin"_s, QString::number( mMinZoom ) );
4899 return rl.release();
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
@ OnLine
Labels can be placed directly over a line feature.
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
@ CentralPoint
Place symbols at the mid point of the line.
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ Curved
Arranges candidates following the curvature of a line feature. Applies to line layers only.
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature or along a line feature....
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
@ FollowPlacement
Alignment follows placement of label, e.g., labels to the left of a feature will be drawn with right ...
RenderUnit
Rendering size units.
MapBoxGlStyleSourceType
Available MapBox GL style source types.
@ RasterDem
Raster DEM source.
@ Unknown
Other/unknown source type.
@ Viewport
Relative to the whole viewport/output device.
@ AllowOverlapAtNoCost
Labels may freely overlap other labels, at no cost.
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.
static QgsFontManager * fontManager()
Returns the application font manager, which manages available fonts and font installation for the QGI...
A paint effect which blurs a source picture, using a number of different blur methods.
void setBlurUnit(const Qgis::RenderUnit unit)
Sets the units used for the blur level (radius).
@ StackBlur
Stack blur, a fast but low quality blur. Valid blur level values are between 0 - 16.
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, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
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).
QString processFontFamilyName(const QString &name) const
Processes a font family name, applying any matching fontFamilyReplacements() to the name.
static QFont createFont(const QString &family, int pointSize=-1, int weight=-1, bool italic=false)
Creates a font with the specified family.
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(Qgis::LabelLinePlacementFlags 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,...
Contains general settings related to how labels are placed.
void setOverlapHandling(Qgis::LabelOverlapHandling handling)
Sets the technique used to handle overlapping labels.
void setAllowDegradedPlacement(bool allow)
Sets whether labels can be placed in inferior fallback positions if they cannot otherwise be placed.
void setQuadrant(Qgis::LabelQuadrantPosition quadrant)
Sets the quadrant in which to offset labels from the point.
Contains settings related to how the label engine removes candidate label positions and reduces the n...
void setAllowDuplicateRemoval(bool allow)
Sets whether duplicate label removal is permitted for this layer.
void setMinimumDistanceToDuplicateUnit(Qgis::RenderUnit unit)
Sets the unit for the minimum distance to labels with duplicate text.
virtual void setWidth(double width)
Sets the width of the line symbol layer.
void setOffset(double offset)
Sets the line's offset.
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the unit for the line's offset.
Abstract base class for MapBox GL style sources.
QString name() const
Returns the source's name.
virtual ~QgsMapBoxGlStyleAbstractSource()
QgsMapBoxGlStyleAbstractSource(const QString &name)
Constructor for QgsMapBoxGlStyleAbstractSource.
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.
QImage spriteImage(const QString &category=QString()) const
Returns the sprite image for a given category to use during conversion, or an invalid image if this i...
double pixelSizeConversionFactor() const
Returns the pixel size conversion factor, used to scale the original pixel sizes when converting styl...
void setTargetUnit(Qgis::RenderUnit targetUnit)
Sets the target unit type.
void setSprites(const QImage &image, const QVariantMap &definitions, const QString &category=QString())
Sets the sprite image and definitions JSON for a given category to use during conversion.
void setPixelSizeConversionFactor(double sizeConversionFactor)
Sets the pixel size conversion factor, used to scale the original pixel sizes when converting styles.
QVariantMap spriteDefinitions(const QString &category=QString()) const
Returns the sprite definitions for a given category to use during conversion.
Qgis::RenderUnit targetUnit() const
Returns the target unit type.
QString layerId() const
Returns the layer ID of the layer currently being converted.
QStringList spriteCategories() const
Returns the list of sprite categories to use during conversion, or an empty list of none is set.
void clearWarnings()
Clears the list of warning messages.
static QgsProperty parseInterpolateByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, double *defaultNumber=nullptr, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential)
Parses a numeric value which is interpolated by zoom range.
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 parseExpression(const QVariantList &expression, QgsMapBoxGlStyleConversionContext &context, bool colorExpected=false)
Converts a MapBox GL expression to a QGIS expression.
InterpolationType
Interpolation types, for interpolated value conversion.
@ Linear
Linear interpolation.
@ Exponential
Exponential interpolation.
@ CubicBezier
Cubic-bezier interpolation.
static QString parsePointStops(double base, const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential, double x1=0, double y1=0, double x2=0, double y2=0)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate point/...
PropertyType
Property types, for interpolated value conversion.
@ Point
Point/offset property.
@ DashArray
Dash array. Like numeric array, but must be even length array. Odd length arrays are considered as ha...
@ Numeric
Numeric property (e.g. line width, text size).
@ Opacity
Opacity property.
@ NumericArray
Numeric array for dash arrays or such.
QList< QgsMapBoxGlStyleAbstractSource * > sources()
Returns the list of converted sources.
QList< QgsMapBoxGlStyleRasterSubLayer > rasterSubLayers() const
Returns a list of raster sub layers contained in the style.
static Qt::PenJoinStyle parseJoinStyle(const QString &style)
Converts a value to Qt::PenJoinStyle enum from JSON value.
static QString interpolateExpression(double zoomMin, double zoomMax, QVariant valueMin, QVariant valueMax, double base, double multiplier=1, double x1=0, double y1=0, double x2=1, double y2=1, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential, QgsMapBoxGlStyleConversionContext *contextPtr=nullptr)
Generates an interpolation for values between valueMin and valueMax, scaled between the ranges zoomMi...
static QgsProperty parseInterpolateStringByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, const QVariantMap &conversionMap, QString *defaultString=nullptr)
Interpolates a string by zoom.
static QgsProperty parseStepList(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 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()
QList< QgsMapLayer * > createSubLayers() const
Returns a list of new map layers corresponding to sublayers of the style, e.g.
Result
Result of conversion.
@ Success
Conversion was successful.
@ NoLayerList
No layer list was found in JSON input.
QgsMapBoxGlStyleConverter()
Constructor for QgsMapBoxGlStyleConverter.
static QgsProperty parseInterpolateColorByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, QColor *defaultColor=nullptr, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential)
Parses a color value which is interpolated by zoom range.
static QImage retrieveSprite(const QString &name, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize)
Retrieves the sprite image with the specified name, taken from the specified context.
static QString parseLabelStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context)
Parses a list of interpolation stops containing label values.
void parseLayers(const QVariantList &layers, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse list of layers from JSON.
static QgsProperty parseInterpolateOpacityByZoom(const QVariantMap &json, int maxOpacity, QgsMapBoxGlStyleConversionContext *contextPtr=nullptr, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential)
Interpolates opacity with either scale_linear() or scale_exp() (depending on base value).
static QString retrieveSpriteAsBase64WithProperties(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...
std::unique_ptr< QgsVectorTileLabeling > labeling() const
Returns a new instance of a vector tile labeling representing the converted style,...
void parseSources(const QVariantMap &sources, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse list of sources from JSON.
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 QString parseOpacityStops(double base, const QVariantList &stops, int maxOpacity, QgsMapBoxGlStyleConversionContext &context, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential, double x1=0, double y1=0, double x2=1, double y2=1)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate alpha ...
static bool parseLineLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context)
Parses a line layer.
void parseRasterSource(const QVariantMap &source, const QString &name, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse a raster source from JSON.
std::unique_ptr< QgsVectorTileRenderer > renderer() const
Returns a new instance of a vector tile renderer representing the converted style,...
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 QString parseStops(double base, const QVariantList &stops, double multiplier, QgsMapBoxGlStyleConversionContext &context, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential, double x1=0, double y1=0, double x2=1, double y2=1)
Parses a list of interpolation stops.
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 QgsProperty parseInterpolatePointByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, QPointF *defaultPoint=nullptr, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential)
Interpolates a point/offset with either scale_linear() or scale_exp() (depending on base value).
static QString parseArrayStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier=1)
Takes numerical arrays from stops.
Encapsulates a MapBox GL style raster source.
Qgis::MapBoxGlStyleSourceType type() const override
Returns the source type.
QgsMapBoxGlStyleRasterSource(const QString &name)
Constructor for QgsMapBoxGlStyleRasterSource.
QgsRasterLayer * toRasterLayer() const
Returns a new raster layer representing the raster source, or nullptr if the source cannot be represe...
bool setFromJson(const QVariantMap &json, QgsMapBoxGlStyleConversionContext *context) override
Sets the source's state from a json map.
QStringList tiles() const
Returns the list of tile sources.
Encapsulates a MapBox GL style raster sub layer.
QString source() const
Returns the layer's source.
QString id() const
Returns the layer's ID.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the layer's data defined properties.
QgsMapBoxGlStyleRasterSubLayer(const QString &id, const QString &source)
Constructor for QgsMapBoxGlStyleRasterSubLayer, with the given id and source.
Line symbol layer type which draws repeating marker symbols along a line feature.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
virtual void setSize(double size)
Sets the symbol size.
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's offset.
void setAngle(double angle)
Sets the rotation angle for the marker.
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's size.
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.
const QgsLabelObstacleSettings & obstacleSettings() const
Returns the label obstacle settings.
const QgsLabelPlacementSettings & placementSettings() const
Returns the label placement settings.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
double xOffset
Horizontal offset of label.
Qgis::LabelPlacement placement
Label placement mode.
Qgis::LabelMultiLineAlignment multilineAlign
Horizontal alignment of multi-line labels.
int priority
Label priority.
double angleOffset
Label rotation, in degrees clockwise.
const QgsLabelThinningSettings & thinningSettings() const
Returns the label thinning settings.
void setThinningSettings(const QgsLabelThinningSettings &settings)
Sets the label thinning settings.
Qgis::RenderUnit offsetUnits
Units for offsets of label.
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'
void setPlacementSettings(const QgsLabelPlacementSettings &settings)
Sets the label placement settings.
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
double dist
Distance from feature to the label.
Qgis::RenderUnit distUnits
Units the distance from feature to the label.
@ LinePlacementOptions
Line placement flags.
@ LabelRotation
Label rotation.
@ FontStyle
Font style name.
@ RemoveDuplicateLabelDistance
Minimum distance from labels for this feature to other labels with duplicate text.
@ FontLetterSpacing
Letter spacing.
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...
const QgsLabelPointSettings & pointSettings() const
Returns the label point settings, which contain settings related to how the label engine places and f...
A grouped map of multiple QgsProperty objects, each referenced by an integer key value.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const final
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 final
Returns true if the collection contains an active property with the specified key.
QgsProperty property(int key) const final
Returns a matching property from the collection, if one exists.
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.
void setExpressionString(const QString &expression)
Sets the expression to use for the property value.
static QgsProperty fromValue(const QVariant &value, bool isActive=true)
Returns a new StaticProperty created from the specified value.
void setActive(bool active)
Sets whether the property is currently active.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
A fill symbol layer which fills polygons with a repeated raster image.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the unit for the image's width and height.
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 setWidth(double width)
Sets the width for scaling the image used in the fill.
void setCoordinateMode(Qgis::SymbolCoordinateReference mode)
Set the coordinate mode for fill.
Represents a raster layer.
Line symbol layer type which draws line sections using a raster image file.
void setOutputUnit(Qgis::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.
@ RendererOpacity
Raster renderer global opacity.
Renders polygons using a single fill and stroke color.
void setBrushStyle(Qt::BrushStyle style)
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setStrokeWidth(double strokeWidth)
void setStrokeStyle(Qt::PenStyle strokeStyle)
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the unit for the fill's offset.
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.
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 setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the pen join style used to render the line (e.g.
Simple marker symbol layer, consisting of a rendered shape with solid fill color and a stroke.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
void setStrokeWidthUnit(Qgis::RenderUnit u)
Sets the unit for the width of the marker's stroke.
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,...
@ File
Filename, eg for svg files.
@ CustomDash
Custom dash pattern.
@ Name
Name, eg shape name for simple markers.
@ StrokeColor
Stroke color.
@ Interval
Line marker interval.
@ StrokeWidth
Stroke width.
@ LayerEnabled
Whether symbol layer is enabled.
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.
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 setSizeUnit(Qgis::RenderUnit unit)
Sets the units used for the shape's size.
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 setColor(const QColor &color)
Sets the color for the buffer.
void setOpacity(double opacity)
Sets the buffer opacity.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units used for the buffer size.
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 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(Qgis::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.
Configuration of a single style within QgsVectorTileBasicLabeling.
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 setGeometryType(Qgis::GeometryType geomType)
Sets type of the geometry that will be used (point / line / polygon).
void setLabelSettings(const QgsPalLayerSettings &settings)
Sets labeling configuration of this style.
void setEnabled(bool enabled)
Sets whether this style is enabled (used for rendering).
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 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).
void setGeometryType(Qgis::GeometryType geomType)
Sets type of the geometry that will be used (point / line / polygon).
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
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
#define QgsDebugError(str)
QList< QgsSymbolLayer * > QgsSymbolLayerList