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:
392 QObject::tr(
"%1: Skipping unsupported fill-translate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillTranslate.userType() ) ) )
399 Q_ASSERT( fillSymbol );
402 symbol->setOutputUnit( context.
targetUnit() );
405 if ( !fillTranslate.isNull() )
411 if ( jsonPaint.contains( isBackgroundStyle ? u
"background-pattern"_s : u
"fill-pattern"_s ) )
415 const QVariant fillPatternJson = jsonPaint.value( isBackgroundStyle ? u
"background-pattern"_s : u
"fill-pattern"_s );
418 fillColor = QColor();
419 fillOutlineColor = QColor();
426 QString spriteProperty, spriteSizeProperty;
428 if ( !sprite.isEmpty() )
433 rasterFill->
setWidth( spriteSize.width() );
437 if ( rasterOpacity >= 0 )
442 if ( !spriteProperty.isEmpty() )
449 symbol->appendSymbolLayer( rasterFill );
455 if ( fillOpacity != -1 )
457 symbol->setOpacity( fillOpacity );
468 if ( fillOutlineColor.isValid() && ( fillOutlineColor.alpha() == 255 || fillOutlineColor != fillColor ) )
479 if ( fillColor.isValid() )
483 else if ( colorIsDataDefined )
499 if ( !jsonLayer.contains( u
"paint"_s ) )
501 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
506 QString rasterLineSprite;
508 const QVariantMap jsonPaint = jsonLayer.
value( u
"paint"_s ).toMap();
509 if ( jsonPaint.contains( u
"line-pattern"_s ) )
511 const QVariant jsonLinePattern = jsonPaint.value( u
"line-pattern"_s );
512 switch ( jsonLinePattern.userType() )
514 case QMetaType::Type::QVariantMap:
515 case QMetaType::Type::QString:
518 QString spriteProperty, spriteSizeProperty;
524 case QMetaType::Type::QVariantList:
525 case QMetaType::Type::QStringList:
530 if ( rasterLineSprite.isEmpty() )
533 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-pattern property" ).arg( context.
layerId() ) );
540 if ( jsonPaint.contains( u
"line-color"_s ) )
542 const QVariant jsonLineColor = jsonPaint.value( u
"line-color"_s );
543 switch ( jsonLineColor.userType() )
545 case QMetaType::Type::QVariantMap:
550 case QMetaType::Type::QVariantList:
551 case QMetaType::Type::QStringList:
556 case QMetaType::Type::QString:
557 lineColor =
parseColor( jsonLineColor.toString(), context );
561 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineColor.userType() ) ) ) );
568 lineColor = QColor( 0, 0, 0 );
574 if ( jsonPaint.contains( u
"line-width"_s ) )
576 const QVariant jsonLineWidth = jsonPaint.value( u
"line-width"_s );
577 switch ( jsonLineWidth.userType() )
579 case QMetaType::Type::Int:
580 case QMetaType::Type::LongLong:
581 case QMetaType::Type::Double:
585 case QMetaType::Type::QVariantMap:
597 case QMetaType::Type::QVariantList:
598 case QMetaType::Type::QStringList:
610 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineWidth.userType() ) ) ) );
615 double lineOffset = 0.0;
616 if ( jsonPaint.contains( u
"line-offset"_s ) )
618 const QVariant jsonLineOffset = jsonPaint.value( u
"line-offset"_s );
619 switch ( jsonLineOffset.userType() )
621 case QMetaType::Type::Int:
622 case QMetaType::Type::LongLong:
623 case QMetaType::Type::Double:
627 case QMetaType::Type::QVariantMap:
632 case QMetaType::Type::QVariantList:
633 case QMetaType::Type::QStringList:
638 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineOffset.userType() ) ) ) );
643 double lineOpacity = -1.0;
645 if ( jsonPaint.contains( u
"line-opacity"_s ) )
647 const QVariant jsonLineOpacity = jsonPaint.value( u
"line-opacity"_s );
648 switch ( jsonLineOpacity.userType() )
650 case QMetaType::Type::Int:
651 case QMetaType::Type::LongLong:
652 case QMetaType::Type::Double:
653 lineOpacity = jsonLineOpacity.toDouble();
656 case QMetaType::Type::QVariantMap:
659 double defaultValue = 1.0;
668 case QMetaType::Type::QVariantList:
669 case QMetaType::Type::QStringList:
672 double defaultValue = 1.0;
683 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineOpacity.userType() ) ) ) );
688 QVector< double > dashVector;
689 if ( jsonPaint.contains( u
"line-dasharray"_s ) )
691 const QVariant jsonLineDashArray = jsonPaint.value( u
"line-dasharray"_s );
692 switch ( jsonLineDashArray.userType() )
694 case QMetaType::Type::QVariantMap:
696 QString arrayExpression;
699 arrayExpression = u
"array_to_string(array_foreach(%1,@element * (%2)), ';')"_s
704 arrayExpression = u
"array_to_string(%1, ';')"_s.arg(
parseArrayStops( jsonLineDashArray.toMap().value( u
"stops"_s ).toList(), context, lineWidth ) );
708 const QVariantList dashSource = jsonLineDashArray.toMap().value( u
"stops"_s ).toList().first().toList().value( 1 ).toList();
709 for (
const QVariant &v : dashSource )
711 dashVector << v.toDouble() * lineWidth;
716 case QMetaType::Type::QVariantList:
717 case QMetaType::Type::QStringList:
719 const QVariantList dashSource = jsonLineDashArray.toList();
721 if ( dashSource.at( 0 ).userType() == QMetaType::Type::QString )
727 u
"array_to_string(array_foreach(%1,@element * (%2)), ';')"_s
728 .arg( property.asExpression(), lineWidthProperty.
asExpression() )
739 QVector< double > rawDashVectorSizes;
740 rawDashVectorSizes.reserve( dashSource.size() );
741 for (
const QVariant &v : dashSource )
743 rawDashVectorSizes << v.toDouble();
747 if ( rawDashVectorSizes.size() == 1 )
750 rawDashVectorSizes.clear();
752 else if ( rawDashVectorSizes.size() % 2 == 1 )
756 rawDashVectorSizes[0] = rawDashVectorSizes[0] + rawDashVectorSizes[rawDashVectorSizes.size() - 1];
757 rawDashVectorSizes.resize( rawDashVectorSizes.size() - 1 );
760 if ( !rawDashVectorSizes.isEmpty() && ( !lineWidthProperty.
asExpression().isEmpty() ) )
762 QStringList dashArrayStringParts;
763 dashArrayStringParts.reserve( rawDashVectorSizes.size() );
764 for (
double v : std::as_const( rawDashVectorSizes ) )
769 QString arrayExpression = u
"array_to_string(array_foreach(array(%1),@element * (%2)), ';')"_s
770 .arg( dashArrayStringParts.join(
',' ), lineWidthProperty.
asExpression() );
775 for (
double v : std::as_const( rawDashVectorSizes ) )
777 dashVector << v * lineWidth;
785 QObject::tr(
"%1: Skipping unsupported line-dasharray type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineDashArray.userType() ) ) )
791 Qt::PenCapStyle penCapStyle = Qt::FlatCap;
792 Qt::PenJoinStyle penJoinStyle = Qt::MiterJoin;
793 if ( jsonLayer.contains( u
"layout"_s ) )
795 const QVariantMap jsonLayout = jsonLayer.value( u
"layout"_s ).toMap();
796 if ( jsonLayout.contains( u
"line-cap"_s ) )
798 penCapStyle =
parseCapStyle( jsonLayout.value( u
"line-cap"_s ).toString() );
800 if ( jsonLayout.contains( u
"line-join"_s ) )
802 penJoinStyle =
parseJoinStyle( jsonLayout.value( u
"line-join"_s ).toString() );
806 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsLineSymbol >() );
807 symbol->setOutputUnit( context.
targetUnit() );
809 if ( !rasterLineSprite.isEmpty() )
819 if ( lineOpacity != -1 )
821 symbol->setOpacity( lineOpacity );
827 symbol->setDataDefinedProperties( ddProperties );
829 if ( lineWidth != -1 )
833 symbol->changeSymbolLayer( 0, lineSymbol );
838 Q_ASSERT( lineSymbol );
848 if ( lineOpacity != -1 )
850 symbol->setOpacity( lineOpacity );
856 symbol->setDataDefinedProperties( ddProperties );
858 if ( lineColor.isValid() )
862 if ( lineWidth != -1 )
866 if ( !dashVector.empty() )
880 if ( !jsonLayer.contains( u
"paint"_s ) )
882 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
886 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
891 QColor circleFillColor;
892 if ( jsonPaint.contains( u
"circle-color"_s ) )
894 const QVariant jsonCircleColor = jsonPaint.value( u
"circle-color"_s );
895 switch ( jsonCircleColor.userType() )
897 case QMetaType::Type::QVariantMap:
901 case QMetaType::Type::QVariantList:
902 case QMetaType::Type::QStringList:
906 case QMetaType::Type::QString:
907 circleFillColor =
parseColor( jsonCircleColor.toString(), context );
911 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleColor.userType() ) ) ) );
918 circleFillColor = QColor( 0, 0, 0 );
922 double circleDiameter = 10.0;
923 if ( jsonPaint.contains( u
"circle-radius"_s ) )
925 const QVariant jsonCircleRadius = jsonPaint.value( u
"circle-radius"_s );
926 switch ( jsonCircleRadius.userType() )
928 case QMetaType::Type::Int:
929 case QMetaType::Type::LongLong:
930 case QMetaType::Type::Double:
934 case QMetaType::Type::QVariantMap:
939 case QMetaType::Type::QVariantList:
940 case QMetaType::Type::QStringList:
946 QObject::tr(
"%1: Skipping unsupported circle-radius type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleRadius.userType() ) ) )
952 double circleOpacity = -1.0;
953 if ( jsonPaint.contains( u
"circle-opacity"_s ) )
955 const QVariant jsonCircleOpacity = jsonPaint.value( u
"circle-opacity"_s );
956 switch ( jsonCircleOpacity.userType() )
958 case QMetaType::Type::Int:
959 case QMetaType::Type::LongLong:
960 case QMetaType::Type::Double:
961 circleOpacity = jsonCircleOpacity.toDouble();
964 case QMetaType::Type::QVariantMap:
968 case QMetaType::Type::QVariantList:
969 case QMetaType::Type::QStringList:
975 QObject::tr(
"%1: Skipping unsupported circle-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleOpacity.userType() ) ) )
980 if ( ( circleOpacity != -1 ) && circleFillColor.isValid() )
982 circleFillColor.setAlphaF( circleOpacity );
986 QColor circleStrokeColor;
987 if ( jsonPaint.contains( u
"circle-stroke-color"_s ) )
989 const QVariant jsonCircleStrokeColor = jsonPaint.value( u
"circle-stroke-color"_s );
990 switch ( jsonCircleStrokeColor.userType() )
992 case QMetaType::Type::QVariantMap:
996 case QMetaType::Type::QVariantList:
997 case QMetaType::Type::QStringList:
1001 case QMetaType::Type::QString:
1002 circleStrokeColor =
parseColor( jsonCircleStrokeColor.toString(), context );
1007 QObject::tr(
"%1: Skipping unsupported circle-stroke-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleStrokeColor.userType() ) ) )
1014 double circleStrokeWidth = -1.0;
1015 if ( jsonPaint.contains( u
"circle-stroke-width"_s ) )
1017 const QVariant circleStrokeWidthJson = jsonPaint.value( u
"circle-stroke-width"_s );
1018 switch ( circleStrokeWidthJson.userType() )
1020 case QMetaType::Type::Int:
1021 case QMetaType::Type::LongLong:
1022 case QMetaType::Type::Double:
1026 case QMetaType::Type::QVariantMap:
1027 circleStrokeWidth = -1.0;
1031 case QMetaType::Type::QVariantList:
1032 case QMetaType::Type::QStringList:
1038 QObject::tr(
"%1: Skipping unsupported circle-stroke-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( circleStrokeWidthJson.userType() ) ) )
1044 double circleStrokeOpacity = -1.0;
1045 if ( jsonPaint.contains( u
"circle-stroke-opacity"_s ) )
1047 const QVariant jsonCircleStrokeOpacity = jsonPaint.value( u
"circle-stroke-opacity"_s );
1048 switch ( jsonCircleStrokeOpacity.userType() )
1050 case QMetaType::Type::Int:
1051 case QMetaType::Type::LongLong:
1052 case QMetaType::Type::Double:
1053 circleStrokeOpacity = jsonCircleStrokeOpacity.toDouble();
1056 case QMetaType::Type::QVariantMap:
1060 case QMetaType::Type::QVariantList:
1061 case QMetaType::Type::QStringList:
1068 QObject::tr(
"%1: Skipping unsupported circle-stroke-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleStrokeOpacity.userType() ) ) )
1073 if ( ( circleStrokeOpacity != -1 ) && circleStrokeColor.isValid() )
1075 circleStrokeColor.setAlphaF( circleStrokeOpacity );
1079 QPointF circleTranslate;
1080 if ( jsonPaint.contains( u
"circle-translate"_s ) )
1082 const QVariant jsonCircleTranslate = jsonPaint.value( u
"circle-translate"_s );
1083 switch ( jsonCircleTranslate.userType() )
1085 case QMetaType::Type::QVariantMap:
1089 case QMetaType::Type::QVariantList:
1090 case QMetaType::Type::QStringList:
1097 QObject::tr(
"%1: Skipping unsupported circle-translate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleTranslate.userType() ) ) )
1103 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsMarkerSymbol >() );
1105 Q_ASSERT( markerSymbolLayer );
1108 symbol->setOutputUnit( context.
targetUnit() );
1111 if ( !circleTranslate.isNull() )
1113 markerSymbolLayer->
setOffset( circleTranslate );
1117 if ( circleFillColor.isValid() )
1121 if ( circleDiameter != -1 )
1123 markerSymbolLayer->
setSize( circleDiameter );
1126 if ( circleStrokeColor.isValid() )
1130 if ( circleStrokeWidth != -1 )
1145 hasLabeling =
false;
1146 hasRenderer =
false;
1148 if ( !jsonLayer.contains( u
"layout"_s ) )
1150 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
1153 const QVariantMap jsonLayout = jsonLayer.value( u
"layout"_s ).toMap();
1154 if ( !jsonLayout.contains( u
"text-field"_s ) )
1160 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
1166 if ( jsonLayout.contains( u
"text-size"_s ) )
1168 const QVariant jsonTextSize = jsonLayout.value( u
"text-size"_s );
1169 switch ( jsonTextSize.userType() )
1171 case QMetaType::Type::Int:
1172 case QMetaType::Type::LongLong:
1173 case QMetaType::Type::Double:
1177 case QMetaType::Type::QVariantMap:
1183 case QMetaType::Type::QVariantList:
1184 case QMetaType::Type::QStringList:
1190 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextSize.userType() ) ) ) );
1194 if ( textSizeProperty )
1201 constexpr double EM_TO_CHARS = 2.0;
1203 double textMaxWidth = -1;
1204 if ( jsonLayout.contains( u
"text-max-width"_s ) )
1206 const QVariant jsonTextMaxWidth = jsonLayout.value( u
"text-max-width"_s );
1207 switch ( jsonTextMaxWidth.userType() )
1209 case QMetaType::Type::Int:
1210 case QMetaType::Type::LongLong:
1211 case QMetaType::Type::Double:
1212 textMaxWidth = jsonTextMaxWidth.toDouble() * EM_TO_CHARS;
1215 case QMetaType::Type::QVariantMap:
1219 case QMetaType::Type::QVariantList:
1220 case QMetaType::Type::QStringList:
1226 QObject::tr(
"%1: Skipping unsupported text-max-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextMaxWidth.userType() ) ) )
1234 textMaxWidth = 10 * EM_TO_CHARS;
1237 double textLetterSpacing = -1;
1238 if ( jsonLayout.contains( u
"text-letter-spacing"_s ) )
1240 const QVariant jsonTextLetterSpacing = jsonLayout.value( u
"text-letter-spacing"_s );
1241 switch ( jsonTextLetterSpacing.userType() )
1243 case QMetaType::Type::Int:
1244 case QMetaType::Type::LongLong:
1245 case QMetaType::Type::Double:
1246 textLetterSpacing = jsonTextLetterSpacing.toDouble();
1249 case QMetaType::Type::QVariantMap:
1253 case QMetaType::Type::QVariantList:
1254 case QMetaType::Type::QStringList:
1260 QObject::tr(
"%1: Skipping unsupported text-letter-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextLetterSpacing.userType() ) ) )
1267 bool foundFont =
false;
1269 QString fontStyleName;
1271 bool allowOverlap = jsonLayout.contains( u
"text-allow-overlap"_s ) && jsonLayout.
value( u
"text-allow-overlap"_s ).toBool();
1273 if ( jsonLayout.contains( u
"text-font"_s ) )
1275 auto splitFontFamily = [](
const QString &fontName, QString &family, QString &style ) ->
bool {
1276 QString matchedFamily;
1277 const QStringList textFontParts = fontName.split(
' ' );
1278 for (
int i = textFontParts.size() - 1; i >= 1; --i )
1280 const QString candidateFontFamily = textFontParts.mid( 0, i ).join(
' ' );
1281 const QString candidateFontStyle = textFontParts.mid( i ).join(
' ' );
1286 family = processedFontFamily;
1287 style = candidateFontStyle;
1292 if ( processedFontFamily == matchedFamily )
1294 family = processedFontFamily;
1295 style = candidateFontStyle;
1299 family = matchedFamily;
1300 style = processedFontFamily;
1301 style.replace( matchedFamily, QString() );
1302 style = style.trimmed();
1303 if ( !style.isEmpty() && !candidateFontStyle.isEmpty() )
1305 style += u
" %1"_s.arg( candidateFontStyle );
1313 if ( QFontDatabase().hasFamily( processedFontFamily ) )
1316 family = processedFontFamily;
1322 family = matchedFamily;
1329 const QVariant jsonTextFont = jsonLayout.value( u
"text-font"_s );
1330 if ( jsonTextFont.userType() != QMetaType::Type::QVariantList
1331 && jsonTextFont.userType() != QMetaType::Type::QStringList
1332 && jsonTextFont.userType() != QMetaType::Type::QString
1333 && jsonTextFont.userType() != QMetaType::Type::QVariantMap )
1335 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-font type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextFont.userType() ) ) ) );
1339 switch ( jsonTextFont.userType() )
1341 case QMetaType::Type::QVariantList:
1342 case QMetaType::Type::QStringList:
1343 fontName = jsonTextFont.toList().value( 0 ).toString();
1346 case QMetaType::Type::QString:
1347 fontName = jsonTextFont.toString();
1350 case QMetaType::Type::QVariantMap:
1352 QString familyCaseString = u
"CASE "_s;
1353 QString styleCaseString = u
"CASE "_s;
1355 const QVariantList stops = jsonTextFont.toMap().value( u
"stops"_s ).toList();
1358 for (
int i = 0; i < stops.length() - 1; ++i )
1361 const QVariant bz = stops.value( i ).toList().value( 0 );
1362 const QString bv = stops.value( i ).toList().value( 1 ).userType() == QMetaType::Type::QString ? stops.value( i ).toList().value( 1 ).toString()
1363 : stops.value( i ).toList().value( 1 ).toList().value( 0 ).toString();
1364 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
1366 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1372 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
1373 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
1375 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1380 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1382 familyCaseString += QStringLiteral(
1383 "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1387 styleCaseString += QStringLiteral(
1388 "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1395 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1401 const QString bv = stops.constLast().toList().value( 1 ).userType() == QMetaType::Type::QString ? stops.constLast().toList().value( 1 ).toString()
1402 : stops.constLast().toList().value( 1 ).toList().value( 0 ).toString();
1403 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1410 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1417 fontName = fontFamily;
1427 if ( splitFontFamily( fontName, fontFamily, fontStyleName ) )
1430 if ( !fontStyleName.isEmpty() )
1431 textFont.setStyleName( fontStyleName );
1441 fontName = u
"Open Sans"_s;
1443 textFont.setStyleName( u
"Regular"_s );
1444 fontStyleName = u
"Regular"_s;
1449 fontName = u
"Arial Unicode MS"_s;
1451 textFont.setStyleName( u
"Regular"_s );
1452 fontStyleName = u
"Regular"_s;
1457 fontName = u
"Open Sans, Arial Unicode MS"_s;
1460 if ( !foundFont && !fontName.isEmpty() )
1462 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), fontName ) );
1467 if ( jsonPaint.contains( u
"text-color"_s ) )
1469 const QVariant jsonTextColor = jsonPaint.value( u
"text-color"_s );
1470 switch ( jsonTextColor.userType() )
1472 case QMetaType::Type::QVariantMap:
1476 case QMetaType::Type::QVariantList:
1477 case QMetaType::Type::QStringList:
1481 case QMetaType::Type::QString:
1482 textColor =
parseColor( jsonTextColor.toString(), context );
1486 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextColor.userType() ) ) ) );
1493 textColor = QColor( 0, 0, 0 );
1497 QColor bufferColor( 0, 0, 0, 0 );
1498 if ( jsonPaint.contains( u
"text-halo-color"_s ) )
1500 const QVariant jsonBufferColor = jsonPaint.value( u
"text-halo-color"_s );
1501 switch ( jsonBufferColor.userType() )
1503 case QMetaType::Type::QVariantMap:
1507 case QMetaType::Type::QVariantList:
1508 case QMetaType::Type::QStringList:
1512 case QMetaType::Type::QString:
1513 bufferColor =
parseColor( jsonBufferColor.toString(), context );
1518 QObject::tr(
"%1: Skipping unsupported text-halo-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonBufferColor.userType() ) ) )
1524 double bufferSize = 0.0;
1528 constexpr double BUFFER_SIZE_SCALE = 2.0;
1529 if ( jsonPaint.contains( u
"text-halo-width"_s ) )
1531 const QVariant jsonHaloWidth = jsonPaint.value( u
"text-halo-width"_s );
1532 QString bufferSizeDataDefined;
1533 switch ( jsonHaloWidth.userType() )
1535 case QMetaType::Type::Int:
1536 case QMetaType::Type::LongLong:
1537 case QMetaType::Type::Double:
1541 case QMetaType::Type::QVariantMap:
1546 case QMetaType::Type::QVariantList:
1547 case QMetaType::Type::QStringList:
1553 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-halo-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonHaloWidth.userType() ) ) ) );
1559 if ( bufferSize > 0 )
1561 if ( textSize > 0 && bufferSizeDataDefined.isEmpty() )
1563 bufferSize = std::min( bufferSize, textSize * BUFFER_SIZE_SCALE / 4 );
1565 else if ( textSize > 0 && !bufferSizeDataDefined.isEmpty() )
1567 bufferSizeDataDefined = u
"min(%1/4, %2)"_s.arg( textSize * BUFFER_SIZE_SCALE ).arg( bufferSizeDataDefined );
1570 else if ( !bufferSizeDataDefined.isEmpty() )
1572 bufferSizeDataDefined = u
"min(%1*%2/4, %3)"_s.arg( textSizeProperty.
asExpression() ).arg( BUFFER_SIZE_SCALE ).arg( bufferSizeDataDefined );
1575 else if ( bufferSizeDataDefined.isEmpty() )
1577 bufferSizeDataDefined = u
"min(%1*%2/4, %3)"_s.arg( textSizeProperty.
asExpression() ).arg( BUFFER_SIZE_SCALE ).arg( bufferSize );
1583 double haloBlurSize = 0;
1584 if ( jsonPaint.contains( u
"text-halo-blur"_s ) )
1586 const QVariant jsonTextHaloBlur = jsonPaint.value( u
"text-halo-blur"_s );
1587 switch ( jsonTextHaloBlur.userType() )
1589 case QMetaType::Type::Int:
1590 case QMetaType::Type::LongLong:
1591 case QMetaType::Type::Double:
1599 QObject::tr(
"%1: Skipping unsupported text-halo-blur type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextHaloBlur.userType() ) ) )
1607 if ( textColor.isValid() )
1609 if ( textSize >= 0 )
1614 if ( !fontStyleName.isEmpty() )
1617 if ( textLetterSpacing > 0 )
1619 QFont f = format.
font();
1620 f.setLetterSpacing( QFont::AbsoluteSpacing, textLetterSpacing );
1624 if ( bufferSize > 0 )
1627 const double opacity = bufferColor.alphaF();
1628 bufferColor.setAlphaF( 1.0 );
1636 if ( haloBlurSize > 0 )
1659 if ( textMaxWidth > 0 )
1665 if ( jsonLayout.contains( u
"text-field"_s ) )
1667 const QVariant jsonTextField = jsonLayout.value( u
"text-field"_s );
1668 switch ( jsonTextField.userType() )
1670 case QMetaType::Type::QString:
1672 labelSettings.
fieldName = processLabelField( jsonTextField.toString(), labelSettings.
isExpression );
1676 case QMetaType::Type::QVariantList:
1677 case QMetaType::Type::QStringList:
1679 const QVariantList textFieldList = jsonTextField.toList();
1687 if ( textFieldList.size() > 2 && textFieldList.at( 0 ).toString() ==
"format"_L1 )
1690 for (
int i = 1; i < textFieldList.size(); ++i )
1692 bool isExpression =
false;
1693 const QString part = processLabelField( textFieldList.at( i ).toString(), isExpression );
1694 if ( !isExpression )
1701 labelSettings.
fieldName = u
"concat(%1)"_s.arg( parts.join(
',' ) );
1716 case QMetaType::Type::QVariantMap:
1718 const QVariantList stops = jsonTextField.toMap().value( u
"stops"_s ).toList();
1719 if ( !stops.empty() )
1726 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-field dictionary" ).arg( context.
layerId() ) );
1732 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-field type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextField.userType() ) ) ) );
1737 if ( jsonLayout.contains( u
"text-rotate"_s ) )
1739 const QVariant jsonTextRotate = jsonLayout.value( u
"text-rotate"_s );
1740 switch ( jsonTextRotate.userType() )
1742 case QMetaType::Type::Double:
1743 case QMetaType::Type::Int:
1745 labelSettings.
angleOffset = jsonTextRotate.toDouble();
1749 case QMetaType::Type::QVariantList:
1750 case QMetaType::Type::QStringList:
1757 case QMetaType::Type::QVariantMap:
1759 QVariantMap rotateMap = jsonTextRotate.toMap();
1760 if ( rotateMap.contains( u
"property"_s ) && rotateMap[u
"type"_s].toString() ==
"identity"_L1 )
1766 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-rotate map content (%2)" ).arg( context.
layerId(), QString( QJsonDocument::fromVariant( rotateMap ).toJson() ) ) );
1771 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextRotate.userType() ) ) ) );
1776 if ( jsonLayout.contains( u
"text-transform"_s ) )
1778 const QString textTransform = jsonLayout.value( u
"text-transform"_s ).toString();
1779 if ( textTransform ==
"uppercase"_L1 )
1783 else if ( textTransform ==
"lowercase"_L1 )
1792 if ( jsonLayout.contains( u
"symbol-placement"_s ) )
1794 const QString symbolPlacement = jsonLayout.value( u
"symbol-placement"_s ).toString();
1795 if ( symbolPlacement ==
"line"_L1 )
1801 if ( jsonLayout.contains( u
"text-rotation-alignment"_s ) )
1803 const QString textRotationAlignment = jsonLayout.value( u
"text-rotation-alignment"_s ).toString();
1804 if ( textRotationAlignment ==
"viewport"_L1 )
1814 if ( jsonLayout.contains( u
"text-offset"_s ) )
1816 const QVariant jsonTextOffset = jsonLayout.value( u
"text-offset"_s );
1819 switch ( jsonTextOffset.userType() )
1821 case QMetaType::Type::QVariantMap:
1822 textOffsetProperty =
parseInterpolatePointByZoom( jsonTextOffset.toMap(), context, !textSizeProperty ? textSize : 1.0, &textOffset );
1823 if ( !textSizeProperty )
1831 u
"with_variable('text_size',%2,abs(array_get(%1,1))*@text_size-@text_size)"_s.arg( textOffsetProperty.
asExpression(), textSizeProperty.
asExpression() )
1837 case QMetaType::Type::QVariantList:
1838 case QMetaType::Type::QStringList:
1839 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize, jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1844 QObject::tr(
"%1: Skipping unsupported text-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextOffset.userType() ) ) )
1849 if ( !textOffset.isNull() )
1852 labelSettings.
dist = std::abs( textOffset.y() ) - textSize;
1854 if ( textSizeProperty && !textOffsetProperty )
1858 u
"with_variable('text_size',%2,%1*@text_size-@text_size)"_s.arg( std::abs( textOffset.y() / textSize ) ).arg( textSizeProperty.
asExpression() )
1864 if ( textOffset.isNull() )
1872 if ( jsonLayout.contains( u
"text-justify"_s ) )
1874 const QVariant jsonTextJustify = jsonLayout.value( u
"text-justify"_s );
1877 QString textAlign = u
"center"_s;
1879 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 } };
1881 switch ( jsonTextJustify.userType() )
1883 case QMetaType::Type::QString:
1884 textAlign = jsonTextJustify.toString();
1887 case QMetaType::Type::QVariantList:
1891 case QMetaType::Type::QVariantMap:
1896 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-justify type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextJustify.userType() ) ) ) );
1900 if ( textAlign ==
"left"_L1 )
1902 else if ( textAlign ==
"right"_L1 )
1904 else if ( textAlign ==
"center"_L1 )
1906 else if ( textAlign ==
"follow"_L1 )
1916 if ( jsonLayout.contains( u
"text-anchor"_s ) )
1918 const QVariant jsonTextAnchor = jsonLayout.value( u
"text-anchor"_s );
1921 const QVariantMap conversionMap {
1927 { u
"top-left"_s, 8 },
1928 { u
"top-right"_s, 6 },
1929 { u
"bottom-left"_s, 2 },
1930 { u
"bottom-right"_s, 0 },
1933 switch ( jsonTextAnchor.userType() )
1935 case QMetaType::Type::QString:
1936 textAnchor = jsonTextAnchor.toString();
1939 case QMetaType::Type::QVariantList:
1943 case QMetaType::Type::QVariantMap:
1948 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-anchor type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextAnchor.userType() ) ) ) );
1952 if ( textAnchor ==
"center"_L1 )
1954 else if ( textAnchor ==
"left"_L1 )
1956 else if ( textAnchor ==
"right"_L1 )
1958 else if ( textAnchor ==
"top"_L1 )
1960 else if ( textAnchor ==
"bottom"_L1 )
1962 else if ( textAnchor ==
"top-left"_L1 )
1964 else if ( textAnchor ==
"top-right"_L1 )
1966 else if ( textAnchor ==
"bottom-left"_L1 )
1968 else if ( textAnchor ==
"bottom-right"_L1 )
1973 if ( jsonLayout.contains( u
"text-offset"_s ) )
1975 const QVariant jsonTextOffset = jsonLayout.value( u
"text-offset"_s );
1978 switch ( jsonTextOffset.userType() )
1980 case QMetaType::Type::QVariantMap:
1984 case QMetaType::Type::QVariantList:
1985 case QMetaType::Type::QStringList:
1986 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize, jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1990 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextOffset.userType() ) ) ) );
1994 if ( !textOffset.isNull() )
1997 labelSettings.
xOffset = textOffset.x();
1998 labelSettings.
yOffset = textOffset.y();
2006 QString spriteProperty, spriteSizeProperty;
2008 if ( !sprite.isEmpty() )
2011 if ( jsonLayout.contains( u
"icon-size"_s ) )
2014 const QVariant jsonIconSize = jsonLayout.
value( u
"icon-size"_s );
2015 switch ( jsonIconSize.userType() )
2017 case QMetaType::Type::Int:
2018 case QMetaType::Type::LongLong:
2019 case QMetaType::Type::Double:
2021 size = jsonIconSize.toDouble();
2022 if ( !spriteSizeProperty.isEmpty() )
2029 case QMetaType::Type::QVariantMap:
2033 case QMetaType::Type::QVariantList:
2034 case QMetaType::Type::QStringList:
2039 QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) )
2046 if ( !spriteSizeProperty.isEmpty() )
2059 markerLayer->
setPath( sprite );
2060 markerLayer->
setSize( spriteSize.width() );
2063 if ( !spriteProperty.isEmpty() )
2073 backgroundSettings.
setSize( spriteSize * size );
2081 if ( textSize >= 0 )
2104 if ( !jsonLayer.contains( u
"layout"_s ) )
2106 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
2109 const QVariantMap jsonLayout = jsonLayer.value( u
"layout"_s ).toMap();
2111 if ( jsonLayout.value( u
"symbol-placement"_s ).toString() ==
"line"_L1 && !jsonLayout.contains( u
"text-field"_s ) )
2115 double spacing = -1.0;
2116 if ( jsonLayout.contains( u
"symbol-spacing"_s ) )
2118 const QVariant jsonSpacing = jsonLayout.value( u
"symbol-spacing"_s );
2119 switch ( jsonSpacing.userType() )
2121 case QMetaType::Type::Int:
2122 case QMetaType::Type::LongLong:
2123 case QMetaType::Type::Double:
2127 case QMetaType::Type::QVariantMap:
2131 case QMetaType::Type::QVariantList:
2132 case QMetaType::Type::QStringList:
2137 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported symbol-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonSpacing.userType() ) ) ) );
2147 bool rotateMarkers =
true;
2148 if ( jsonLayout.contains( u
"icon-rotation-alignment"_s ) )
2150 const QString alignment = jsonLayout.value( u
"icon-rotation-alignment"_s ).toString();
2151 if ( alignment ==
"map"_L1 || alignment ==
"auto"_L1 )
2153 rotateMarkers =
true;
2155 else if ( alignment ==
"viewport"_L1 )
2157 rotateMarkers =
false;
2162 double rotation = 0.0;
2163 if ( jsonLayout.contains( u
"icon-rotate"_s ) )
2165 const QVariant jsonIconRotate = jsonLayout.value( u
"icon-rotate"_s );
2166 switch ( jsonIconRotate.userType() )
2168 case QMetaType::Type::Int:
2169 case QMetaType::Type::LongLong:
2170 case QMetaType::Type::Double:
2171 rotation = jsonIconRotate.toDouble();
2174 case QMetaType::Type::QVariantMap:
2178 case QMetaType::Type::QVariantList:
2179 case QMetaType::Type::QStringList:
2184 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconRotate.userType() ) ) ) );
2200 QString spriteProperty, spriteSizeProperty;
2202 if ( !sprite.isNull() )
2204 markerLayer->
setPath( sprite );
2205 markerLayer->
setSize( spriteSize.width() );
2208 if ( !spriteProperty.isEmpty() )
2215 if ( jsonLayout.contains( u
"icon-size"_s ) )
2217 const QVariant jsonIconSize = jsonLayout.value( u
"icon-size"_s );
2220 switch ( jsonIconSize.userType() )
2222 case QMetaType::Type::Int:
2223 case QMetaType::Type::LongLong:
2224 case QMetaType::Type::Double:
2226 size = jsonIconSize.toDouble();
2227 if ( !spriteSizeProperty.isEmpty() )
2234 case QMetaType::Type::QVariantMap:
2238 case QMetaType::Type::QVariantList:
2239 case QMetaType::Type::QStringList:
2243 context.
pushWarning( QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) ) );
2246 markerLayer->
setSize( size * spriteSize.width() );
2249 if ( !spriteSizeProperty.isEmpty() )
2265 std::unique_ptr< QgsSymbol > symbol = std::make_unique< QgsLineSymbol >(
QgsSymbolLayerList() << lineSymbol );
2268 symbol->setOutputUnit( context.
targetUnit() );
2272 rendererStyle.
setSymbol( symbol.release() );
2275 else if ( jsonLayout.contains( u
"icon-image"_s ) )
2277 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
2280 QString spriteProperty, spriteSizeProperty;
2282 if ( !sprite.isEmpty() || !spriteProperty.isEmpty() )
2285 rasterMarker->
setPath( sprite );
2286 rasterMarker->
setSize( spriteSize.width() );
2290 if ( !spriteProperty.isEmpty() )
2296 if ( jsonLayout.contains( u
"icon-size"_s ) )
2298 const QVariant jsonIconSize = jsonLayout.value( u
"icon-size"_s );
2301 switch ( jsonIconSize.userType() )
2303 case QMetaType::Type::Int:
2304 case QMetaType::Type::LongLong:
2305 case QMetaType::Type::Double:
2307 size = jsonIconSize.toDouble();
2308 if ( !spriteSizeProperty.isEmpty() )
2315 case QMetaType::Type::QVariantMap:
2319 case QMetaType::Type::QVariantList:
2320 case QMetaType::Type::QStringList:
2325 QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) )
2329 rasterMarker->
setSize( size * spriteSize.width() );
2332 if ( !spriteSizeProperty.isEmpty() )
2344 double rotation = 0.0;
2345 if ( jsonLayout.contains( u
"icon-rotate"_s ) )
2347 const QVariant jsonIconRotate = jsonLayout.value( u
"icon-rotate"_s );
2348 switch ( jsonIconRotate.userType() )
2350 case QMetaType::Type::Int:
2351 case QMetaType::Type::LongLong:
2352 case QMetaType::Type::Double:
2353 rotation = jsonIconRotate.toDouble();
2356 case QMetaType::Type::QVariantMap:
2360 case QMetaType::Type::QVariantList:
2361 case QMetaType::Type::QStringList:
2367 QObject::tr(
"%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconRotate.userType() ) ) )
2373 double iconOpacity = -1.0;
2374 if ( jsonPaint.contains( u
"icon-opacity"_s ) )
2376 const QVariant jsonIconOpacity = jsonPaint.value( u
"icon-opacity"_s );
2377 switch ( jsonIconOpacity.userType() )
2379 case QMetaType::Type::Int:
2380 case QMetaType::Type::LongLong:
2381 case QMetaType::Type::Double:
2382 iconOpacity = jsonIconOpacity.toDouble();
2385 case QMetaType::Type::QVariantMap:
2389 case QMetaType::Type::QVariantList:
2390 case QMetaType::Type::QStringList:
2396 QObject::tr(
"%1: Skipping unsupported icon-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconOpacity.userType() ) ) )
2403 rasterMarker->
setAngle( rotation );
2404 if ( iconOpacity >= 0 )
2408 rendererStyle.
setSymbol( markerSymbol );
2419 const double base = json.
value( u
"base"_s, u
"1"_s ).toDouble();
2420 const QVariantList stops = json.value( u
"stops"_s ).toList();
2421 if ( stops.empty() )
2424 QString caseString = u
"CASE "_s;
2425 const QString colorComponent(
"color_part(%1,'%2')" );
2427 for (
int i = 0; i < stops.length() - 1; ++i )
2430 const QString bz = stops.at( i ).toList().value( 0 ).toString();
2432 const QString tz = stops.at( i + 1 ).toList().value( 0 ).toString();
2434 const QVariant bcVariant = stops.at( i ).toList().value( 1 );
2435 const QVariant tcVariant = stops.at( i + 1 ).toList().value( 1 );
2437 const QColor bottomColor =
parseColor( bcVariant.toString(), context );
2438 const QColor topColor =
parseColor( tcVariant.toString(), context );
2440 if ( i == 0 && bottomColor.isValid() )
2447 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 );
2450 if ( bottomColor.isValid() && topColor.isValid() )
2462 caseString += QStringLiteral(
2463 "WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2482 "WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2488 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"hsl_hue" ), colorComponent.arg( topColorExpr ).arg(
"hsl_hue" ), base, 1, &context ),
2489 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"hsl_saturation" ), colorComponent.arg( topColorExpr ).arg(
"hsl_saturation" ), base, 1, &context ),
2490 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"lightness" ), colorComponent.arg( topColorExpr ).arg(
"lightness" ), base, 1, &context ),
2491 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"alpha" ), colorComponent.arg( topColorExpr ).arg(
"alpha" ), base, 1, &context )
2497 const QString tz = stops.last().toList().value( 0 ).toString();
2498 const QVariant tcVariant = stops.last().toList().value( 1 );
2500 if ( tcVariant.userType() == QMetaType::Type::QString )
2503 if ( topColor.isValid() )
2510 caseString += QStringLiteral(
2511 "WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2512 "ELSE color_hsla(%2, %3, %4, %5) END"
2521 else if ( tcVariant.userType() == QMetaType::QVariantList )
2525 caseString += QStringLiteral(
2526 "WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2527 "ELSE color_hsla(%2, %3, %4, %5) END"
2530 .arg( colorComponent.arg( topColorExpr ).arg(
"hsl_hue" ) )
2531 .arg( colorComponent.arg( topColorExpr ).arg(
"hsl_saturation" ) )
2532 .arg( colorComponent.arg( topColorExpr ).arg(
"lightness" ) )
2533 .arg( colorComponent.arg( topColorExpr ).arg(
"alpha" ) );
2536 if ( !stops.empty() && defaultColor )
2537 *defaultColor =
parseColor( stops.value( 0 ).toList().value( 1 ).toString(), context );
2544 const double base = json.
value( u
"base"_s, u
"1"_s ).toDouble();
2545 const QVariantList stops = json.value( u
"stops"_s ).toList();
2546 if ( stops.empty() )
2549 QString scaleExpression;
2550 if ( stops.size() <= 2 )
2553 stops.value( 0 ).toList().value( 0 ).toDouble(),
2554 stops.last().toList().value( 0 ).toDouble(),
2555 stops.value( 0 ).toList().value( 1 ),
2556 stops.last().toList().value( 1 ),
2564 scaleExpression =
parseStops( base, stops, multiplier, context );
2567 if ( !stops.empty() && defaultNumber )
2568 *defaultNumber = stops.value( 0 ).toList().value( 1 ).toDouble() * multiplier;
2578 context = *contextPtr;
2580 const double base = json.value( u
"base"_s, u
"1"_s ).toDouble();
2581 const QVariantList stops = json.value( u
"stops"_s ).toList();
2582 if ( stops.empty() )
2585 QString scaleExpression;
2586 if ( stops.length() <= 2 )
2588 const QVariant bv = stops.value( 0 ).toList().value( 1 );
2589 const QVariant tv = stops.last().toList().value( 1 );
2590 double bottom = 0.0;
2592 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2594 stops.value( 0 ).toList().value( 0 ).toDouble(),
2595 stops.last().toList().value( 0 ).toDouble(),
2596 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2597 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ),
2612 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() )
2613 .arg( stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity );
2615 for (
int i = 0; i < stops.size() - 1; ++i )
2617 const QVariant bv = stops.value( i ).toList().value( 1 );
2618 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2619 double bottom = 0.0;
2621 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2623 caseString += QStringLiteral(
2624 " WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
2625 "THEN set_color_part(@symbol_color, 'alpha', %3)"
2628 stops.value( i ).toList().value( 0 ).toString(),
2629 stops.value( i + 1 ).toList().value( 0 ).toString(),
2631 stops.value( i ).toList().value( 0 ).toDouble(),
2632 stops.value( i + 1 ).toList().value( 0 ).toDouble(),
2633 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2634 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ),
2643 bool numeric =
false;
2644 const QVariant vv = stops.last().toList().value( 1 );
2645 double dv = vv.toDouble( &numeric );
2647 caseString += QStringLiteral(
2648 " WHEN @vector_tile_zoom >= %1 "
2649 "THEN set_color_part(@symbol_color, 'alpha', %2) END"
2651 .arg( stops.last().toList().value( 0 ).toString(), numeric ? QString::number( dv * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( vv, context ) ).arg( maxOpacity ) );
2657 const double base = json.
value( u
"base"_s, u
"1"_s ).toDouble();
2658 const QVariantList stops = json.value( u
"stops"_s ).toList();
2659 if ( stops.empty() )
2662 QString scaleExpression;
2663 if ( stops.size() <= 2 )
2665 scaleExpression = u
"array(%1,%2)"_s.arg(
2667 stops.value( 0 ).toList().value( 0 ).toDouble(),
2668 stops.last().toList().value( 0 ).toDouble(),
2669 stops.value( 0 ).toList().value( 1 ).toList().value( 0 ),
2670 stops.last().toList().value( 1 ).toList().value( 0 ),
2676 stops.value( 0 ).toList().value( 0 ).toDouble(),
2677 stops.last().toList().value( 0 ).toDouble(),
2678 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ),
2679 stops.last().toList().value( 1 ).toList().value( 1 ),
2688 scaleExpression =
parsePointStops( base, stops, context, multiplier );
2691 if ( !stops.empty() && defaultPoint )
2692 *defaultPoint = QPointF( stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble() * multiplier, stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble() * multiplier );
2699 const QVariantList stops = json.
value( u
"stops"_s ).toList();
2700 if ( stops.empty() )
2703 const QString scaleExpression =
parseStringStops( stops, context, conversionMap, defaultString );
2710 QString caseString = u
"CASE "_s;
2712 for (
int i = 0; i < stops.length() - 1; ++i )
2715 const QVariant bz = stops.value( i ).toList().value( 0 );
2716 const QVariant bv = stops.value( i ).toList().value( 1 );
2717 if ( bv.userType() != QMetaType::Type::QVariantList && bv.userType() != QMetaType::Type::QStringList )
2719 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( bz.userType() ) ) ) );
2724 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2725 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2726 if ( tv.userType() != QMetaType::Type::QVariantList && tv.userType() != QMetaType::Type::QStringList )
2728 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( tz.userType() ) ) ) );
2732 caseString += QStringLiteral(
2733 "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2739 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 0 ), tv.toList().value( 0 ), base, multiplier, &context ),
2740 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 1 ), tv.toList().value( 1 ), base, multiplier, &context )
2743 caseString +=
"END"_L1;
2749 if ( stops.length() < 2 )
2752 QString caseString = u
"CASE"_s;
2754 for (
int i = 0; i < stops.length(); ++i )
2756 caseString +=
" WHEN "_L1;
2757 QStringList conditions;
2760 const QVariant bottomZoom = stops.value( i ).toList().value( 0 );
2761 conditions << u
"@vector_tile_zoom > %1"_s.arg( bottomZoom.toString() );
2763 if ( i < stops.length() - 1 )
2765 const QVariant topZoom = stops.value( i + 1 ).toList().value( 0 );
2766 conditions << u
"@vector_tile_zoom <= %1"_s.arg( topZoom.toString() );
2769 const QVariantList values = stops.value( i ).toList().value( 1 ).toList();
2770 QStringList valuesFixed;
2772 for (
const QVariant &value : values )
2774 const double number = value.toDouble( &ok );
2776 valuesFixed << QString::number( number * multiplier );
2780 caseString += u
"%1 THEN array(%3)"_s.arg( conditions.join(
" AND "_L1 ), valuesFixed.join(
',' ) );
2782 caseString +=
" END"_L1;
2788 QString caseString = u
"CASE "_s;
2790 for (
int i = 0; i < stops.length() - 1; ++i )
2793 const QVariant bz = stops.value( i ).toList().value( 0 );
2794 const QVariant bv = stops.value( i ).toList().value( 1 );
2795 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2797 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2802 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2803 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2804 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2806 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2810 const QString lowerComparator = i == 0 ? u
">="_s : u
">"_s;
2812 caseString += QStringLiteral(
2813 "WHEN @vector_tile_zoom %1 %2 AND @vector_tile_zoom <= %3 "
2816 .arg( lowerComparator, bz.toString(), tz.toString(),
interpolateExpression( bz.toDouble(), tz.toDouble(), bv, tv, base, multiplier, &context ) );
2819 const QVariant z = stops.last().toList().value( 0 );
2820 const QVariant v = stops.last().toList().value( 1 );
2821 QString vStr = v.toString();
2822 if ( ( QMetaType::Type ) v.userType() == QMetaType::QVariantList )
2825 caseString += QStringLiteral(
2826 "WHEN @vector_tile_zoom > %1 "
2827 "THEN ( ( %2 ) * %3 ) END"
2829 .arg( z.toString() )
2835 caseString += QStringLiteral(
2836 "WHEN @vector_tile_zoom > %1 "
2839 .arg( z.toString() )
2840 .arg( v.toDouble() * multiplier );
2848 QString caseString = u
"CASE "_s;
2850 for (
int i = 0; i < stops.length() - 1; ++i )
2853 const QVariant bz = stops.value( i ).toList().value( 0 );
2854 const QString bv = stops.value( i ).toList().value( 1 ).toString();
2855 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2857 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2862 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2863 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2865 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2869 caseString += QStringLiteral(
2870 "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2875 caseString += u
"ELSE %1 END"_s.arg(
QgsExpression::quotedValue( conversionMap.value( stops.constLast().toList().value( 1 ).toString(), stops.constLast().toList().value( 1 ) ) ) );
2876 if ( defaultString )
2877 *defaultString = stops.constLast().toList().value( 1 ).toString();
2883 QString caseString = u
"CASE "_s;
2885 bool isExpression =
false;
2886 for (
int i = 0; i < stops.length() - 1; ++i )
2889 const QVariant bz = stops.value( i ).toList().value( 0 );
2890 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2892 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2897 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2898 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2900 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2904 QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
2905 if ( fieldPart.isEmpty() )
2906 fieldPart = u
"''"_s;
2907 else if ( !isExpression )
2910 caseString += QStringLiteral(
2911 "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom < %2 "
2914 .arg( bz.toString(), tz.toString(), fieldPart );
2918 const QVariant bz = stops.constLast().toList().value( 0 );
2919 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2921 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2925 QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
2926 if ( fieldPart.isEmpty() )
2927 fieldPart = u
"''"_s;
2928 else if ( !isExpression )
2931 caseString += QStringLiteral(
2932 "WHEN @vector_tile_zoom >= %1 "
2935 .arg( bz.toString(), fieldPart );
2938 QString defaultPart = processLabelField( stops.constFirst().toList().value( 1 ).toString(), isExpression );
2939 if ( defaultPart.isEmpty() )
2940 defaultPart = u
"''"_s;
2941 else if ( !isExpression )
2943 caseString += u
"ELSE %1 END"_s.arg( defaultPart );
2952 const QString method = json.
value( 0 ).toString();
2953 if ( method ==
"interpolate"_L1 )
2957 else if ( method ==
"match"_L1 )
2959 return parseMatchList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
2961 else if ( method ==
"step"_L1 )
2963 return parseStepList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
2975 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
2976 if ( attribute.isEmpty() )
2978 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
2982 QString caseString = u
"CASE "_s;
2984 for (
int i = 2; i < json.length() - 1; i += 2 )
2987 QVariant variantKeys = json.value( i );
2988 if ( variantKeys.userType() == QMetaType::Type::QVariantList || variantKeys.userType() == QMetaType::Type::QStringList )
2989 keys = variantKeys.toList();
2991 keys = { variantKeys };
2993 QStringList matchString;
2994 for (
const QVariant &key : keys )
2999 const QVariant value = json.value( i + 1 );
3001 QString valueString;
3006 if ( value.userType() == QMetaType::Type::QVariantList || value.userType() == QMetaType::Type::QStringList )
3012 const QColor color =
parseColor( value, context );
3020 const double v = value.toDouble() * multiplier;
3021 valueString = QString::number( v );
3027 const double v = value.toDouble() * maxOpacity;
3028 valueString = QString::number( v );
3034 valueString = u
"array(%1,%2)"_s.arg( value.toList().value( 0 ).toDouble() * multiplier, value.toList().value( 0 ).toDouble() * multiplier );
3040 if ( value.toList().count() == 2 && value.toList().first().toString() ==
"literal"_L1 )
3042 valueString = u
"array(%1)"_s.arg( value.toList().at( 1 ).toStringList().join(
',' ) );
3046 valueString = u
"array(%1)"_s.arg( value.toStringList().join(
',' ) );
3052 if ( matchString.count() == 1 )
3054 caseString += u
"WHEN %1 IS %2 THEN %3 "_s.arg( attribute, matchString.at( 0 ), valueString );
3058 caseString += u
"WHEN %1 IN (%2) THEN %3 "_s.arg( attribute, matchString.join(
',' ), valueString );
3062 QVariant lastValue = json.constLast();
3065 switch ( lastValue.userType() )
3067 case QMetaType::Type::QVariantList:
3068 case QMetaType::Type::QStringList:
3069 elseValue =
parseValueList( lastValue.toList(), type, context, multiplier, maxOpacity, defaultColor, defaultNumber ).
asExpression();
3078 const QColor color =
parseColor( lastValue, context );
3080 *defaultColor = color;
3088 const double v = json.constLast().toDouble() * multiplier;
3089 if ( defaultNumber )
3091 elseValue = QString::number( v );
3097 const double v = json.constLast().toDouble() * maxOpacity;
3098 if ( defaultNumber )
3100 elseValue = QString::number( v );
3106 elseValue = u
"array(%1,%2)"_s.arg( json.constLast().toList().value( 0 ).toDouble() * multiplier ).arg( json.constLast().toList().value( 0 ).toDouble() * multiplier );
3112 if ( json.constLast().toList().count() == 2 && json.constLast().toList().first().toString() ==
"literal"_L1 )
3114 elseValue = u
"array(%1)"_s.arg( json.constLast().toList().at( 1 ).toStringList().join(
',' ) );
3118 elseValue = u
"array(%1)"_s.arg( json.constLast().toStringList().join(
',' ) );
3127 caseString += u
"ELSE %1 END"_s.arg( elseValue );
3135 const QString expression =
parseExpression( json.value( 1 ).toList(), context );
3136 if ( expression.isEmpty() )
3138 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3142 QString caseString = u
"CASE "_s;
3145 for (
int i = json.length() - 2; i > 0; i -= 2 )
3147 const QVariant stepValue = json.value( i + 1 );
3149 QString valueString;
3160 const QColor color =
parseColor( stepValue, context );
3167 const double v = stepValue.toDouble() * multiplier;
3168 valueString = QString::number( v );
3174 const double v = stepValue.toDouble() * maxOpacity;
3175 valueString = QString::number( v );
3181 valueString = u
"array(%1,%2)"_s.arg( stepValue.toList().value( 0 ).toDouble() * multiplier ).arg( stepValue.toList().value( 0 ).toDouble() * multiplier );
3187 if ( stepValue.toList().count() == 2 && stepValue.toList().first().toString() ==
"literal"_L1 )
3189 valueString = u
"array(%1)"_s.arg( stepValue.toList().at( 1 ).toStringList().join(
',' ) );
3193 valueString = u
"array(%1)"_s.arg( stepValue.toStringList().join(
',' ) );
3203 caseString += u
" WHEN %1 >= %2 THEN (%3) "_s.arg( expression, stepKey, valueString );
3207 caseString += u
"ELSE (%1) END"_s.arg( valueString );
3217 if ( json.value( 0 ).toString() !=
"interpolate"_L1 )
3219 context.
pushWarning( QObject::tr(
"%1: Could not interpret value list" ).arg( context.
layerId() ) );
3224 const QString technique = json.
value( 1 ).toList().value( 0 ).toString();
3225 if ( technique ==
"linear"_L1 )
3227 else if ( technique ==
"exponential"_L1 )
3228 base = json.value( 1 ).toList().value( 1 ).toDouble();
3229 else if ( technique ==
"cubic-bezier"_L1 )
3231 context.
pushWarning( QObject::tr(
"%1: Cubic-bezier interpolation is not supported, linear used instead." ).arg( context.
layerId() ) );
3236 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation method %2" ).arg( context.
layerId(), technique ) );
3240 if ( json.value( 2 ).toList().value( 0 ).toString() !=
"zoom"_L1 )
3242 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation input %2" ).arg( context.
layerId(), json.value( 2 ).toString() ) );
3248 for (
int i = 3; i < json.length(); i += 2 )
3250 stops.push_back( QVariantList() << json.value( i ).toString() << json.value( i + 1 ) );
3254 props.insert( u
"stops"_s, stops );
3255 props.insert( u
"base"_s, base );
3271 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported numeric array in interpolate" ).arg( context.
layerId() ) );
3279 if ( ( QMetaType::Type ) colorExpression.userType() == QMetaType::QVariantList )
3283 return parseValue( colorExpression, context,
true );
3288 if ( color.userType() != QMetaType::Type::QString )
3290 context.
pushWarning( QObject::tr(
"%1: Could not parse non-string color %2, skipping" ).arg( context.
layerId(), color.toString() ) );
3299 hue = std::max( 0, color.hslHue() );
3300 saturation = color.hslSaturation() / 255.0 * 100;
3301 lightness = color.lightness() / 255.0 * 100;
3302 alpha = color.alpha();
3310 context = *contextPtr;
3314 if ( valueMin.canConvert( QMetaType::Double ) && valueMax.canConvert( QMetaType::Double ) )
3316 bool minDoubleOk =
true;
3317 const double min = valueMin.toDouble( &minDoubleOk );
3318 bool maxDoubleOk =
true;
3319 const double max = valueMax.toDouble( &maxDoubleOk );
3320 if ( minDoubleOk && maxDoubleOk &&
qgsDoubleNear( min, max ) )
3322 return QString::number( min * multiplier );
3326 QString minValueExpr = valueMin.toString();
3327 QString maxValueExpr = valueMax.toString();
3328 if ( valueMin.userType() == QMetaType::Type::QVariantList )
3332 if ( valueMax.userType() == QMetaType::Type::QVariantList )
3338 if ( minValueExpr == maxValueExpr )
3340 expression = minValueExpr;
3346 expression = u
"scale_linear(@vector_tile_zoom,%1,%2,%3,%4)"_s.arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr );
3350 expression = u
"scale_exponential(@vector_tile_zoom,%1,%2,%3,%4,%5)"_s.arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr ).arg( base );
3354 if ( multiplier != 1 )
3355 return u
"(%1) * %2"_s.arg( expression ).arg( multiplier );
3362 if ( style ==
"round"_L1 )
3363 return Qt::RoundCap;
3364 else if ( style ==
"square"_L1 )
3365 return Qt::SquareCap;
3372 if ( style ==
"bevel"_L1 )
3373 return Qt::BevelJoin;
3374 else if ( style ==
"round"_L1 )
3375 return Qt::RoundJoin;
3377 return Qt::MiterJoin;
3382 QString op = expression.value( 0 ).toString();
3383 if ( ( op ==
"%"_L1 || op ==
"/"_L1 || op ==
"-"_L1 || op ==
"^"_L1 ) && expression.size() >= 3 )
3385 if ( expression.size() != 3 )
3387 context.
pushWarning( QObject::tr(
"%1: Operator %2 requires exactly two operands, skipping extra operands" ).arg( context.
layerId() ).arg( op ) );
3389 QString v1 = parseValue( expression.value( 1 ), context, colorExpected );
3390 QString v2 = parseValue( expression.value( 2 ), context, colorExpected );
3391 return u
"(%1 %2 %3)"_s.arg( v1, op, v2 );
3393 else if ( ( op ==
"*"_L1 || op ==
"+"_L1 ) && expression.size() >= 3 )
3395 QStringList operands;
3396 std::transform( std::next( expression.begin() ), expression.end(), std::back_inserter( operands ), [&context, colorExpected](
const QVariant &val ) {
3397 return parseValue( val, context, colorExpected );
3399 return u
"(%1)"_s.arg( operands.join( u
" %1 "_s.arg( op ) ) );
3401 else if ( op ==
"to-number"_L1 )
3403 return u
"to_real(%1)"_s.arg( parseValue( expression.value( 1 ), context ) );
3405 if ( op ==
"literal"_L1 )
3407 return expression.value( 1 ).toString();
3409 else if ( op ==
"all"_L1 || op ==
"any"_L1 || op ==
"none"_L1 )
3412 for (
int i = 1; i < expression.size(); ++i )
3414 const QString part = parseValue( expression.at( i ), context );
3415 if ( part.isEmpty() )
3417 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3423 if ( op ==
"none"_L1 )
3424 return u
"NOT (%1)"_s.arg( parts.join(
") AND NOT ("_L1 ) );
3426 QString operatorString;
3427 if ( op ==
"all"_L1 )
3428 operatorString = u
") AND ("_s;
3429 else if ( op ==
"any"_L1 )
3430 operatorString = u
") OR ("_s;
3432 return u
"(%1)"_s.arg( parts.join( operatorString ) );
3434 else if ( op ==
'!' )
3437 QVariantList contraJsonExpr = expression.value( 1 ).toList();
3438 contraJsonExpr[0] = QString( op + contraJsonExpr[0].toString() );
3440 return parseKey( contraJsonExpr, context );
3442 else if ( op ==
"=="_L1 || op ==
"!="_L1 || op ==
">="_L1 || op ==
'>' || op ==
"<="_L1 || op ==
'<' )
3445 if ( op ==
"=="_L1 )
3447 else if ( op ==
"!="_L1 )
3449 return u
"%1 %2 %3"_s.arg( parseKey( expression.value( 1 ), context ), op, parseValue( expression.value( 2 ), context ) );
3451 else if ( op ==
"has"_L1 )
3453 return parseKey( expression.value( 1 ), context ) + u
" IS NOT NULL"_s;
3455 else if ( op ==
"!has"_L1 )
3457 return parseKey( expression.value( 1 ), context ) + u
" IS NULL"_s;
3459 else if ( op ==
"in"_L1 || op ==
"!in"_L1 )
3461 const QString key = parseKey( expression.value( 1 ), context );
3464 QVariantList values = expression.mid( 2 );
3465 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 )
3467 values = expression.at( 2 ).toList().at( 1 ).toList();
3470 for (
const QVariant &value : std::as_const( values ) )
3472 const QString part = parseValue( value, context );
3473 if ( part.isEmpty() )
3475 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3481 if ( parts.size() == 1 )
3483 if ( op ==
"in"_L1 )
3484 return u
"%1 IS %2"_s.arg( key, parts.at( 0 ) );
3486 return u
"(%1 IS NULL OR %1 IS NOT %2)"_s.arg( key, parts.at( 0 ) );
3490 if ( op ==
"in"_L1 )
3491 return u
"%1 IN (%2)"_s.arg( key, parts.join(
", "_L1 ) );
3493 return u
"(%1 IS NULL OR %1 NOT IN (%2))"_s.arg( key, parts.join(
", "_L1 ) );
3496 else if ( op ==
"get"_L1 )
3498 return parseKey( expression.value( 1 ), context );
3500 else if ( op ==
"match"_L1 )
3502 const QString attribute = expression.value( 1 ).toList().value( 1 ).toString();
3504 if ( expression.size() == 5
3505 && expression.at( 3 ).userType() == QMetaType::Type::Bool
3506 && expression.at( 3 ).toBool() ==
true
3507 && expression.at( 4 ).userType() == QMetaType::Type::Bool
3508 && expression.at( 4 ).toBool() ==
false )
3511 if ( expression.at( 2 ).userType() == QMetaType::Type::QVariantList || expression.at( 2 ).userType() == QMetaType::Type::QStringList )
3514 for (
const QVariant &p : expression.at( 2 ).toList() )
3516 parts << parseValue( p, context );
3519 if ( parts.size() > 1 )
3524 else if ( expression.at( 2 ).userType() == QMetaType::Type::QString
3525 || expression.at( 2 ).userType() == QMetaType::Type::Int
3526 || expression.at( 2 ).userType() == QMetaType::Type::Double
3527 || expression.at( 2 ).userType() == QMetaType::Type::LongLong )
3533 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3539 QString caseString = u
"CASE "_s;
3540 for (
int i = 2; i < expression.size() - 2; i += 2 )
3542 if ( expression.at( i ).userType() == QMetaType::Type::QVariantList || expression.at( i ).userType() == QMetaType::Type::QStringList )
3545 for (
const QVariant &p : expression.at( i ).toList() )
3550 if ( parts.size() > 1 )
3555 else if ( expression.at( i ).userType() == QMetaType::Type::QString
3556 || expression.at( i ).userType() == QMetaType::Type::Int
3557 || expression.at( i ).userType() == QMetaType::Type::Double
3558 || expression.at( i ).userType() == QMetaType::Type::LongLong )
3563 caseString += u
"THEN %1 "_s.arg( parseValue( expression.at( i + 1 ), context, colorExpected ) );
3565 caseString += u
"ELSE %1 END"_s.arg( parseValue( expression.last(), context, colorExpected ) );
3569 else if ( op ==
"to-string"_L1 )
3571 return u
"to_string(%1)"_s.arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3573 else if ( op ==
"to-boolean"_L1 )
3575 return u
"to_bool(%1)"_s.arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3577 else if ( op ==
"case"_L1 )
3579 QString caseString = u
"CASE"_s;
3580 for (
int i = 1; i < expression.size() - 2; i += 2 )
3582 const QString condition =
parseExpression( expression.value( i ).toList(), context );
3583 const QString value = parseValue( expression.value( i + 1 ), context, colorExpected );
3584 caseString += u
" WHEN (%1) THEN %2"_s.arg( condition, value );
3586 const QString value = parseValue( expression.constLast(), context, colorExpected );
3587 caseString += u
" ELSE %1 END"_s.arg( value );
3590 else if ( op ==
"zoom"_L1 && expression.count() == 1 )
3592 return u
"@vector_tile_zoom"_s;
3594 else if ( op ==
"concat"_L1 )
3596 QString concatString = u
"concat("_s;
3597 for (
int i = 1; i < expression.size(); i++ )
3600 concatString +=
", "_L1;
3601 concatString += parseValue( expression.value( i ), context );
3603 concatString +=
')'_L1;
3604 return concatString;
3606 else if ( op ==
"length"_L1 )
3608 return u
"length(%1)"_s.arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3610 else if ( op ==
"step"_L1 )
3612 const QString stepExpression =
parseExpression( expression.value( 1 ).toList(), context );
3613 if ( stepExpression.isEmpty() )
3615 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3619 QString caseString = u
"CASE "_s;
3621 for (
int i = expression.length() - 2; i > 0; i -= 2 )
3623 const QString stepValue = parseValue( expression.value( i + 1 ), context, colorExpected );
3627 caseString += u
" WHEN %1 >= %2 THEN (%3) "_s.arg( stepExpression, stepKey, stepValue );
3631 caseString += u
"ELSE (%1) END"_s.arg( stepValue );
3638 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression \"%2\"" ).arg( context.
layerId(), op ) );
3647 QString actualName = name;
3648 const int categorySeparator = name.indexOf(
':' );
3649 if ( categorySeparator > 0 )
3651 category = name.left( categorySeparator );
3654 actualName = name.mid( categorySeparator + 1 );
3663 if ( category.isEmpty() )
3668 if ( spriteImage.isNull() )
3670 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3674 const QVariantMap spriteDefinition = context.
spriteDefinitions( category ).value( actualName ).toMap();
3675 if ( spriteDefinition.size() == 0 )
3677 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3682 = 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() );
3683 if ( sprite.isNull() )
3685 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3689 spriteSize = sprite.size() / spriteDefinition.value( u
"pixelRatio"_s ).toDouble() * context.
pixelSizeConversionFactor();
3699 auto prepareBase64 = [](
const QImage &sprite ) {
3701 if ( !sprite.isNull() )
3704 QBuffer buffer( &blob );
3705 buffer.open( QIODevice::WriteOnly );
3706 sprite.save( &buffer,
"PNG" );
3708 const QByteArray encoded = blob.toBase64();
3709 path = QString( encoded );
3710 path.prepend(
"base64:"_L1 );
3715 switch ( value.userType() )
3717 case QMetaType::Type::QString:
3719 QString spriteName = value.toString();
3720 const thread_local QRegularExpression fieldNameMatch( u
"{([^}]+)}"_s );
3721 QRegularExpressionMatch match = fieldNameMatch.match( spriteName );
3722 if ( match.hasMatch() )
3724 const QString fieldName = match.captured( 1 );
3725 spriteProperty = u
"CASE"_s;
3726 spriteSizeProperty = u
"CASE"_s;
3728 spriteName.replace(
"(",
"\\("_L1 );
3729 spriteName.replace(
")",
"\\)"_L1 );
3730 spriteName.replace( fieldNameMatch, u
"([^\\/\\\\]+)"_s );
3731 const QRegularExpression fieldValueMatch( spriteName );
3733 for (
const QString &name : spriteNames )
3735 match = fieldValueMatch.match( name );
3736 if ( match.hasMatch() )
3740 const QString fieldValue = match.captured( 1 );
3742 path = prepareBase64( sprite );
3743 if ( spritePath.isEmpty() && !path.isEmpty() )
3749 spriteProperty += u
" WHEN \"%1\" = '%2' THEN '%3'"_s.arg( fieldName, fieldValue, path );
3750 spriteSizeProperty += u
" WHEN \"%1\" = '%2' THEN %3"_s.arg( fieldName ).arg( fieldValue ).arg( size.width() );
3754 spriteProperty +=
" END"_L1;
3755 spriteSizeProperty +=
" END"_L1;
3759 spriteProperty.clear();
3760 spriteSizeProperty.clear();
3761 const QImage sprite =
retrieveSprite( spriteName, context, spriteSize );
3762 spritePath = prepareBase64( sprite );
3767 case QMetaType::Type::QVariantMap:
3769 const QVariantList stops = value.toMap().value( u
"stops"_s ).toList();
3770 if ( stops.size() == 0 )
3777 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, spriteSize );
3778 spritePath = prepareBase64( sprite );
3780 spriteProperty = u
"CASE WHEN @vector_tile_zoom < %1 THEN '%2'"_s.arg( stops.value( 0 ).toList().value( 0 ).toString() ).arg( spritePath );
3781 spriteSizeProperty = u
"CASE WHEN @vector_tile_zoom < %1 THEN %2"_s.arg( stops.value( 0 ).toList().value( 0 ).toString() ).arg( spriteSize.width() );
3783 for (
int i = 0; i < stops.size() - 1; ++i )
3786 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, size );
3787 path = prepareBase64( sprite );
3789 spriteProperty += QStringLiteral(
3790 " WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
3793 .arg( stops.value( i ).toList().value( 0 ).toString(), stops.value( i + 1 ).toList().value( 0 ).toString(), path );
3794 spriteSizeProperty += QStringLiteral(
3795 " WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
3798 .arg( stops.value( i ).toList().value( 0 ).toString(), stops.value( i + 1 ).toList().value( 0 ).toString() )
3799 .arg( size.width() );
3801 sprite =
retrieveSprite( stops.last().toList().value( 1 ).toString(), context, size );
3802 path = prepareBase64( sprite );
3804 spriteProperty += QStringLiteral(
3805 " WHEN @vector_tile_zoom >= %1 "
3808 .arg( stops.last().toList().value( 0 ).toString() )
3810 spriteSizeProperty += QStringLiteral(
3811 " WHEN @vector_tile_zoom >= %1 "
3814 .arg( stops.last().toList().value( 0 ).toString() )
3815 .arg( size.width() );
3819 case QMetaType::Type::QVariantList:
3821 const QVariantList json = value.toList();
3822 const QString method = json.value( 0 ).toString();
3824 if ( method ==
"match"_L1 )
3826 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
3827 if ( attribute.isEmpty() )
3829 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
3833 spriteProperty = u
"CASE"_s;
3834 spriteSizeProperty = u
"CASE"_s;
3836 for (
int i = 2; i < json.length() - 1; i += 2 )
3838 const QVariant matchKey = json.value( i );
3839 const QVariant matchValue = json.value( i + 1 );
3840 QString matchString;
3841 switch ( matchKey.userType() )
3843 case QMetaType::Type::QVariantList:
3844 case QMetaType::Type::QStringList:
3846 const QVariantList keys = matchKey.toList();
3847 QStringList matchStringList;
3848 for (
const QVariant &key : keys )
3852 matchString = matchStringList.join(
',' );
3856 case QMetaType::Type::Bool:
3857 case QMetaType::Type::QString:
3858 case QMetaType::Type::Int:
3859 case QMetaType::Type::LongLong:
3860 case QMetaType::Type::Double:
3867 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported sprite type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( value.userType() ) ) ) );
3871 const QImage sprite =
retrieveSprite( matchValue.toString(), context, spriteSize );
3872 spritePath = prepareBase64( sprite );
3874 spriteProperty += QStringLiteral(
3878 .arg( attribute, matchString, spritePath );
3880 spriteSizeProperty += QStringLiteral(
3884 .arg( attribute, matchString )
3885 .arg( spriteSize.width() );
3888 if ( !json.constLast().toString().isEmpty() )
3890 const QImage sprite =
retrieveSprite( json.constLast().toString(), context, spriteSize );
3891 spritePath = prepareBase64( sprite );
3895 spritePath = QString();
3898 spriteProperty += u
" ELSE '%1' END"_s.arg( spritePath );
3899 spriteSizeProperty += u
" ELSE %3 END"_s.arg( spriteSize.width() );
3902 else if ( method ==
"step"_L1 )
3904 const QString expression =
parseExpression( json.value( 1 ).toList(), context );
3905 if ( expression.isEmpty() )
3907 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3911 spriteProperty = u
"CASE"_s;
3912 spriteSizeProperty = u
"CASE"_s;
3913 for (
int i = json.length() - 2; i > 2; i -= 2 )
3916 const QString stepValue = json.value( i + 1 ).toString();
3918 const QImage sprite =
retrieveSprite( stepValue, context, spriteSize );
3919 spritePath = prepareBase64( sprite );
3921 spriteProperty += u
" WHEN %1 >= %2 THEN '%3' "_s.arg( expression, stepKey, spritePath );
3922 spriteSizeProperty += u
" WHEN %1 >= %2 THEN %3 "_s.arg( expression ).arg( stepKey ).arg( spriteSize.width() );
3925 const QImage sprite =
retrieveSprite( json.at( 2 ).toString(), context, spriteSize );
3926 spritePath = prepareBase64( sprite );
3928 spriteProperty += u
"ELSE '%1' END"_s.arg( spritePath );
3929 spriteSizeProperty += u
"ELSE %3 END"_s.arg( spriteSize.width() );
3932 else if ( method ==
"case"_L1 )
3934 spriteProperty = u
"CASE"_s;
3935 spriteSizeProperty = u
"CASE"_s;
3936 for (
int i = 1; i < json.length() - 2; i += 2 )
3938 const QString caseExpression =
parseExpression( json.value( i ).toList(), context );
3939 const QString caseValue = json.value( i + 1 ).toString();
3941 const QImage sprite =
retrieveSprite( caseValue, context, spriteSize );
3942 spritePath = prepareBase64( sprite );
3944 spriteProperty += u
" WHEN %1 THEN '%2' "_s.arg( caseExpression, spritePath );
3945 spriteSizeProperty += u
" WHEN %1 THEN %2 "_s.arg( caseExpression ).arg( spriteSize.width() );
3947 const QImage sprite =
retrieveSprite( json.last().toString(), context, spriteSize );
3948 spritePath = prepareBase64( sprite );
3950 spriteProperty += u
"ELSE '%1' END"_s.arg( spritePath );
3951 spriteSizeProperty += u
"ELSE %3 END"_s.arg( spriteSize.width() );
3956 context.
pushWarning( QObject::tr(
"%1: Could not interpret sprite value list with method %2" ).arg( context.
layerId(), method ) );
3962 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported sprite type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( value.userType() ) ) ) );
3972 switch ( value.userType() )
3974 case QMetaType::Type::QVariantList:
3975 case QMetaType::Type::QStringList:
3978 case QMetaType::Type::Bool:
3979 case QMetaType::Type::QString:
3980 if ( colorExpected )
3985 return parseValue(
c, context );
3990 case QMetaType::Type::Int:
3991 case QMetaType::Type::LongLong:
3992 case QMetaType::Type::Double:
3993 return value.toString();
3995 case QMetaType::Type::QColor:
3996 c = value.value<QColor>();
3997 return QString(
"color_rgba(%1,%2,%3,%4)" ).arg(
c.red() ).arg(
c.green() ).arg(
c.blue() ).arg(
c.alpha() );
4000 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression part" ).arg( context.
layerId() ) );
4008 if ( value.toString() ==
"$type"_L1 )
4010 return u
"_geom_type"_s;
4012 if ( value.toString() ==
"level"_L1 )
4016 else if ( ( value.userType() == QMetaType::Type::QVariantList && value.toList().size() == 1 ) || value.userType() == QMetaType::Type::QStringList )
4018 if ( value.toList().size() > 1 )
4019 return value.toList().at( 1 ).toString();
4022 QString valueString = value.toList().value( 0 ).toString();
4023 if ( valueString ==
"geometry-type"_L1 )
4025 return u
"_geom_type"_s;
4030 else if ( value.userType() == QMetaType::Type::QVariantList && value.toList().size() > 1 )
4037QString QgsMapBoxGlStyleConverter::processLabelField(
const QString &
string,
bool &isExpression )
4041 const thread_local QRegularExpression singleFieldRx( u
"^{([^}]+)}$"_s );
4042 const QRegularExpressionMatch match = singleFieldRx.match(
string );
4043 if ( match.hasMatch() )
4045 isExpression =
false;
4046 return match.captured( 1 );
4049 const thread_local QRegularExpression multiFieldRx( u
"(?={[^}]+})"_s );
4050 const QStringList parts =
string.split( multiFieldRx );
4051 if ( parts.size() > 1 )
4053 isExpression =
true;
4056 for (
const QString &part : parts )
4058 if ( part.isEmpty() )
4061 if ( !part.contains(
'{' ) )
4068 const QStringList split = part.split(
'}' );
4070 if ( !split.at( 1 ).isEmpty() )
4073 return u
"concat(%1)"_s.arg( res.join(
',' ) );
4077 isExpression =
false;
4084 return mRenderer ? mRenderer->
clone() :
nullptr;
4089 return mLabeling ? mLabeling->
clone() :
nullptr;
4099 return mRasterSubLayers;
4104 QList<QgsMapLayer *> subLayers;
4107 const QString sourceName = subLayer.source();
4108 std::unique_ptr< QgsRasterLayer > rl;
4115 rl->pipe()->setDataDefinedProperties( subLayer.dataDefinedProperties() );
4122 subLayers.append( rl.release() );
4131 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
4134 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
4135 context = tmpContext.get();
4139 if (
string.compare(
"vector"_L1, Qt::CaseInsensitive ) == 0 )
4141 else if (
string.compare(
"raster"_L1, Qt::CaseInsensitive ) == 0 )
4143 else if (
string.compare(
"raster-dem"_L1, Qt::CaseInsensitive ) == 0 )
4145 else if (
string.compare(
"geojson"_L1, Qt::CaseInsensitive ) == 0 )
4147 else if (
string.compare(
"image"_L1, Qt::CaseInsensitive ) == 0 )
4149 else if (
string.compare(
"video"_L1, Qt::CaseInsensitive ) == 0 )
4151 context->
pushWarning( QObject::tr(
"Invalid source type \"%1\" for source \"%2\"" ).arg(
string, name ) );
4157 const QString name = it.key();
4158 const QVariantMap jsonSource = it.value().toMap();
4159 const QString typeString = jsonSource.value( u
"type"_s ).toString();
4182 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
4185 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
4186 context = tmpContext.get();
4189 auto raster = std::make_unique< QgsMapBoxGlStyleRasterSource >( name );
4190 if ( raster->setFromJson( source, context ) )
4191 mSources.append( raster.release() );
4194bool QgsMapBoxGlStyleConverter::numericArgumentsOnly(
const QVariant &bottomVariant,
const QVariant &topVariant,
double &bottom,
double &top )
4196 if ( bottomVariant.canConvert( QMetaType::Double ) && topVariant.canConvert( QMetaType::Double ) )
4198 bool bDoubleOk, tDoubleOk;
4199 bottom = bottomVariant.toDouble( &bDoubleOk );
4200 top = topVariant.toDouble( &tDoubleOk );
4201 return ( bDoubleOk && tDoubleOk );
4212 mWarnings << warning;
4227 return mSizeConversionFactor;
4232 mSizeConversionFactor = sizeConversionFactor;
4237 return mSpriteImage.keys();
4242 return mSpriteImage.contains( category ) ? mSpriteImage[category] : QImage();
4247 return mSpriteDefinitions.contains( category ) ? mSpriteDefinitions[category] : QVariantMap();
4252 mSpriteImage[category] = image;
4253 mSpriteDefinitions[category] = definitions;
4300 mAttribution = json.value( u
"attribution"_s ).toString();
4302 const QString scheme = json.value( u
"scheme"_s, u
"xyz"_s ).toString();
4303 if ( scheme.compare(
"xyz"_L1 ) == 0 )
4309 context->
pushWarning( QObject::tr(
"%1 scheme is not supported for raster source %2" ).arg( scheme,
name() ) );
4313 mMinZoom = json.value( u
"minzoom"_s, u
"0"_s ).toInt();
4314 mMaxZoom = json.value( u
"maxzoom"_s, u
"22"_s ).toInt();
4315 mTileSize = json.value( u
"tileSize"_s, u
"512"_s ).toInt();
4317 const QVariantList
tiles = json.value( u
"tiles"_s ).toList();
4318 for (
const QVariant &tile :
tiles )
4320 mTiles.append( tile.toString() );
4329 parts.insert( u
"type"_s, u
"xyz"_s );
4330 parts.insert( u
"url"_s, mTiles.value( 0 ) );
4332 if ( mTileSize == 256 )
4333 parts.insert( u
"tilePixelRation"_s, u
"1"_s );
4334 else if ( mTileSize == 512 )
4335 parts.insert( u
"tilePixelRation"_s, u
"2"_s );
4337 parts.insert( u
"zmax"_s, QString::number( mMaxZoom ) );
4338 parts.insert( u
"zmin"_s, QString::number( mMinZoom ) );
4341 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. Applies to polygon layers only...
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.
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 QString parseOpacityStops(double base, const QVariantList &stops, int maxOpacity, QgsMapBoxGlStyleConversionContext &context)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate alpha ...
static QString parseColorExpression(const QVariant &colorExpression, QgsMapBoxGlStyleConversionContext &context)
Converts an expression representing a color to a string (can be color string or an expression where a...
static QgsProperty parseInterpolateOpacityByZoom(const QVariantMap &json, int maxOpacity, QgsMapBoxGlStyleConversionContext *contextPtr=nullptr)
Interpolates opacity with either scale_linear() or scale_exp() (depending on base value).
static QString parseStops(double base, const QVariantList &stops, double multiplier, QgsMapBoxGlStyleConversionContext &context)
Parses a list of interpolation stops.
QgsVectorTileRenderer * renderer() const
Returns a new instance of a vector tile renderer representing the converted style,...
static QString parseExpression(const QVariantList &expression, QgsMapBoxGlStyleConversionContext &context, bool colorExpected=false)
Converts a MapBox GL expression to a QGIS expression.
PropertyType
Property types, for interpolated value conversion.
@ Point
Point/offset property.
@ 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.
QgsVectorTileLabeling * labeling() const
Returns a new instance of a vector tile labeling representing the converted style,...
QList< QgsMapBoxGlStyleRasterSubLayer > rasterSubLayers() const
Returns a list of raster sub layers contained in the style.
static QgsProperty parseInterpolateByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, double *defaultNumber=nullptr)
Parses a numeric value which is interpolated by zoom range.
static Qt::PenJoinStyle parseJoinStyle(const QString &style)
Converts a value to Qt::PenJoinStyle enum from JSON value.
static QgsProperty parseInterpolateStringByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, const QVariantMap &conversionMap, QString *defaultString=nullptr)
Interpolates a string by zoom.
static 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 QgsProperty parseInterpolatePointByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, QPointF *defaultPoint=nullptr)
Interpolates a point/offset with either scale_linear() or scale_exp() (depending on base value).
static bool parseCircleLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context)
Parses a circle layer.
Result convert(const QVariantMap &style, QgsMapBoxGlStyleConversionContext *context=nullptr)
Converts a JSON style map, and returns the resultant status of the conversion.
static QgsProperty parseInterpolateListByZoom(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Interpolates a list which starts with the interpolate function.
~QgsMapBoxGlStyleConverter()
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 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 parseInterpolateColorByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, QColor *defaultColor=nullptr)
Parses a color value which is interpolated by zoom range.
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...
void parseSources(const QVariantMap &sources, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse list of sources from JSON.
static QString interpolateExpression(double zoomMin, double zoomMax, QVariant valueMin, QVariant valueMax, double base, double multiplier=1, QgsMapBoxGlStyleConversionContext *contextPtr=nullptr)
Generates an interpolation for values between valueMin and valueMax, scaled between the ranges zoomMi...
static QColor parseColor(const QVariant &color, QgsMapBoxGlStyleConversionContext &context)
Parses a color in one of these supported formats:
static bool parseSymbolLayerAsRenderer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as a renderer.
static bool parseFillLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context, bool isBackgroundStyle=false)
Parses a fill layer.
static void parseSymbolLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, bool &hasRenderer, QgsVectorTileBasicLabelingStyle &labelingStyle, bool &hasLabeling, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as renderer or labeling.
static bool parseLineLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context)
Parses a line layer.
void parseRasterSource(const QVariantMap &source, const QString &name, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse a raster source from JSON.
static void colorAsHslaComponents(const QColor &color, int &hue, int &saturation, int &lightness, int &alpha)
Takes a QColor object and returns HSLA components in required format for QGIS color_hsla() expression...
static Qt::PenCapStyle parseCapStyle(const QString &style)
Converts a value to Qt::PenCapStyle enum from JSON value.
static QString parseStringStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, const QVariantMap &conversionMap, QString *defaultString=nullptr)
Parses a list of interpolation stops containing string values.
static QgsProperty parseMatchList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a match function value list.
static QgsProperty parseValueList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a value list (e.g.
static QString parseArrayStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier=1)
Takes numerical arrays from stops.
static QString parsePointStops(double base, const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier=1)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate point/...
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.
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.
@ 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 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).
Base class for labeling configuration classes for vector tile layers.
virtual QgsVectorTileLabeling * clone() const =0SIP_FACTORY
Returns a new copy of the object.
Abstract base class for all vector tile renderer implementations.
virtual QgsVectorTileRenderer * clone() const =0
Returns a clone of the renderer.
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