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.empty() )
723 if ( dashSource.at( 0 ).userType() == QMetaType::Type::QString )
729 u
"array_to_string(array_foreach(%1,@element * (%2)), ';')"_s
730 .arg( property.asExpression(), lineWidthProperty.
asExpression() )
741 QVector< double > rawDashVectorSizes;
742 rawDashVectorSizes.reserve( dashSource.size() );
743 for (
const QVariant &v : dashSource )
745 rawDashVectorSizes << v.toDouble();
749 if ( rawDashVectorSizes.size() == 1 )
752 rawDashVectorSizes.clear();
754 else if ( rawDashVectorSizes.size() % 2 == 1 )
758 rawDashVectorSizes.append( 0 );
761 if ( !rawDashVectorSizes.isEmpty() && ( !lineWidthProperty.
asExpression().isEmpty() ) )
763 QStringList dashArrayStringParts;
764 dashArrayStringParts.reserve( rawDashVectorSizes.size() );
765 for (
double v : std::as_const( rawDashVectorSizes ) )
770 QString arrayExpression = u
"array_to_string(array_foreach(array(%1),@element * (%2)), ';')"_s
771 .arg( dashArrayStringParts.join(
',' ), lineWidthProperty.
asExpression() );
776 for (
double v : std::as_const( rawDashVectorSizes ) )
778 dashVector << v * lineWidth;
787 QObject::tr(
"%1: Skipping unsupported line-dasharray type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineDashArray.userType() ) ) )
793 Qt::PenCapStyle penCapStyle = Qt::FlatCap;
794 Qt::PenJoinStyle penJoinStyle = Qt::MiterJoin;
795 if ( jsonLayer.contains( u
"layout"_s ) )
797 const QVariantMap jsonLayout = jsonLayer.value( u
"layout"_s ).toMap();
798 if ( jsonLayout.contains( u
"line-cap"_s ) )
800 penCapStyle =
parseCapStyle( jsonLayout.value( u
"line-cap"_s ).toString() );
802 if ( jsonLayout.contains( u
"line-join"_s ) )
804 penJoinStyle =
parseJoinStyle( jsonLayout.value( u
"line-join"_s ).toString() );
808 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsLineSymbol >() );
809 symbol->setOutputUnit( context.
targetUnit() );
811 if ( !rasterLineSprite.isEmpty() )
821 if ( lineOpacity != -1 )
823 symbol->setOpacity( lineOpacity );
829 symbol->setDataDefinedProperties( ddProperties );
831 if ( lineWidth != -1 )
835 symbol->changeSymbolLayer( 0, lineSymbol );
840 Q_ASSERT( lineSymbol );
850 if ( lineOpacity != -1 )
852 symbol->setOpacity( lineOpacity );
858 symbol->setDataDefinedProperties( ddProperties );
860 if ( lineColor.isValid() )
864 if ( lineWidth != -1 )
868 if ( !dashVector.empty() )
882 if ( !jsonLayer.contains( u
"paint"_s ) )
884 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
888 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
893 QColor circleFillColor;
894 if ( jsonPaint.contains( u
"circle-color"_s ) )
896 const QVariant jsonCircleColor = jsonPaint.value( u
"circle-color"_s );
897 switch ( jsonCircleColor.userType() )
899 case QMetaType::Type::QVariantMap:
903 case QMetaType::Type::QVariantList:
904 case QMetaType::Type::QStringList:
908 case QMetaType::Type::QString:
909 circleFillColor =
parseColor( jsonCircleColor.toString(), context );
913 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleColor.userType() ) ) ) );
920 circleFillColor = QColor( 0, 0, 0 );
924 double circleDiameter = 10.0;
925 if ( jsonPaint.contains( u
"circle-radius"_s ) )
927 const QVariant jsonCircleRadius = jsonPaint.value( u
"circle-radius"_s );
928 switch ( jsonCircleRadius.userType() )
930 case QMetaType::Type::Int:
931 case QMetaType::Type::LongLong:
932 case QMetaType::Type::Double:
936 case QMetaType::Type::QVariantMap:
941 case QMetaType::Type::QVariantList:
942 case QMetaType::Type::QStringList:
948 QObject::tr(
"%1: Skipping unsupported circle-radius type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleRadius.userType() ) ) )
954 double circleOpacity = -1.0;
955 if ( jsonPaint.contains( u
"circle-opacity"_s ) )
957 const QVariant jsonCircleOpacity = jsonPaint.value( u
"circle-opacity"_s );
958 switch ( jsonCircleOpacity.userType() )
960 case QMetaType::Type::Int:
961 case QMetaType::Type::LongLong:
962 case QMetaType::Type::Double:
963 circleOpacity = jsonCircleOpacity.toDouble();
966 case QMetaType::Type::QVariantMap:
970 case QMetaType::Type::QVariantList:
971 case QMetaType::Type::QStringList:
977 QObject::tr(
"%1: Skipping unsupported circle-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleOpacity.userType() ) ) )
982 if ( ( circleOpacity != -1 ) && circleFillColor.isValid() )
984 circleFillColor.setAlphaF( circleOpacity );
988 QColor circleStrokeColor;
989 if ( jsonPaint.contains( u
"circle-stroke-color"_s ) )
991 const QVariant jsonCircleStrokeColor = jsonPaint.value( u
"circle-stroke-color"_s );
992 switch ( jsonCircleStrokeColor.userType() )
994 case QMetaType::Type::QVariantMap:
998 case QMetaType::Type::QVariantList:
999 case QMetaType::Type::QStringList:
1003 case QMetaType::Type::QString:
1004 circleStrokeColor =
parseColor( jsonCircleStrokeColor.toString(), context );
1009 QObject::tr(
"%1: Skipping unsupported circle-stroke-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleStrokeColor.userType() ) ) )
1016 double circleStrokeWidth = -1.0;
1017 if ( jsonPaint.contains( u
"circle-stroke-width"_s ) )
1019 const QVariant circleStrokeWidthJson = jsonPaint.value( u
"circle-stroke-width"_s );
1020 switch ( circleStrokeWidthJson.userType() )
1022 case QMetaType::Type::Int:
1023 case QMetaType::Type::LongLong:
1024 case QMetaType::Type::Double:
1028 case QMetaType::Type::QVariantMap:
1029 circleStrokeWidth = -1.0;
1033 case QMetaType::Type::QVariantList:
1034 case QMetaType::Type::QStringList:
1040 QObject::tr(
"%1: Skipping unsupported circle-stroke-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( circleStrokeWidthJson.userType() ) ) )
1046 double circleStrokeOpacity = -1.0;
1047 if ( jsonPaint.contains( u
"circle-stroke-opacity"_s ) )
1049 const QVariant jsonCircleStrokeOpacity = jsonPaint.value( u
"circle-stroke-opacity"_s );
1050 switch ( jsonCircleStrokeOpacity.userType() )
1052 case QMetaType::Type::Int:
1053 case QMetaType::Type::LongLong:
1054 case QMetaType::Type::Double:
1055 circleStrokeOpacity = jsonCircleStrokeOpacity.toDouble();
1058 case QMetaType::Type::QVariantMap:
1062 case QMetaType::Type::QVariantList:
1063 case QMetaType::Type::QStringList:
1070 QObject::tr(
"%1: Skipping unsupported circle-stroke-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleStrokeOpacity.userType() ) ) )
1075 if ( ( circleStrokeOpacity != -1 ) && circleStrokeColor.isValid() )
1077 circleStrokeColor.setAlphaF( circleStrokeOpacity );
1081 QPointF circleTranslate;
1082 if ( jsonPaint.contains( u
"circle-translate"_s ) )
1084 const QVariant jsonCircleTranslate = jsonPaint.value( u
"circle-translate"_s );
1085 switch ( jsonCircleTranslate.userType() )
1087 case QMetaType::Type::QVariantMap:
1091 case QMetaType::Type::QVariantList:
1092 case QMetaType::Type::QStringList:
1099 QObject::tr(
"%1: Skipping unsupported circle-translate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleTranslate.userType() ) ) )
1105 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsMarkerSymbol >() );
1107 Q_ASSERT( markerSymbolLayer );
1110 symbol->setOutputUnit( context.
targetUnit() );
1113 if ( !circleTranslate.isNull() )
1115 markerSymbolLayer->
setOffset( circleTranslate );
1119 if ( circleFillColor.isValid() )
1123 if ( circleDiameter != -1 )
1125 markerSymbolLayer->
setSize( circleDiameter );
1128 if ( circleStrokeColor.isValid() )
1132 if ( circleStrokeWidth != -1 )
1147 hasLabeling =
false;
1148 hasRenderer =
false;
1150 if ( !jsonLayer.contains( u
"layout"_s ) )
1152 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
1155 const QVariantMap jsonLayout = jsonLayer.value( u
"layout"_s ).toMap();
1156 if ( !jsonLayout.contains( u
"text-field"_s ) )
1162 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
1168 if ( jsonLayout.contains( u
"text-size"_s ) )
1170 const QVariant jsonTextSize = jsonLayout.value( u
"text-size"_s );
1171 switch ( jsonTextSize.userType() )
1173 case QMetaType::Type::Int:
1174 case QMetaType::Type::LongLong:
1175 case QMetaType::Type::Double:
1179 case QMetaType::Type::QVariantMap:
1185 case QMetaType::Type::QVariantList:
1186 case QMetaType::Type::QStringList:
1192 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextSize.userType() ) ) ) );
1196 if ( textSizeProperty )
1203 constexpr double EM_TO_CHARS = 2.0;
1205 double textMaxWidth = -1;
1206 if ( jsonLayout.contains( u
"text-max-width"_s ) )
1208 const QVariant jsonTextMaxWidth = jsonLayout.value( u
"text-max-width"_s );
1209 switch ( jsonTextMaxWidth.userType() )
1211 case QMetaType::Type::Int:
1212 case QMetaType::Type::LongLong:
1213 case QMetaType::Type::Double:
1214 textMaxWidth = jsonTextMaxWidth.toDouble() * EM_TO_CHARS;
1217 case QMetaType::Type::QVariantMap:
1221 case QMetaType::Type::QVariantList:
1222 case QMetaType::Type::QStringList:
1228 QObject::tr(
"%1: Skipping unsupported text-max-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextMaxWidth.userType() ) ) )
1236 textMaxWidth = 10 * EM_TO_CHARS;
1239 double textLetterSpacing = -1;
1240 if ( jsonLayout.contains( u
"text-letter-spacing"_s ) )
1242 const QVariant jsonTextLetterSpacing = jsonLayout.value( u
"text-letter-spacing"_s );
1243 switch ( jsonTextLetterSpacing.userType() )
1245 case QMetaType::Type::Int:
1246 case QMetaType::Type::LongLong:
1247 case QMetaType::Type::Double:
1248 textLetterSpacing = jsonTextLetterSpacing.toDouble();
1251 case QMetaType::Type::QVariantMap:
1255 case QMetaType::Type::QVariantList:
1256 case QMetaType::Type::QStringList:
1262 QObject::tr(
"%1: Skipping unsupported text-letter-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextLetterSpacing.userType() ) ) )
1269 bool foundFont =
false;
1271 QString fontStyleName;
1273 bool allowOverlap = jsonLayout.contains( u
"text-allow-overlap"_s ) && jsonLayout.
value( u
"text-allow-overlap"_s ).toBool();
1275 if ( jsonLayout.contains( u
"text-font"_s ) )
1277 auto splitFontFamily = [](
const QString &fontName, QString &family, QString &style ) ->
bool {
1278 QString matchedFamily;
1279 const QStringList textFontParts = fontName.split(
' ' );
1280 for (
int i = textFontParts.size() - 1; i >= 1; --i )
1282 const QString candidateFontFamily = textFontParts.mid( 0, i ).join(
' ' );
1283 const QString candidateFontStyle = textFontParts.mid( i ).join(
' ' );
1288 family = processedFontFamily;
1289 style = candidateFontStyle;
1294 if ( processedFontFamily == matchedFamily )
1296 family = processedFontFamily;
1297 style = candidateFontStyle;
1301 family = matchedFamily;
1302 style = processedFontFamily;
1303 style.replace( matchedFamily, QString() );
1304 style = style.trimmed();
1305 if ( !style.isEmpty() && !candidateFontStyle.isEmpty() )
1307 style += u
" %1"_s.arg( candidateFontStyle );
1315 if ( QFontDatabase().hasFamily( processedFontFamily ) )
1318 family = processedFontFamily;
1324 family = matchedFamily;
1331 const QVariant jsonTextFont = jsonLayout.value( u
"text-font"_s );
1332 if ( jsonTextFont.userType() != QMetaType::Type::QVariantList
1333 && jsonTextFont.userType() != QMetaType::Type::QStringList
1334 && jsonTextFont.userType() != QMetaType::Type::QString
1335 && jsonTextFont.userType() != QMetaType::Type::QVariantMap )
1337 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-font type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextFont.userType() ) ) ) );
1341 switch ( jsonTextFont.userType() )
1343 case QMetaType::Type::QVariantList:
1344 case QMetaType::Type::QStringList:
1345 fontName = jsonTextFont.toList().value( 0 ).toString();
1348 case QMetaType::Type::QString:
1349 fontName = jsonTextFont.toString();
1352 case QMetaType::Type::QVariantMap:
1354 QString familyCaseString = u
"CASE "_s;
1355 QString styleCaseString = u
"CASE "_s;
1357 const QVariantList stops = jsonTextFont.toMap().value( u
"stops"_s ).toList();
1360 for (
int i = 0; i < stops.length() - 1; ++i )
1363 const QVariant bz = stops.value( i ).toList().value( 0 );
1364 const QString bv = stops.value( i ).toList().value( 1 ).userType() == QMetaType::Type::QString ? stops.value( i ).toList().value( 1 ).toString()
1365 : stops.value( i ).toList().value( 1 ).toList().value( 0 ).toString();
1366 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
1368 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1374 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
1375 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
1377 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1382 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1384 familyCaseString += QStringLiteral(
1385 "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1389 styleCaseString += QStringLiteral(
1390 "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1397 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1403 const QString bv = stops.constLast().toList().value( 1 ).userType() == QMetaType::Type::QString ? stops.constLast().toList().value( 1 ).toString()
1404 : stops.constLast().toList().value( 1 ).toList().value( 0 ).toString();
1405 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1412 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1419 fontName = fontFamily;
1429 if ( splitFontFamily( fontName, fontFamily, fontStyleName ) )
1432 if ( !fontStyleName.isEmpty() )
1433 textFont.setStyleName( fontStyleName );
1443 fontName = u
"Open Sans"_s;
1445 textFont.setStyleName( u
"Regular"_s );
1446 fontStyleName = u
"Regular"_s;
1451 fontName = u
"Arial Unicode MS"_s;
1453 textFont.setStyleName( u
"Regular"_s );
1454 fontStyleName = u
"Regular"_s;
1459 fontName = u
"Open Sans, Arial Unicode MS"_s;
1462 if ( !foundFont && !fontName.isEmpty() )
1464 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), fontName ) );
1469 if ( jsonPaint.contains( u
"text-color"_s ) )
1471 const QVariant jsonTextColor = jsonPaint.value( u
"text-color"_s );
1472 switch ( jsonTextColor.userType() )
1474 case QMetaType::Type::QVariantMap:
1478 case QMetaType::Type::QVariantList:
1479 case QMetaType::Type::QStringList:
1483 case QMetaType::Type::QString:
1484 textColor =
parseColor( jsonTextColor.toString(), context );
1488 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextColor.userType() ) ) ) );
1495 textColor = QColor( 0, 0, 0 );
1499 QColor bufferColor( 0, 0, 0, 0 );
1500 if ( jsonPaint.contains( u
"text-halo-color"_s ) )
1502 const QVariant jsonBufferColor = jsonPaint.value( u
"text-halo-color"_s );
1503 switch ( jsonBufferColor.userType() )
1505 case QMetaType::Type::QVariantMap:
1509 case QMetaType::Type::QVariantList:
1510 case QMetaType::Type::QStringList:
1514 case QMetaType::Type::QString:
1515 bufferColor =
parseColor( jsonBufferColor.toString(), context );
1520 QObject::tr(
"%1: Skipping unsupported text-halo-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonBufferColor.userType() ) ) )
1526 double bufferSize = 0.0;
1530 constexpr double BUFFER_SIZE_SCALE = 2.0;
1531 if ( jsonPaint.contains( u
"text-halo-width"_s ) )
1533 const QVariant jsonHaloWidth = jsonPaint.value( u
"text-halo-width"_s );
1534 QString bufferSizeDataDefined;
1535 switch ( jsonHaloWidth.userType() )
1537 case QMetaType::Type::Int:
1538 case QMetaType::Type::LongLong:
1539 case QMetaType::Type::Double:
1543 case QMetaType::Type::QVariantMap:
1548 case QMetaType::Type::QVariantList:
1549 case QMetaType::Type::QStringList:
1555 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-halo-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonHaloWidth.userType() ) ) ) );
1561 if ( bufferSize > 0 )
1563 if ( textSize > 0 && bufferSizeDataDefined.isEmpty() )
1565 bufferSize = std::min( bufferSize, textSize * BUFFER_SIZE_SCALE / 4 );
1567 else if ( textSize > 0 && !bufferSizeDataDefined.isEmpty() )
1569 bufferSizeDataDefined = u
"min(%1/4, %2)"_s.arg( textSize * BUFFER_SIZE_SCALE ).arg( bufferSizeDataDefined );
1572 else if ( !bufferSizeDataDefined.isEmpty() )
1574 bufferSizeDataDefined = u
"min(%1*%2/4, %3)"_s.arg( textSizeProperty.
asExpression() ).arg( BUFFER_SIZE_SCALE ).arg( bufferSizeDataDefined );
1577 else if ( bufferSizeDataDefined.isEmpty() )
1579 bufferSizeDataDefined = u
"min(%1*%2/4, %3)"_s.arg( textSizeProperty.
asExpression() ).arg( BUFFER_SIZE_SCALE ).arg( bufferSize );
1585 double haloBlurSize = 0;
1586 if ( jsonPaint.contains( u
"text-halo-blur"_s ) )
1588 const QVariant jsonTextHaloBlur = jsonPaint.value( u
"text-halo-blur"_s );
1589 switch ( jsonTextHaloBlur.userType() )
1591 case QMetaType::Type::Int:
1592 case QMetaType::Type::LongLong:
1593 case QMetaType::Type::Double:
1601 QObject::tr(
"%1: Skipping unsupported text-halo-blur type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextHaloBlur.userType() ) ) )
1609 if ( textColor.isValid() )
1611 if ( textSize >= 0 )
1616 if ( !fontStyleName.isEmpty() )
1619 if ( textLetterSpacing > 0 )
1621 QFont f = format.
font();
1622 f.setLetterSpacing( QFont::AbsoluteSpacing, textLetterSpacing );
1626 if ( bufferSize > 0 )
1629 const double opacity = bufferColor.alphaF();
1630 bufferColor.setAlphaF( 1.0 );
1638 if ( haloBlurSize > 0 )
1661 if ( textMaxWidth > 0 )
1667 if ( jsonLayout.contains( u
"text-field"_s ) )
1669 const QVariant jsonTextField = jsonLayout.value( u
"text-field"_s );
1670 switch ( jsonTextField.userType() )
1672 case QMetaType::Type::QString:
1674 labelSettings.
fieldName = processLabelField( jsonTextField.toString(), labelSettings.
isExpression );
1678 case QMetaType::Type::QVariantList:
1679 case QMetaType::Type::QStringList:
1681 const QVariantList textFieldList = jsonTextField.toList();
1689 if ( textFieldList.size() > 2 && textFieldList.at( 0 ).toString() ==
"format"_L1 )
1692 for (
int i = 1; i < textFieldList.size(); ++i )
1694 bool isExpression =
false;
1695 const QString part = processLabelField( textFieldList.at( i ).toString(), isExpression );
1696 if ( !isExpression )
1703 labelSettings.
fieldName = u
"concat(%1)"_s.arg( parts.join(
',' ) );
1718 case QMetaType::Type::QVariantMap:
1720 const QVariantList stops = jsonTextField.toMap().value( u
"stops"_s ).toList();
1721 if ( !stops.empty() )
1728 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-field dictionary" ).arg( context.
layerId() ) );
1734 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-field type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextField.userType() ) ) ) );
1739 if ( jsonLayout.contains( u
"text-rotate"_s ) )
1741 const QVariant jsonTextRotate = jsonLayout.value( u
"text-rotate"_s );
1742 switch ( jsonTextRotate.userType() )
1744 case QMetaType::Type::Double:
1745 case QMetaType::Type::Int:
1747 labelSettings.
angleOffset = jsonTextRotate.toDouble();
1751 case QMetaType::Type::QVariantList:
1752 case QMetaType::Type::QStringList:
1759 case QMetaType::Type::QVariantMap:
1761 QVariantMap rotateMap = jsonTextRotate.toMap();
1762 if ( rotateMap.contains( u
"property"_s ) && rotateMap[u
"type"_s].toString() ==
"identity"_L1 )
1768 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-rotate map content (%2)" ).arg( context.
layerId(), QString( QJsonDocument::fromVariant( rotateMap ).toJson() ) ) );
1773 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextRotate.userType() ) ) ) );
1778 if ( jsonLayout.contains( u
"text-transform"_s ) )
1780 const QString textTransform = jsonLayout.value( u
"text-transform"_s ).toString();
1781 if ( textTransform ==
"uppercase"_L1 )
1785 else if ( textTransform ==
"lowercase"_L1 )
1794 if ( jsonLayout.contains( u
"symbol-placement"_s ) )
1796 const QString symbolPlacement = jsonLayout.value( u
"symbol-placement"_s ).toString();
1797 if ( symbolPlacement ==
"line"_L1 )
1803 if ( jsonLayout.contains( u
"text-rotation-alignment"_s ) )
1805 const QString textRotationAlignment = jsonLayout.value( u
"text-rotation-alignment"_s ).toString();
1806 if ( textRotationAlignment ==
"viewport"_L1 )
1816 if ( jsonLayout.contains( u
"text-offset"_s ) )
1818 const QVariant jsonTextOffset = jsonLayout.value( u
"text-offset"_s );
1821 switch ( jsonTextOffset.userType() )
1823 case QMetaType::Type::QVariantMap:
1824 textOffsetProperty =
parseInterpolatePointByZoom( jsonTextOffset.toMap(), context, !textSizeProperty ? textSize : 1.0, &textOffset );
1825 if ( !textSizeProperty )
1833 u
"with_variable('text_size',%2,abs(array_get(%1,1))*@text_size-@text_size)"_s.arg( textOffsetProperty.
asExpression(), textSizeProperty.
asExpression() )
1839 case QMetaType::Type::QVariantList:
1840 case QMetaType::Type::QStringList:
1841 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize, jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1846 QObject::tr(
"%1: Skipping unsupported text-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextOffset.userType() ) ) )
1851 if ( !textOffset.isNull() )
1854 labelSettings.
dist = std::abs( textOffset.y() ) - textSize;
1856 if ( textSizeProperty && !textOffsetProperty )
1860 u
"with_variable('text_size',%2,%1*@text_size-@text_size)"_s.arg( std::abs( textOffset.y() / textSize ) ).arg( textSizeProperty.
asExpression() )
1866 if ( textOffset.isNull() )
1874 if ( jsonLayout.contains( u
"text-justify"_s ) )
1876 const QVariant jsonTextJustify = jsonLayout.value( u
"text-justify"_s );
1879 QString textAlign = u
"center"_s;
1881 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 } };
1883 switch ( jsonTextJustify.userType() )
1885 case QMetaType::Type::QString:
1886 textAlign = jsonTextJustify.toString();
1889 case QMetaType::Type::QVariantList:
1893 case QMetaType::Type::QVariantMap:
1898 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-justify type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextJustify.userType() ) ) ) );
1902 if ( textAlign ==
"left"_L1 )
1904 else if ( textAlign ==
"right"_L1 )
1906 else if ( textAlign ==
"center"_L1 )
1908 else if ( textAlign ==
"follow"_L1 )
1918 if ( jsonLayout.contains( u
"text-anchor"_s ) )
1920 const QVariant jsonTextAnchor = jsonLayout.value( u
"text-anchor"_s );
1923 const QVariantMap conversionMap {
1929 { u
"top-left"_s, 8 },
1930 { u
"top-right"_s, 6 },
1931 { u
"bottom-left"_s, 2 },
1932 { u
"bottom-right"_s, 0 },
1935 switch ( jsonTextAnchor.userType() )
1937 case QMetaType::Type::QString:
1938 textAnchor = jsonTextAnchor.toString();
1941 case QMetaType::Type::QVariantList:
1945 case QMetaType::Type::QVariantMap:
1950 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-anchor type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextAnchor.userType() ) ) ) );
1954 if ( textAnchor ==
"center"_L1 )
1956 else if ( textAnchor ==
"left"_L1 )
1958 else if ( textAnchor ==
"right"_L1 )
1960 else if ( textAnchor ==
"top"_L1 )
1962 else if ( textAnchor ==
"bottom"_L1 )
1964 else if ( textAnchor ==
"top-left"_L1 )
1966 else if ( textAnchor ==
"top-right"_L1 )
1968 else if ( textAnchor ==
"bottom-left"_L1 )
1970 else if ( textAnchor ==
"bottom-right"_L1 )
1975 if ( jsonLayout.contains( u
"text-offset"_s ) )
1977 const QVariant jsonTextOffset = jsonLayout.value( u
"text-offset"_s );
1980 switch ( jsonTextOffset.userType() )
1982 case QMetaType::Type::QVariantMap:
1986 case QMetaType::Type::QVariantList:
1987 case QMetaType::Type::QStringList:
1988 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize, jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1992 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextOffset.userType() ) ) ) );
1996 if ( !textOffset.isNull() )
1999 labelSettings.
xOffset = textOffset.x();
2000 labelSettings.
yOffset = textOffset.y();
2008 QString spriteProperty, spriteSizeProperty;
2010 if ( !sprite.isEmpty() )
2013 if ( jsonLayout.contains( u
"icon-size"_s ) )
2016 const QVariant jsonIconSize = jsonLayout.
value( u
"icon-size"_s );
2017 switch ( jsonIconSize.userType() )
2019 case QMetaType::Type::Int:
2020 case QMetaType::Type::LongLong:
2021 case QMetaType::Type::Double:
2023 size = jsonIconSize.toDouble();
2024 if ( !spriteSizeProperty.isEmpty() )
2031 case QMetaType::Type::QVariantMap:
2035 case QMetaType::Type::QVariantList:
2036 case QMetaType::Type::QStringList:
2041 QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) )
2048 if ( !spriteSizeProperty.isEmpty() )
2061 markerLayer->
setPath( sprite );
2062 markerLayer->
setSize( spriteSize.width() );
2065 if ( !spriteProperty.isEmpty() )
2075 backgroundSettings.
setSize( spriteSize * size );
2083 if ( jsonLayout.contains( u
"symbol-spacing"_s ) )
2086 const QVariant jsonSpacing = jsonLayout.value( u
"symbol-spacing"_s );
2096 switch ( jsonSpacing.userType() )
2098 case QMetaType::Type::Int:
2099 case QMetaType::Type::LongLong:
2100 case QMetaType::Type::Double:
2107 case QMetaType::Type::QVariantMap:
2113 case QMetaType::Type::QVariantList:
2114 case QMetaType::Type::QStringList:
2121 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported symbol-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonSpacing.userType() ) ) ) );
2129 if ( textSize >= 0 )
2152 if ( !jsonLayer.contains( u
"layout"_s ) )
2154 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
2157 const QVariantMap jsonLayout = jsonLayer.value( u
"layout"_s ).toMap();
2159 if ( jsonLayout.value( u
"symbol-placement"_s ).toString() ==
"line"_L1 && !jsonLayout.contains( u
"text-field"_s ) )
2163 double spacing = -1.0;
2164 if ( jsonLayout.contains( u
"symbol-spacing"_s ) )
2166 const QVariant jsonSpacing = jsonLayout.value( u
"symbol-spacing"_s );
2167 switch ( jsonSpacing.userType() )
2169 case QMetaType::Type::Int:
2170 case QMetaType::Type::LongLong:
2171 case QMetaType::Type::Double:
2175 case QMetaType::Type::QVariantMap:
2179 case QMetaType::Type::QVariantList:
2180 case QMetaType::Type::QStringList:
2185 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported symbol-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonSpacing.userType() ) ) ) );
2195 bool rotateMarkers =
true;
2196 if ( jsonLayout.contains( u
"icon-rotation-alignment"_s ) )
2198 const QString alignment = jsonLayout.value( u
"icon-rotation-alignment"_s ).toString();
2199 if ( alignment ==
"map"_L1 || alignment ==
"auto"_L1 )
2201 rotateMarkers =
true;
2203 else if ( alignment ==
"viewport"_L1 )
2205 rotateMarkers =
false;
2210 double rotation = 0.0;
2211 if ( jsonLayout.contains( u
"icon-rotate"_s ) )
2213 const QVariant jsonIconRotate = jsonLayout.value( u
"icon-rotate"_s );
2214 switch ( jsonIconRotate.userType() )
2216 case QMetaType::Type::Int:
2217 case QMetaType::Type::LongLong:
2218 case QMetaType::Type::Double:
2219 rotation = jsonIconRotate.toDouble();
2222 case QMetaType::Type::QVariantMap:
2226 case QMetaType::Type::QVariantList:
2227 case QMetaType::Type::QStringList:
2232 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconRotate.userType() ) ) ) );
2248 QString spriteProperty, spriteSizeProperty;
2250 if ( !sprite.isNull() )
2252 markerLayer->
setPath( sprite );
2253 markerLayer->
setSize( spriteSize.width() );
2256 if ( !spriteProperty.isEmpty() )
2263 if ( jsonLayout.contains( u
"icon-size"_s ) )
2265 const QVariant jsonIconSize = jsonLayout.value( u
"icon-size"_s );
2268 switch ( jsonIconSize.userType() )
2270 case QMetaType::Type::Int:
2271 case QMetaType::Type::LongLong:
2272 case QMetaType::Type::Double:
2274 size = jsonIconSize.toDouble();
2275 if ( !spriteSizeProperty.isEmpty() )
2282 case QMetaType::Type::QVariantMap:
2286 case QMetaType::Type::QVariantList:
2287 case QMetaType::Type::QStringList:
2291 context.
pushWarning( QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) ) );
2294 markerLayer->
setSize( size * spriteSize.width() );
2297 if ( !spriteSizeProperty.isEmpty() )
2313 std::unique_ptr< QgsSymbol > symbol = std::make_unique< QgsLineSymbol >(
QgsSymbolLayerList() << lineSymbol );
2316 symbol->setOutputUnit( context.
targetUnit() );
2320 rendererStyle.
setSymbol( symbol.release() );
2323 else if ( jsonLayout.contains( u
"icon-image"_s ) )
2325 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
2328 QString spriteProperty, spriteSizeProperty;
2330 if ( !sprite.isEmpty() || !spriteProperty.isEmpty() )
2333 rasterMarker->
setPath( sprite );
2334 rasterMarker->
setSize( spriteSize.width() );
2338 if ( !spriteProperty.isEmpty() )
2344 if ( jsonLayout.contains( u
"icon-size"_s ) )
2346 const QVariant jsonIconSize = jsonLayout.value( u
"icon-size"_s );
2349 switch ( jsonIconSize.userType() )
2351 case QMetaType::Type::Int:
2352 case QMetaType::Type::LongLong:
2353 case QMetaType::Type::Double:
2355 size = jsonIconSize.toDouble();
2356 if ( !spriteSizeProperty.isEmpty() )
2363 case QMetaType::Type::QVariantMap:
2367 case QMetaType::Type::QVariantList:
2368 case QMetaType::Type::QStringList:
2373 QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) )
2377 rasterMarker->
setSize( size * spriteSize.width() );
2380 if ( !spriteSizeProperty.isEmpty() )
2392 double rotation = 0.0;
2393 if ( jsonLayout.contains( u
"icon-rotate"_s ) )
2395 const QVariant jsonIconRotate = jsonLayout.value( u
"icon-rotate"_s );
2396 switch ( jsonIconRotate.userType() )
2398 case QMetaType::Type::Int:
2399 case QMetaType::Type::LongLong:
2400 case QMetaType::Type::Double:
2401 rotation = jsonIconRotate.toDouble();
2404 case QMetaType::Type::QVariantMap:
2408 case QMetaType::Type::QVariantList:
2409 case QMetaType::Type::QStringList:
2415 QObject::tr(
"%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconRotate.userType() ) ) )
2421 double iconOpacity = -1.0;
2422 if ( jsonPaint.contains( u
"icon-opacity"_s ) )
2424 const QVariant jsonIconOpacity = jsonPaint.value( u
"icon-opacity"_s );
2425 switch ( jsonIconOpacity.userType() )
2427 case QMetaType::Type::Int:
2428 case QMetaType::Type::LongLong:
2429 case QMetaType::Type::Double:
2430 iconOpacity = jsonIconOpacity.toDouble();
2433 case QMetaType::Type::QVariantMap:
2437 case QMetaType::Type::QVariantList:
2438 case QMetaType::Type::QStringList:
2444 QObject::tr(
"%1: Skipping unsupported icon-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconOpacity.userType() ) ) )
2451 rasterMarker->
setAngle( rotation );
2452 if ( iconOpacity >= 0 )
2456 rendererStyle.
setSymbol( markerSymbol );
2467 const double base = json.
value( u
"base"_s, u
"1"_s ).toDouble();
2468 const QVariantList stops = json.value( u
"stops"_s ).toList();
2469 if ( stops.empty() )
2472 QString caseString = u
"CASE "_s;
2473 const QString colorComponent(
"color_part(%1,'%2')" );
2475 for (
int i = 0; i < stops.length() - 1; ++i )
2478 const QString bz = stops.at( i ).toList().value( 0 ).toString();
2480 const QString tz = stops.at( i + 1 ).toList().value( 0 ).toString();
2482 const QVariant bcVariant = stops.at( i ).toList().value( 1 );
2483 const QVariant tcVariant = stops.at( i + 1 ).toList().value( 1 );
2485 const QColor bottomColor =
parseColor( bcVariant.toString(), context );
2486 const QColor topColor =
parseColor( tcVariant.toString(), context );
2488 if ( i == 0 && bottomColor.isValid() )
2495 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 );
2498 if ( bottomColor.isValid() && topColor.isValid() )
2510 caseString += QStringLiteral(
2511 "WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2524 json.value( u
"x1"_s ).toDouble(),
2525 json.value( u
"y1"_s ).toDouble(),
2526 json.value( u
"x2"_s ).toDouble(),
2527 json.value( u
"y2"_s ).toDouble(),
2538 json.value( u
"x1"_s ).toDouble(),
2539 json.value( u
"y1"_s ).toDouble(),
2540 json.value( u
"x2"_s ).toDouble(),
2541 json.value( u
"y2"_s ).toDouble(),
2552 json.value( u
"x1"_s ).toDouble(),
2553 json.value( u
"y1"_s ).toDouble(),
2554 json.value( u
"x2"_s ).toDouble(),
2555 json.value( u
"y2"_s ).toDouble(),
2566 json.value( u
"x1"_s ).toDouble(),
2567 json.value( u
"y1"_s ).toDouble(),
2568 json.value( u
"x2"_s ).toDouble(),
2569 json.value( u
"y2"_s ).toDouble(),
2580 caseString += QStringLiteral(
2581 "WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2590 colorComponent.arg( bottomColorExpr ).arg(
"hsl_hue" ),
2591 colorComponent.arg( topColorExpr ).arg(
"hsl_hue" ),
2594 json.value( u
"x1"_s ).toDouble(),
2595 json.value( u
"y1"_s ).toDouble(),
2596 json.value( u
"x2"_s ).toDouble(),
2597 json.value( u
"y2"_s ).toDouble(),
2604 colorComponent.arg( bottomColorExpr ).arg(
"hsl_saturation" ),
2605 colorComponent.arg( topColorExpr ).arg(
"hsl_saturation" ),
2608 json.value( u
"x1"_s ).toDouble(),
2609 json.value( u
"y1"_s ).toDouble(),
2610 json.value( u
"x2"_s ).toDouble(),
2611 json.value( u
"y2"_s ).toDouble(),
2618 colorComponent.arg( bottomColorExpr ).arg(
"lightness" ),
2619 colorComponent.arg( topColorExpr ).arg(
"lightness" ),
2622 json.value( u
"x1"_s ).toDouble(),
2623 json.value( u
"y1"_s ).toDouble(),
2624 json.value( u
"x2"_s ).toDouble(),
2625 json.value( u
"y2"_s ).toDouble(),
2632 colorComponent.arg( bottomColorExpr ).arg(
"alpha" ),
2633 colorComponent.arg( topColorExpr ).arg(
"alpha" ),
2636 json.value( u
"x1"_s ).toDouble(),
2637 json.value( u
"y1"_s ).toDouble(),
2638 json.value( u
"x2"_s ).toDouble(),
2639 json.value( u
"y2"_s ).toDouble(),
2648 const QString tz = stops.last().toList().value( 0 ).toString();
2649 const QVariant tcVariant = stops.last().toList().value( 1 );
2651 if ( tcVariant.userType() == QMetaType::Type::QString )
2654 if ( topColor.isValid() )
2661 caseString += QStringLiteral(
2662 "WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2663 "ELSE color_hsla(%2, %3, %4, %5) END"
2672 else if ( tcVariant.userType() == QMetaType::QVariantList )
2676 caseString += QStringLiteral(
2677 "WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2678 "ELSE color_hsla(%2, %3, %4, %5) END"
2681 .arg( colorComponent.arg( topColorExpr ).arg(
"hsl_hue" ) )
2682 .arg( colorComponent.arg( topColorExpr ).arg(
"hsl_saturation" ) )
2683 .arg( colorComponent.arg( topColorExpr ).arg(
"lightness" ) )
2684 .arg( colorComponent.arg( topColorExpr ).arg(
"alpha" ) );
2687 if ( !stops.empty() && defaultColor )
2688 *defaultColor =
parseColor( stops.value( 0 ).toList().value( 1 ).toString(), context );
2695 const double base = json.
value( u
"base"_s, u
"1"_s ).toDouble();
2696 const QVariantList stops = json.value( u
"stops"_s ).toList();
2697 if ( stops.empty() )
2700 QString scaleExpression;
2701 if ( stops.size() <= 2 )
2704 stops.value( 0 ).toList().value( 0 ).toDouble(),
2705 stops.last().toList().value( 0 ).toDouble(),
2706 stops.value( 0 ).toList().value( 1 ),
2707 stops.last().toList().value( 1 ),
2710 json.value( u
"x1"_s ).toDouble(),
2711 json.value( u
"y1"_s ).toDouble(),
2712 json.value( u
"x2"_s ).toDouble(),
2713 json.value( u
"y2"_s ).toDouble(),
2721 =
parseStops( base, stops, multiplier, context, type, json.value( u
"x1"_s ).toDouble(), json.value( u
"y1"_s ).toDouble(), json.value( u
"x2"_s ).toDouble(), json.value( u
"y2"_s ).toDouble() );
2724 if ( !stops.empty() && defaultNumber )
2725 *defaultNumber = stops.value( 0 ).toList().value( 1 ).toDouble() * multiplier;
2735 context = *contextPtr;
2737 const double base = json.value( u
"base"_s, u
"1"_s ).toDouble();
2738 const QVariantList stops = json.value( u
"stops"_s ).toList();
2739 if ( stops.empty() )
2742 QString scaleExpression;
2743 if ( stops.length() <= 2 )
2745 const QVariant bv = stops.value( 0 ).toList().value( 1 );
2746 const QVariant tv = stops.last().toList().value( 1 );
2747 double bottom = 0.0;
2749 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2751 stops.value( 0 ).toList().value( 0 ).toDouble(),
2752 stops.last().toList().value( 0 ).toDouble(),
2753 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2754 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ),
2757 json.value( u
"x1"_s ).toDouble(),
2758 json.value( u
"y1"_s ).toDouble(),
2759 json.value( u
"x2"_s ).toDouble(),
2760 json.value( u
"y2"_s ).toDouble(),
2768 =
parseOpacityStops( base, stops, maxOpacity, context, type, json.value( u
"x1"_s ).toDouble(), json.value( u
"y1"_s ).toDouble(), json.value( u
"x2"_s ).toDouble(), json.value( u
"y2"_s ).toDouble() );
2777 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() )
2778 .arg( stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity );
2780 for (
int i = 0; i < stops.size() - 1; ++i )
2782 const QVariant bv = stops.value( i ).toList().value( 1 );
2783 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2784 double bottom = 0.0;
2786 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2788 caseString += QStringLiteral(
2789 " WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
2790 "THEN set_color_part(@symbol_color, 'alpha', %3)"
2793 stops.value( i ).toList().value( 0 ).toString(),
2794 stops.value( i + 1 ).toList().value( 0 ).toString(),
2796 stops.value( i ).toList().value( 0 ).toDouble(),
2797 stops.value( i + 1 ).toList().value( 0 ).toDouble(),
2798 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2799 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ),
2813 bool numeric =
false;
2814 const QVariant vv = stops.last().toList().value( 1 );
2815 double dv = vv.toDouble( &numeric );
2817 caseString += QStringLiteral(
2818 " WHEN @vector_tile_zoom >= %1 "
2819 "THEN set_color_part(@symbol_color, 'alpha', %2) END"
2821 .arg( stops.last().toList().value( 0 ).toString(), numeric ? QString::number( dv * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( vv, context ) ).arg( maxOpacity ) );
2827 const double base = json.
value( u
"base"_s, u
"1"_s ).toDouble();
2828 const QVariantList stops = json.value( u
"stops"_s ).toList();
2829 if ( stops.empty() )
2832 QString scaleExpression;
2833 if ( stops.size() <= 2 )
2835 scaleExpression = u
"array(%1,%2)"_s.arg(
2837 stops.value( 0 ).toList().value( 0 ).toDouble(),
2838 stops.last().toList().value( 0 ).toDouble(),
2839 stops.value( 0 ).toList().value( 1 ).toList().value( 0 ),
2840 stops.last().toList().value( 1 ).toList().value( 0 ),
2843 json.value( u
"x1"_s ).toDouble(),
2844 json.value( u
"y1"_s ).toDouble(),
2845 json.value( u
"x2"_s ).toDouble(),
2846 json.value( u
"y2"_s ).toDouble(),
2851 stops.value( 0 ).toList().value( 0 ).toDouble(),
2852 stops.last().toList().value( 0 ).toDouble(),
2853 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ),
2854 stops.last().toList().value( 1 ).toList().value( 1 ),
2857 json.value( u
"x1"_s ).toDouble(),
2858 json.value( u
"y1"_s ).toDouble(),
2859 json.value( u
"x2"_s ).toDouble(),
2860 json.value( u
"y2"_s ).toDouble(),
2869 =
parsePointStops( base, stops, context, multiplier, type, json.value( u
"x1"_s ).toDouble(), json.value( u
"y1"_s ).toDouble(), json.value( u
"x2"_s ).toDouble(), json.value( u
"y2"_s ).toDouble() );
2872 if ( !stops.empty() && defaultPoint )
2873 *defaultPoint = QPointF( stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble() * multiplier, stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble() * multiplier );
2880 const QVariantList stops = json.
value( u
"stops"_s ).toList();
2881 if ( stops.empty() )
2884 const QString scaleExpression =
parseStringStops( stops, context, conversionMap, defaultString );
2893 QString caseString = u
"CASE "_s;
2895 for (
int i = 0; i < stops.length() - 1; ++i )
2898 const QVariant bz = stops.value( i ).toList().value( 0 );
2899 const QVariant bv = stops.value( i ).toList().value( 1 );
2900 if ( bv.userType() != QMetaType::Type::QVariantList && bv.userType() != QMetaType::Type::QStringList )
2902 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( bz.userType() ) ) ) );
2907 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2908 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2909 if ( tv.userType() != QMetaType::Type::QVariantList && tv.userType() != QMetaType::Type::QStringList )
2911 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( tz.userType() ) ) ) );
2915 caseString += QStringLiteral(
2916 "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2922 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 0 ), tv.toList().value( 0 ), base, multiplier, x1, y1, x2, y2, type, &context ),
2923 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 1 ), tv.toList().value( 1 ), base, multiplier, x1, y1, x2, y2, type, &context )
2926 caseString +=
"END"_L1;
2932 if ( stops.length() < 2 )
2935 QString caseString = u
"CASE"_s;
2937 for (
int i = 0; i < stops.length(); ++i )
2939 caseString +=
" WHEN "_L1;
2940 QStringList conditions;
2943 const QVariant bottomZoom = stops.value( i ).toList().value( 0 );
2944 conditions << u
"@vector_tile_zoom > %1"_s.arg( bottomZoom.toString() );
2946 if ( i < stops.length() - 1 )
2948 const QVariant topZoom = stops.value( i + 1 ).toList().value( 0 );
2949 conditions << u
"@vector_tile_zoom <= %1"_s.arg( topZoom.toString() );
2952 const QVariantList values = stops.value( i ).toList().value( 1 ).toList();
2953 QStringList valuesFixed;
2955 for (
const QVariant &value : values )
2957 const double number = value.toDouble( &ok );
2959 valuesFixed << QString::number( number * multiplier );
2963 caseString += u
"%1 THEN array(%3)"_s.arg( conditions.join(
" AND "_L1 ), valuesFixed.join(
',' ) );
2965 caseString +=
" END"_L1;
2973 QString caseString = u
"CASE "_s;
2975 for (
int i = 0; i < stops.length() - 1; ++i )
2978 const QVariant bz = stops.value( i ).toList().value( 0 );
2979 const QVariant bv = stops.value( i ).toList().value( 1 );
2980 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2982 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2987 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2988 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2989 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2991 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2995 const QString lowerComparator = i == 0 ? u
">="_s : u
">"_s;
2997 caseString += QStringLiteral(
2998 "WHEN @vector_tile_zoom %1 %2 AND @vector_tile_zoom <= %3 "
3001 .arg( lowerComparator, bz.toString(), tz.toString(),
interpolateExpression( bz.toDouble(), tz.toDouble(), bv, tv, base, multiplier, x1, y1, x2, y2, type, &context ) );
3004 const QVariant z = stops.last().toList().value( 0 );
3005 const QVariant v = stops.last().toList().value( 1 );
3006 QString vStr = v.toString();
3007 if ( ( QMetaType::Type ) v.userType() == QMetaType::QVariantList )
3010 caseString += QStringLiteral(
3011 "WHEN @vector_tile_zoom > %1 "
3012 "THEN ( ( %2 ) * %3 ) END"
3014 .arg( z.toString() )
3020 caseString += QStringLiteral(
3021 "WHEN @vector_tile_zoom > %1 "
3024 .arg( z.toString() )
3025 .arg( v.toDouble() * multiplier );
3033 QString caseString = u
"CASE "_s;
3035 for (
int i = 0; i < stops.length() - 1; ++i )
3038 const QVariant bz = stops.value( i ).toList().value( 0 );
3039 const QString bv = stops.value( i ).toList().value( 1 ).toString();
3040 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
3042 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
3047 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
3048 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
3050 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
3054 caseString += QStringLiteral(
3055 "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
3060 caseString += u
"ELSE %1 END"_s.arg(
QgsExpression::quotedValue( conversionMap.value( stops.constLast().toList().value( 1 ).toString(), stops.constLast().toList().value( 1 ) ) ) );
3061 if ( defaultString )
3062 *defaultString = stops.constLast().toList().value( 1 ).toString();
3068 QString caseString = u
"CASE "_s;
3070 bool isExpression =
false;
3071 for (
int i = 0; i < stops.length() - 1; ++i )
3074 const QVariant bz = stops.value( i ).toList().value( 0 );
3075 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
3077 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
3082 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
3083 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
3085 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
3089 QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
3090 if ( fieldPart.isEmpty() )
3091 fieldPart = u
"''"_s;
3092 else if ( !isExpression )
3095 caseString += QStringLiteral(
3096 "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom < %2 "
3099 .arg( bz.toString(), tz.toString(), fieldPart );
3103 const QVariant bz = stops.constLast().toList().value( 0 );
3104 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
3106 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
3110 QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
3111 if ( fieldPart.isEmpty() )
3112 fieldPart = u
"''"_s;
3113 else if ( !isExpression )
3116 caseString += QStringLiteral(
3117 "WHEN @vector_tile_zoom >= %1 "
3120 .arg( bz.toString(), fieldPart );
3123 QString defaultPart = processLabelField( stops.constFirst().toList().value( 1 ).toString(), isExpression );
3124 if ( defaultPart.isEmpty() )
3125 defaultPart = u
"''"_s;
3126 else if ( !isExpression )
3128 caseString += u
"ELSE %1 END"_s.arg( defaultPart );
3137 const QString method = json.
value( 0 ).toString();
3138 if ( method ==
"interpolate"_L1 )
3142 else if ( method ==
"match"_L1 )
3144 return parseMatchList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
3146 else if ( method ==
"step"_L1 )
3148 return parseStepList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
3160 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
3161 if ( attribute.isEmpty() )
3163 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
3167 QString caseString = u
"CASE "_s;
3169 for (
int i = 2; i < json.length() - 1; i += 2 )
3172 QVariant variantKeys = json.value( i );
3173 if ( variantKeys.userType() == QMetaType::Type::QVariantList || variantKeys.userType() == QMetaType::Type::QStringList )
3174 keys = variantKeys.toList();
3176 keys = { variantKeys };
3178 QStringList matchString;
3179 for (
const QVariant &key : keys )
3184 const QVariant value = json.value( i + 1 );
3186 QString valueString;
3191 if ( value.userType() == QMetaType::Type::QVariantList || value.userType() == QMetaType::Type::QStringList )
3197 const QColor color =
parseColor( value, context );
3205 const double v = value.toDouble() * multiplier;
3206 valueString = QString::number( v );
3212 const double v = value.toDouble() * maxOpacity;
3213 valueString = QString::number( v );
3219 valueString = u
"array(%1,%2)"_s.arg( value.toList().value( 0 ).toDouble() * multiplier, value.toList().value( 0 ).toDouble() * multiplier );
3225 if ( value.toList().count() == 2 && value.toList().first().toString() ==
"literal"_L1 )
3227 valueString = u
"array(%1)"_s.arg( value.toList().at( 1 ).toStringList().join(
',' ) );
3231 valueString = u
"array(%1)"_s.arg( value.toStringList().join(
',' ) );
3238 if ( value.toList().count() == 2 && value.toList().first().toString() ==
"literal"_L1 )
3240 QStringList dashValues = value.toList().at( 1 ).toStringList();
3241 if ( dashValues.length() % 2 == 1 )
3243 dashValues << u
"0"_s;
3245 valueString = u
"array(%1)"_s.arg( dashValues.join(
',' ) );
3249 QStringList dashValues = value.toStringList();
3250 if ( dashValues.length() % 2 == 1 )
3252 dashValues << u
"0"_s;
3254 valueString = u
"array(%1)"_s.arg( dashValues.join(
',' ) );
3260 if ( matchString.count() == 1 )
3262 caseString += u
"WHEN %1 IS %2 THEN %3 "_s.arg( attribute, matchString.at( 0 ), valueString );
3266 caseString += u
"WHEN %1 IN (%2) THEN %3 "_s.arg( attribute, matchString.join(
',' ), valueString );
3270 QVariant lastValue = json.constLast();
3273 switch ( lastValue.userType() )
3275 case QMetaType::Type::QVariantList:
3276 case QMetaType::Type::QStringList:
3277 elseValue =
parseValueList( lastValue.toList(), type, context, multiplier, maxOpacity, defaultColor, defaultNumber ).
asExpression();
3286 const QColor color =
parseColor( lastValue, context );
3288 *defaultColor = color;
3296 const double v = json.constLast().toDouble() * multiplier;
3297 if ( defaultNumber )
3299 elseValue = QString::number( v );
3305 const double v = json.constLast().toDouble() * maxOpacity;
3306 if ( defaultNumber )
3308 elseValue = QString::number( v );
3314 elseValue = u
"array(%1,%2)"_s.arg( json.constLast().toList().value( 0 ).toDouble() * multiplier ).arg( json.constLast().toList().value( 0 ).toDouble() * multiplier );
3320 if ( json.constLast().toList().count() == 2 && json.constLast().toList().first().toString() ==
"literal"_L1 )
3322 elseValue = u
"array(%1)"_s.arg( json.constLast().toList().at( 1 ).toStringList().join(
',' ) );
3326 elseValue = u
"array(%1)"_s.arg( json.constLast().toStringList().join(
',' ) );
3333 if ( json.constLast().toList().count() == 2 && json.constLast().toList().first().toString() ==
"literal"_L1 )
3335 QStringList dashValues = json.constLast().toList().at( 1 ).toStringList();
3336 if ( dashValues.length() % 2 == 1 )
3338 dashValues << u
"0"_s;
3340 elseValue = u
"array(%1)"_s.arg( dashValues.join(
',' ) );
3344 QStringList dashValues = json.constLast().toStringList();
3345 if ( dashValues.length() % 2 == 1 )
3347 dashValues << u
"0"_s;
3349 elseValue = u
"array(%1)"_s.arg( dashValues.join(
',' ) );
3358 caseString += u
"ELSE %1 END"_s.arg( elseValue );
3366 const QString expression =
parseExpression( json.value( 1 ).toList(), context );
3367 if ( expression.isEmpty() )
3369 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3373 QString caseString = u
"CASE "_s;
3376 for (
int i = json.length() - 2; i > 0; i -= 2 )
3378 const QVariant stepValue = json.value( i + 1 );
3380 QString valueString;
3391 const QColor color =
parseColor( stepValue, context );
3398 const double v = stepValue.toDouble() * multiplier;
3399 valueString = QString::number( v );
3405 const double v = stepValue.toDouble() * maxOpacity;
3406 valueString = QString::number( v );
3412 valueString = u
"array(%1,%2)"_s.arg( stepValue.toList().value( 0 ).toDouble() * multiplier ).arg( stepValue.toList().value( 0 ).toDouble() * multiplier );
3418 if ( stepValue.toList().count() == 2 && stepValue.toList().first().toString() ==
"literal"_L1 )
3420 valueString = u
"array(%1)"_s.arg( stepValue.toList().at( 1 ).toStringList().join(
',' ) );
3424 valueString = u
"array(%1)"_s.arg( stepValue.toStringList().join(
',' ) );
3431 if ( stepValue.toList().count() == 2 && stepValue.toList().first().toString() ==
"literal"_L1 )
3433 QStringList dashValues = stepValue.toList().at( 1 ).toStringList();
3434 if ( dashValues.length() % 2 == 1 )
3436 dashValues << u
"0"_s;
3438 valueString = u
"array(%1)"_s.arg( dashValues.join(
',' ) );
3442 QStringList dashValues = stepValue.toStringList();
3443 if ( dashValues.length() % 2 == 1 )
3445 dashValues << u
"0"_s;
3447 valueString = u
"array(%1)"_s.arg( dashValues.join(
',' ) );
3457 caseString += u
" WHEN %1 >= %2 THEN (%3) "_s.arg( expression, stepKey, valueString );
3461 caseString += u
"ELSE (%1) END"_s.arg( valueString );
3471 if ( json.value( 0 ).toString() !=
"interpolate"_L1 )
3473 context.
pushWarning( QObject::tr(
"%1: Could not interpret value list" ).arg( context.
layerId() ) );
3477 const QVariantList parts = json.
value( 1 ).toList();
3478 const QString technique = parts.value( 0 ).toString();
3482 if ( technique ==
"linear"_L1 )
3484 props.insert( u
"base"_s, 1 );
3487 else if ( technique ==
"exponential"_L1 )
3489 props.insert( u
"base"_s, parts.value( 1 ).toDouble() );
3492 else if ( technique ==
"cubic-bezier"_L1 )
3496 props.insert( u
"x1"_s, parts.value( 1 ).toDouble() );
3497 props.insert( u
"y1"_s, parts.value( 2 ).toDouble() );
3498 props.insert( u
"x2"_s, parts.value( 3 ).toDouble() );
3499 props.insert( u
"y2"_s, parts.value( 4 ).toDouble() );
3503 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation method %2" ).arg( context.
layerId(), technique ) );
3507 if ( json.value( 2 ).toList().value( 0 ).toString() !=
"zoom"_L1 )
3509 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation input %2" ).arg( context.
layerId(), json.value( 2 ).toString() ) );
3515 for (
int i = 3; i < json.length(); i += 2 )
3517 stops.push_back( QVariantList() << json.value( i ).toString() << json.value( i + 1 ) );
3520 props.insert( u
"stops"_s, stops );
3538 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported numeric array in interpolate" ).arg( context.
layerId() ) );
3546 if ( ( QMetaType::Type ) colorExpression.userType() == QMetaType::QVariantList )
3550 return parseValue( colorExpression, context,
true );
3555 if ( color.userType() != QMetaType::Type::QString )
3557 context.
pushWarning( QObject::tr(
"%1: Could not parse non-string color %2, skipping" ).arg( context.
layerId(), color.toString() ) );
3566 hue = std::max( 0, color.hslHue() );
3567 saturation = color.hslSaturation() / 255.0 * 100;
3568 lightness = color.lightness() / 255.0 * 100;
3569 alpha = color.alpha();
3573 double zoomMin,
double zoomMax, QVariant valueMin, QVariant valueMax,
double base,
double multiplier,
double x1,
double y1,
double x2,
double y2,
InterpolationType type,
QgsMapBoxGlStyleConversionContext *contextPtr
3579 context = *contextPtr;
3583 if ( valueMin.canConvert( QMetaType::Double ) && valueMax.canConvert( QMetaType::Double ) )
3585 bool minDoubleOk =
true;
3586 const double min = valueMin.toDouble( &minDoubleOk );
3587 bool maxDoubleOk =
true;
3588 const double max = valueMax.toDouble( &maxDoubleOk );
3589 if ( minDoubleOk && maxDoubleOk &&
qgsDoubleNear( min, max ) )
3591 return QString::number( min * multiplier );
3595 QString minValueExpr = valueMin.toString();
3596 QString maxValueExpr = valueMax.toString();
3597 if ( valueMin.userType() == QMetaType::Type::QVariantList )
3601 if ( valueMax.userType() == QMetaType::Type::QVariantList )
3607 if ( minValueExpr == maxValueExpr )
3609 expression = minValueExpr;
3616 expression = u
"scale_linear(@vector_tile_zoom,%1,%2,%3,%4)"_s.arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr );
3621 expression = u
"scale_linear(@vector_tile_zoom,%1,%2,%3,%4)"_s.arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr );
3625 expression = u
"scale_exponential(@vector_tile_zoom,%1,%2,%3,%4,%5)"_s.arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr ).arg( base );
3630 expression = u
"scale_cubic_bezier(@vector_tile_zoom,%1,%2,%3,%4,%5,%6,%7,%8)"_s.arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr ).arg( x1 ).arg( y1 ).arg( x2 ).arg( y2 );
3635 if ( multiplier != 1 )
3636 return u
"(%1) * %2"_s.arg( expression ).arg( multiplier );
3643 if ( style ==
"round"_L1 )
3644 return Qt::RoundCap;
3645 else if ( style ==
"square"_L1 )
3646 return Qt::SquareCap;
3653 if ( style ==
"bevel"_L1 )
3654 return Qt::BevelJoin;
3655 else if ( style ==
"round"_L1 )
3656 return Qt::RoundJoin;
3658 return Qt::MiterJoin;
3663 QString op = expression.value( 0 ).toString();
3664 if ( ( op ==
"%"_L1 || op ==
"/"_L1 || op ==
"-"_L1 || op ==
"^"_L1 ) && expression.size() >= 3 )
3666 if ( expression.size() != 3 )
3668 context.
pushWarning( QObject::tr(
"%1: Operator %2 requires exactly two operands, skipping extra operands" ).arg( context.
layerId() ).arg( op ) );
3670 QString v1 = parseValue( expression.value( 1 ), context, colorExpected );
3671 QString v2 = parseValue( expression.value( 2 ), context, colorExpected );
3672 return u
"(%1 %2 %3)"_s.arg( v1, op, v2 );
3674 else if ( ( op ==
"*"_L1 || op ==
"+"_L1 ) && expression.size() >= 3 )
3676 QStringList operands;
3677 std::transform( std::next( expression.begin() ), expression.end(), std::back_inserter( operands ), [&context, colorExpected](
const QVariant &val ) {
3678 return parseValue( val, context, colorExpected );
3680 return u
"(%1)"_s.arg( operands.join( u
" %1 "_s.arg( op ) ) );
3682 else if ( op ==
"to-number"_L1 )
3684 return u
"to_real(%1)"_s.arg( parseValue( expression.value( 1 ), context ) );
3686 else if ( op ==
"sqrt"_L1 )
3688 return u
"sqrt(%1)"_s.arg( parseValue( expression.value( 1 ), context ) );
3690 else if ( op ==
"literal"_L1 )
3692 return expression.value( 1 ).toString();
3694 else if ( op ==
"all"_L1 || op ==
"any"_L1 || op ==
"none"_L1 )
3697 for (
int i = 1; i < expression.size(); ++i )
3699 const QString part = parseValue( expression.at( i ), context );
3700 if ( part.isEmpty() )
3702 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3708 if ( op ==
"none"_L1 )
3709 return u
"NOT (%1)"_s.arg( parts.join(
") AND NOT ("_L1 ) );
3711 QString operatorString;
3712 if ( op ==
"all"_L1 )
3713 operatorString = u
") AND ("_s;
3714 else if ( op ==
"any"_L1 )
3715 operatorString = u
") OR ("_s;
3717 return u
"(%1)"_s.arg( parts.join( operatorString ) );
3719 else if ( op ==
'!' )
3722 QVariantList contraJsonExpr = expression.value( 1 ).toList();
3723 contraJsonExpr[0] = QString( op + contraJsonExpr[0].toString() );
3725 return parseKey( contraJsonExpr, context );
3727 else if ( op ==
"=="_L1 || op ==
"!="_L1 || op ==
">="_L1 || op ==
'>' || op ==
"<="_L1 || op ==
'<' )
3730 if ( op ==
"=="_L1 )
3732 else if ( op ==
"!="_L1 )
3734 return u
"%1 %2 %3"_s.arg( parseKey( expression.value( 1 ), context ), op, parseValue( expression.value( 2 ), context ) );
3736 else if ( op ==
"has"_L1 )
3738 return parseKey( expression.value( 1 ), context ) + u
" IS NOT NULL"_s;
3740 else if ( op ==
"!has"_L1 )
3742 return parseKey( expression.value( 1 ), context ) + u
" IS NULL"_s;
3744 else if ( op ==
"in"_L1 || op ==
"!in"_L1 )
3746 const QString key = parseKey( expression.value( 1 ), context );
3749 QVariantList values = expression.mid( 2 );
3750 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 )
3752 values = expression.at( 2 ).toList().at( 1 ).toList();
3755 for (
const QVariant &value : std::as_const( values ) )
3757 const QString part = parseValue( value, context );
3758 if ( part.isEmpty() )
3760 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3766 if ( parts.size() == 1 )
3768 if ( op ==
"in"_L1 )
3769 return u
"%1 IS %2"_s.arg( key, parts.at( 0 ) );
3771 return u
"(%1 IS NULL OR %1 IS NOT %2)"_s.arg( key, parts.at( 0 ) );
3775 if ( op ==
"in"_L1 )
3776 return u
"%1 IN (%2)"_s.arg( key, parts.join(
", "_L1 ) );
3778 return u
"(%1 IS NULL OR %1 NOT IN (%2))"_s.arg( key, parts.join(
", "_L1 ) );
3781 else if ( op ==
"get"_L1 )
3783 return parseKey( expression.value( 1 ), context );
3785 else if ( op ==
"match"_L1 )
3787 const QString attribute = expression.value( 1 ).toList().value( 1 ).toString();
3789 if ( expression.size() == 5
3790 && expression.at( 3 ).userType() == QMetaType::Type::Bool
3791 && expression.at( 3 ).toBool() ==
true
3792 && expression.at( 4 ).userType() == QMetaType::Type::Bool
3793 && expression.at( 4 ).toBool() ==
false )
3796 if ( expression.at( 2 ).userType() == QMetaType::Type::QVariantList || expression.at( 2 ).userType() == QMetaType::Type::QStringList )
3799 for (
const QVariant &p : expression.at( 2 ).toList() )
3801 parts << parseValue( p, context );
3804 if ( parts.size() > 1 )
3809 else if ( expression.at( 2 ).userType() == QMetaType::Type::QString
3810 || expression.at( 2 ).userType() == QMetaType::Type::Int
3811 || expression.at( 2 ).userType() == QMetaType::Type::Double
3812 || expression.at( 2 ).userType() == QMetaType::Type::LongLong )
3818 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3824 QString caseString = u
"CASE "_s;
3825 for (
int i = 2; i < expression.size() - 2; i += 2 )
3827 if ( expression.at( i ).userType() == QMetaType::Type::QVariantList || expression.at( i ).userType() == QMetaType::Type::QStringList )
3830 for (
const QVariant &p : expression.at( i ).toList() )
3835 if ( parts.size() > 1 )
3840 else if ( expression.at( i ).userType() == QMetaType::Type::QString
3841 || expression.at( i ).userType() == QMetaType::Type::Int
3842 || expression.at( i ).userType() == QMetaType::Type::Double
3843 || expression.at( i ).userType() == QMetaType::Type::LongLong )
3848 caseString += u
"THEN %1 "_s.arg( parseValue( expression.at( i + 1 ), context, colorExpected ) );
3850 caseString += u
"ELSE %1 END"_s.arg( parseValue( expression.last(), context, colorExpected ) );
3854 else if ( op ==
"to-string"_L1 )
3856 return u
"to_string(%1)"_s.arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3858 else if ( op ==
"to-boolean"_L1 )
3860 return u
"to_bool(%1)"_s.arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3862 else if ( op ==
"case"_L1 )
3864 QString caseString = u
"CASE"_s;
3865 for (
int i = 1; i < expression.size() - 2; i += 2 )
3867 const QString condition =
parseExpression( expression.value( i ).toList(), context );
3868 const QString value = parseValue( expression.value( i + 1 ), context, colorExpected );
3869 caseString += u
" WHEN (%1) THEN %2"_s.arg( condition, value );
3871 const QString value = parseValue( expression.constLast(), context, colorExpected );
3872 caseString += u
" ELSE %1 END"_s.arg( value );
3875 else if ( op ==
"zoom"_L1 && expression.count() == 1 )
3877 return u
"@vector_tile_zoom"_s;
3879 else if ( op ==
"coalesce"_L1 )
3881 QString coalesceString = u
"coalesce("_s;
3882 for (
int i = 1; i < expression.size(); i++ )
3885 coalesceString +=
", "_L1;
3886 coalesceString += parseValue( expression.value( i ), context );
3888 coalesceString +=
')'_L1;
3889 return coalesceString;
3891 else if ( op ==
"concat"_L1 )
3893 QString concatString = u
"concat("_s;
3894 for (
int i = 1; i < expression.size(); i++ )
3897 concatString +=
", "_L1;
3898 concatString += parseValue( expression.value( i ), context );
3900 concatString +=
')'_L1;
3901 return concatString;
3903 else if ( op ==
"length"_L1 )
3905 return u
"length(%1)"_s.arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3907 else if ( op ==
"step"_L1 )
3909 const QString stepExpression =
parseExpression( expression.value( 1 ).toList(), context );
3910 if ( stepExpression.isEmpty() )
3912 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3916 QString caseString = u
"CASE "_s;
3918 for (
int i = expression.length() - 2; i > 0; i -= 2 )
3920 const QString stepValue = parseValue( expression.value( i + 1 ), context, colorExpected );
3924 caseString += u
" WHEN %1 >= %2 THEN (%3) "_s.arg( stepExpression, stepKey, stepValue );
3928 caseString += u
"ELSE (%1) END"_s.arg( stepValue );
3933 else if ( op ==
"pitch"_L1 )
3937 else if ( op ==
"slice"_L1 )
3942 const QString inputExpression = parseValue( expression.value( 1 ), context );
3943 if ( inputExpression.isEmpty() )
3945 context.
pushWarning( QObject::tr(
"%1: Could not interpret slice list" ).arg( context.
layerId() ) );
3951 const auto constantInt = [](
const QVariant &value,
int &result ) ->
bool {
3952 switch ( value.userType() )
3954 case QMetaType::Int:
3955 case QMetaType::UInt:
3956 case QMetaType::LongLong:
3957 case QMetaType::ULongLong:
3960 result = value.toInt( &ok );
3969 const bool startIsConstant = constantInt( expression.value( 2 ), startValue );
3970 const QString startExpression = parseValue( expression.value( 2 ), context );
3971 const QString startOffset = startIsConstant ? QString::number( startValue + 1 ) : u
"(%1) + 1"_s.arg( startExpression );
3973 if ( expression.size() > 3 )
3976 const bool endIsConstant = constantInt( expression.value( 3 ), endValue );
3977 const QString endExpression = parseValue( expression.value( 3 ), context );
3978 const QString length = ( startIsConstant && endIsConstant ) ? QString::number( endValue - startValue ) : u
"(%1) - (%2)"_s.arg( endExpression, startExpression );
3979 return u
"substr(%1, %2, %3)"_s.arg( inputExpression, startOffset, length );
3983 return u
"substr(%1, %2)"_s.arg( inputExpression, startOffset );
3988 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression \"%2\"" ).arg( context.
layerId(), op ) );
3997 if ( name.isEmpty() )
4003 QString actualName = name;
4004 const int categorySeparator = name.indexOf(
':' );
4005 if ( categorySeparator > 0 )
4007 category = name.left( categorySeparator );
4010 actualName = name.mid( categorySeparator + 1 );
4019 if ( category.isEmpty() )
4024 if ( spriteImage.isNull() )
4026 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
4030 const QVariantMap spriteDefinition = context.
spriteDefinitions( category ).value( actualName ).toMap();
4031 if ( spriteDefinition.size() == 0 )
4033 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
4038 = 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() );
4039 if ( sprite.isNull() )
4041 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
4045 spriteSize = sprite.size() / spriteDefinition.value( u
"pixelRatio"_s ).toDouble() * context.
pixelSizeConversionFactor();
4055 auto prepareBase64 = [](
const QImage &sprite ) {
4057 if ( !sprite.isNull() )
4060 QBuffer buffer( &blob );
4061 buffer.open( QIODevice::WriteOnly );
4062 sprite.save( &buffer,
"PNG" );
4064 const QByteArray encoded = blob.toBase64();
4065 path = QString( encoded );
4066 path.prepend(
"base64:"_L1 );
4071 switch ( value.userType() )
4073 case QMetaType::Type::QString:
4075 QString spriteName = value.toString();
4076 const thread_local QRegularExpression fieldNameMatch( u
"{([^}]+)}"_s );
4077 QRegularExpressionMatch match = fieldNameMatch.match( spriteName );
4078 if ( match.hasMatch() )
4080 const QString fieldName = match.captured( 1 );
4081 spriteProperty = u
"CASE"_s;
4082 spriteSizeProperty = u
"CASE"_s;
4084 spriteName.replace(
"(",
"\\("_L1 );
4085 spriteName.replace(
")",
"\\)"_L1 );
4086 spriteName.replace( fieldNameMatch, u
"([^\\/\\\\]+)"_s );
4087 const QRegularExpression fieldValueMatch( spriteName );
4089 for (
const QString &name : spriteNames )
4091 match = fieldValueMatch.match( name );
4092 if ( match.hasMatch() )
4096 const QString fieldValue = match.captured( 1 );
4098 path = prepareBase64( sprite );
4099 if ( spritePath.isEmpty() && !path.isEmpty() )
4105 spriteProperty += u
" WHEN \"%1\" = '%2' THEN '%3'"_s.arg( fieldName, fieldValue, path );
4106 spriteSizeProperty += u
" WHEN \"%1\" = '%2' THEN %3"_s.arg( fieldName ).arg( fieldValue ).arg( size.width() );
4110 spriteProperty +=
" END"_L1;
4111 spriteSizeProperty +=
" END"_L1;
4115 spriteProperty.clear();
4116 spriteSizeProperty.clear();
4117 const QImage sprite =
retrieveSprite( spriteName, context, spriteSize );
4118 spritePath = prepareBase64( sprite );
4123 case QMetaType::Type::QVariantMap:
4125 const QVariantList stops = value.toMap().value( u
"stops"_s ).toList();
4126 if ( stops.size() == 0 )
4133 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, spriteSize );
4134 spritePath = prepareBase64( sprite );
4136 spriteProperty = u
"CASE WHEN @vector_tile_zoom < %1 THEN '%2'"_s.arg( stops.value( 0 ).toList().value( 0 ).toString() ).arg( spritePath );
4137 spriteSizeProperty = u
"CASE WHEN @vector_tile_zoom < %1 THEN %2"_s.arg( stops.value( 0 ).toList().value( 0 ).toString() ).arg( spriteSize.width() );
4139 for (
int i = 0; i < stops.size() - 1; ++i )
4142 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, size );
4143 path = prepareBase64( sprite );
4145 spriteProperty += QStringLiteral(
4146 " WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
4149 .arg( stops.value( i ).toList().value( 0 ).toString(), stops.value( i + 1 ).toList().value( 0 ).toString(), path );
4150 spriteSizeProperty += QStringLiteral(
4151 " WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
4154 .arg( stops.value( i ).toList().value( 0 ).toString(), stops.value( i + 1 ).toList().value( 0 ).toString() )
4155 .arg( size.width() );
4157 sprite =
retrieveSprite( stops.last().toList().value( 1 ).toString(), context, size );
4158 path = prepareBase64( sprite );
4160 spriteProperty += QStringLiteral(
4161 " WHEN @vector_tile_zoom >= %1 "
4164 .arg( stops.last().toList().value( 0 ).toString() )
4166 spriteSizeProperty += QStringLiteral(
4167 " WHEN @vector_tile_zoom >= %1 "
4170 .arg( stops.last().toList().value( 0 ).toString() )
4171 .arg( size.width() );
4175 case QMetaType::Type::QVariantList:
4177 const QVariantList json = value.toList();
4178 const QString method = json.value( 0 ).toString();
4180 if ( method ==
"match"_L1 )
4182 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
4183 if ( attribute.isEmpty() )
4185 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
4189 spriteProperty = u
"CASE"_s;
4190 spriteSizeProperty = u
"CASE"_s;
4192 for (
int i = 2; i < json.length() - 1; i += 2 )
4194 const QVariant matchKey = json.value( i );
4195 const QVariant matchValue = json.value( i + 1 );
4196 QString matchString;
4197 switch ( matchKey.userType() )
4199 case QMetaType::Type::QVariantList:
4200 case QMetaType::Type::QStringList:
4202 const QVariantList keys = matchKey.toList();
4203 QStringList matchStringList;
4204 for (
const QVariant &key : keys )
4208 matchString = matchStringList.join(
',' );
4212 case QMetaType::Type::Bool:
4213 case QMetaType::Type::QString:
4214 case QMetaType::Type::Int:
4215 case QMetaType::Type::LongLong:
4216 case QMetaType::Type::Double:
4223 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported sprite type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( value.userType() ) ) ) );
4227 const QImage sprite =
retrieveSprite( matchValue.toString(), context, spriteSize );
4228 spritePath = prepareBase64( sprite );
4230 spriteProperty += QStringLiteral(
4234 .arg( attribute, matchString, spritePath );
4236 spriteSizeProperty += QStringLiteral(
4240 .arg( attribute, matchString )
4241 .arg( spriteSize.width() );
4244 if ( !json.constLast().toString().isEmpty() )
4246 const QImage sprite =
retrieveSprite( json.constLast().toString(), context, spriteSize );
4247 spritePath = prepareBase64( sprite );
4251 spritePath = QString();
4254 spriteProperty += u
" ELSE '%1' END"_s.arg( spritePath );
4255 spriteSizeProperty += u
" ELSE %3 END"_s.arg( spriteSize.width() );
4258 else if ( method ==
"step"_L1 )
4260 const QString expression =
parseExpression( json.value( 1 ).toList(), context );
4261 if ( expression.isEmpty() )
4263 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
4267 spriteProperty = u
"CASE"_s;
4268 spriteSizeProperty = u
"CASE"_s;
4269 for (
int i = json.length() - 2; i > 2; i -= 2 )
4272 const QString stepValue = json.value( i + 1 ).toString();
4274 const QImage sprite =
retrieveSprite( stepValue, context, spriteSize );
4275 spritePath = prepareBase64( sprite );
4277 spriteProperty += u
" WHEN %1 >= %2 THEN '%3' "_s.arg( expression, stepKey, spritePath );
4278 spriteSizeProperty += u
" WHEN %1 >= %2 THEN %3 "_s.arg( expression ).arg( stepKey ).arg( spriteSize.width() );
4281 const QImage sprite =
retrieveSprite( json.at( 2 ).toString(), context, spriteSize );
4282 spritePath = prepareBase64( sprite );
4284 spriteProperty += u
"ELSE '%1' END"_s.arg( spritePath );
4285 spriteSizeProperty += u
"ELSE %3 END"_s.arg( spriteSize.width() );
4288 else if ( method ==
"case"_L1 )
4290 spriteProperty = u
"CASE"_s;
4291 spriteSizeProperty = u
"CASE"_s;
4292 for (
int i = 1; i < json.length() - 2; i += 2 )
4294 const QString caseExpression =
parseExpression( json.value( i ).toList(), context );
4295 const QString caseValue = json.value( i + 1 ).toString();
4297 const QImage sprite =
retrieveSprite( caseValue, context, spriteSize );
4298 spritePath = prepareBase64( sprite );
4300 spriteProperty += u
" WHEN %1 THEN '%2' "_s.arg( caseExpression, spritePath );
4301 spriteSizeProperty += u
" WHEN %1 THEN %2 "_s.arg( caseExpression ).arg( spriteSize.width() );
4303 const QImage sprite =
retrieveSprite( json.last().toString(), context, spriteSize );
4304 spritePath = prepareBase64( sprite );
4306 spriteProperty += u
"ELSE '%1' END"_s.arg( spritePath );
4307 spriteSizeProperty += u
"ELSE %3 END"_s.arg( spriteSize.width() );
4312 context.
pushWarning( QObject::tr(
"%1: Could not interpret sprite value list with method %2" ).arg( context.
layerId(), method ) );
4318 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported sprite type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( value.userType() ) ) ) );
4328 switch ( value.userType() )
4330 case QMetaType::Type::QVariantList:
4331 case QMetaType::Type::QStringList:
4334 case QMetaType::Type::Bool:
4335 case QMetaType::Type::QString:
4336 if ( colorExpected )
4341 return parseValue(
c, context );
4346 case QMetaType::Type::Int:
4347 case QMetaType::Type::LongLong:
4348 case QMetaType::Type::Double:
4349 return value.toString();
4351 case QMetaType::Type::QColor:
4352 c = value.value<QColor>();
4353 return QString(
"color_rgba(%1,%2,%3,%4)" ).arg(
c.red() ).arg(
c.green() ).arg(
c.blue() ).arg(
c.alpha() );
4356 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression part" ).arg( context.
layerId() ) );
4364 if ( value.toString() ==
"$type"_L1 )
4366 return u
"_geom_type"_s;
4368 if ( value.toString() ==
"level"_L1 )
4372 else if ( ( value.userType() == QMetaType::Type::QVariantList && value.toList().size() == 1 ) || value.userType() == QMetaType::Type::QStringList )
4374 if ( value.toList().size() > 1 )
4375 return value.toList().at( 1 ).toString();
4378 QString valueString = value.toList().value( 0 ).toString();
4379 if ( valueString ==
"geometry-type"_L1 )
4381 return u
"_geom_type"_s;
4386 else if ( value.userType() == QMetaType::Type::QVariantList && value.toList().size() > 1 )
4393QString QgsMapBoxGlStyleConverter::processLabelField(
const QString &
string,
bool &isExpression )
4397 const thread_local QRegularExpression singleFieldRx( u
"^{([^}]+)}$"_s );
4398 const QRegularExpressionMatch match = singleFieldRx.match(
string );
4399 if ( match.hasMatch() )
4401 isExpression =
false;
4402 return match.captured( 1 );
4405 const thread_local QRegularExpression multiFieldRx( u
"(?={[^}]+})"_s );
4406 const QStringList parts =
string.split( multiFieldRx );
4407 if ( parts.size() > 1 )
4409 isExpression =
true;
4412 for (
const QString &part : parts )
4414 if ( part.isEmpty() )
4417 if ( !part.contains(
'{' ) )
4424 const QStringList split = part.split(
'}' );
4426 if ( !split.at( 1 ).isEmpty() )
4429 return u
"concat(%1)"_s.arg( res.join(
',' ) );
4433 isExpression =
false;
4440 return mRenderer ? mRenderer->
clone() :
nullptr;
4445 return mLabeling ? mLabeling->
clone() :
nullptr;
4455 return mRasterSubLayers;
4460 QList<QgsMapLayer *> subLayers;
4463 const QString sourceName = subLayer.source();
4464 std::unique_ptr< QgsRasterLayer > rl;
4471 rl->pipe()->setDataDefinedProperties( subLayer.dataDefinedProperties() );
4478 subLayers.append( rl.release() );
4487 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
4490 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
4491 context = tmpContext.get();
4495 if (
string.compare(
"vector"_L1, Qt::CaseInsensitive ) == 0 )
4497 else if (
string.compare(
"raster"_L1, Qt::CaseInsensitive ) == 0 )
4499 else if (
string.compare(
"raster-dem"_L1, Qt::CaseInsensitive ) == 0 )
4501 else if (
string.compare(
"geojson"_L1, Qt::CaseInsensitive ) == 0 )
4503 else if (
string.compare(
"image"_L1, Qt::CaseInsensitive ) == 0 )
4505 else if (
string.compare(
"video"_L1, Qt::CaseInsensitive ) == 0 )
4507 context->
pushWarning( QObject::tr(
"Invalid source type \"%1\" for source \"%2\"" ).arg(
string, name ) );
4513 const QString name = it.key();
4514 const QVariantMap jsonSource = it.value().toMap();
4515 const QString typeString = jsonSource.value( u
"type"_s ).toString();
4538 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
4541 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
4542 context = tmpContext.get();
4545 auto raster = std::make_unique< QgsMapBoxGlStyleRasterSource >( name );
4546 if ( raster->setFromJson( source, context ) )
4547 mSources.append( raster.release() );
4550bool QgsMapBoxGlStyleConverter::numericArgumentsOnly(
const QVariant &bottomVariant,
const QVariant &topVariant,
double &bottom,
double &top )
4552 if ( bottomVariant.canConvert( QMetaType::Double ) && topVariant.canConvert( QMetaType::Double ) )
4554 bool bDoubleOk, tDoubleOk;
4555 bottom = bottomVariant.toDouble( &bDoubleOk );
4556 top = topVariant.toDouble( &tDoubleOk );
4557 return ( bDoubleOk && tDoubleOk );
4568 mWarnings << warning;
4583 return mSizeConversionFactor;
4588 mSizeConversionFactor = sizeConversionFactor;
4593 return mSpriteImage.keys();
4598 return mSpriteImage.contains( category ) ? mSpriteImage[category] : QImage();
4603 return mSpriteDefinitions.contains( category ) ? mSpriteDefinitions[category] : QVariantMap();
4608 mSpriteImage[category] = image;
4609 mSpriteDefinitions[category] = definitions;
4656 mAttribution = json.value( u
"attribution"_s ).toString();
4658 const QString scheme = json.value( u
"scheme"_s, u
"xyz"_s ).toString();
4659 if ( scheme.compare(
"xyz"_L1 ) == 0 )
4665 context->
pushWarning( QObject::tr(
"%1 scheme is not supported for raster source %2" ).arg( scheme,
name() ) );
4669 mMinZoom = json.value( u
"minzoom"_s, u
"0"_s ).toInt();
4670 mMaxZoom = json.value( u
"maxzoom"_s, u
"22"_s ).toInt();
4671 mTileSize = json.value( u
"tileSize"_s, u
"512"_s ).toInt();
4673 const QVariantList
tiles = json.value( u
"tiles"_s ).toList();
4674 for (
const QVariant &tile :
tiles )
4676 mTiles.append( tile.toString() );
4685 parts.insert( u
"type"_s, u
"xyz"_s );
4686 parts.insert( u
"url"_s, mTiles.value( 0 ) );
4688 if ( mTileSize == 256 )
4689 parts.insert( u
"tilePixelRation"_s, u
"1"_s );
4690 else if ( mTileSize == 512 )
4691 parts.insert( u
"tilePixelRation"_s, u
"2"_s );
4693 parts.insert( u
"zmax"_s, QString::number( mMaxZoom ) );
4694 parts.insert( u
"zmin"_s, QString::number( mMinZoom ) );
4697 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.
Contains settings related to how the label engine removes candidate label positions and reduces the n...
void setAllowDuplicateRemoval(bool allow)
Sets whether duplicate label removal is permitted for this layer.
void setMinimumDistanceToDuplicateUnit(Qgis::RenderUnit unit)
Sets the unit for the minimum distance to labels with duplicate text.
virtual void setWidth(double width)
Sets the width of the line symbol layer.
void setOffset(double offset)
Sets the line's offset.
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the unit for the line's offset.
Abstract base class for MapBox GL style sources.
QString name() const
Returns the source's name.
virtual ~QgsMapBoxGlStyleAbstractSource()
QgsMapBoxGlStyleAbstractSource(const QString &name)
Constructor for QgsMapBoxGlStyleAbstractSource.
Context for a MapBox GL style conversion operation.
void setLayerId(const QString &value)
Sets the layer ID of the layer currently being converted.
QStringList warnings() const
Returns a list of warning messages generated during the conversion.
void pushWarning(const QString &warning)
Pushes a warning message generated during the conversion.
QImage spriteImage(const QString &category=QString()) const
Returns the sprite image for a given category to use during conversion, or an invalid image if this i...
double pixelSizeConversionFactor() const
Returns the pixel size conversion factor, used to scale the original pixel sizes when converting styl...
void setTargetUnit(Qgis::RenderUnit targetUnit)
Sets the target unit type.
void setSprites(const QImage &image, const QVariantMap &definitions, const QString &category=QString())
Sets the sprite image and definitions JSON for a given category to use during conversion.
void setPixelSizeConversionFactor(double sizeConversionFactor)
Sets the pixel size conversion factor, used to scale the original pixel sizes when converting styles.
QVariantMap spriteDefinitions(const QString &category=QString()) const
Returns the sprite definitions for a given category to use during conversion.
Qgis::RenderUnit targetUnit() const
Returns the target unit type.
QString layerId() const
Returns the layer ID of the layer currently being converted.
QStringList spriteCategories() const
Returns the list of sprite categories to use during conversion, or an empty list of none is set.
void clearWarnings()
Clears the list of warning messages.
static QgsProperty parseInterpolateByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, double *defaultNumber=nullptr, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential)
Parses a numeric value which is interpolated by zoom range.
static QString parseColorExpression(const QVariant &colorExpression, QgsMapBoxGlStyleConversionContext &context)
Converts an expression representing a color to a string (can be color string or an expression where a...
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.
InterpolationType
Interpolation types, for interpolated value conversion.
@ Linear
Linear interpolation.
@ Exponential
Exponential interpolation.
@ CubicBezier
Cubic-bezier interpolation.
static QString parsePointStops(double base, const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential, double x1=0, double y1=0, double x2=0, double y2=0)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate point/...
PropertyType
Property types, for interpolated value conversion.
@ Point
Point/offset property.
@ DashArray
Dash array. Like numeric array, but must be even length array. Odd length arrays are considered as ha...
@ Numeric
Numeric property (e.g. line width, text size).
@ Opacity
Opacity property.
@ NumericArray
Numeric array for dash arrays or such.
QList< QgsMapBoxGlStyleAbstractSource * > sources()
Returns the list of converted sources.
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 Qt::PenJoinStyle parseJoinStyle(const QString &style)
Converts a value to Qt::PenJoinStyle enum from JSON value.
static QString interpolateExpression(double zoomMin, double zoomMax, QVariant valueMin, QVariant valueMax, double base, double multiplier=1, double x1=0, double y1=0, double x2=1, double y2=1, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential, QgsMapBoxGlStyleConversionContext *contextPtr=nullptr)
Generates an interpolation for values between valueMin and valueMax, scaled between the ranges zoomMi...
static QgsProperty parseInterpolateStringByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, const QVariantMap &conversionMap, QString *defaultString=nullptr)
Interpolates a string by zoom.
static QgsProperty parseStepList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a match function value list.
static bool parseCircleLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context)
Parses a circle layer.
Result convert(const QVariantMap &style, QgsMapBoxGlStyleConversionContext *context=nullptr)
Converts a JSON style map, and returns the resultant status of the conversion.
static QgsProperty parseInterpolateListByZoom(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Interpolates a list which starts with the interpolate function.
~QgsMapBoxGlStyleConverter()
QList< QgsMapLayer * > createSubLayers() const
Returns a list of new map layers corresponding to sublayers of the style, e.g.
Result
Result of conversion.
@ Success
Conversion was successful.
@ NoLayerList
No layer list was found in JSON input.
QgsMapBoxGlStyleConverter()
Constructor for QgsMapBoxGlStyleConverter.
static QgsProperty parseInterpolateColorByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, QColor *defaultColor=nullptr, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential)
Parses a color value which is interpolated by zoom range.
static QImage retrieveSprite(const QString &name, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize)
Retrieves the sprite image with the specified name, taken from the specified context.
static QString parseLabelStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context)
Parses a list of interpolation stops containing label values.
void parseLayers(const QVariantList &layers, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse list of layers from JSON.
static QgsProperty parseInterpolateOpacityByZoom(const QVariantMap &json, int maxOpacity, QgsMapBoxGlStyleConversionContext *contextPtr=nullptr, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential)
Interpolates opacity with either scale_linear() or scale_exp() (depending on base value).
static QString retrieveSpriteAsBase64WithProperties(const QVariant &value, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize, QString &spriteProperty, QString &spriteSizeProperty)
Retrieves the sprite image with the specified name, taken from the specified context as a base64 enco...
void parseSources(const QVariantMap &sources, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse list of sources from JSON.
static QColor parseColor(const QVariant &color, QgsMapBoxGlStyleConversionContext &context)
Parses a color in one of these supported formats:
static bool parseSymbolLayerAsRenderer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as a renderer.
static bool parseFillLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context, bool isBackgroundStyle=false)
Parses a fill layer.
static void parseSymbolLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, bool &hasRenderer, QgsVectorTileBasicLabelingStyle &labelingStyle, bool &hasLabeling, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as renderer or labeling.
static QString parseOpacityStops(double base, const QVariantList &stops, int maxOpacity, QgsMapBoxGlStyleConversionContext &context, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential, double x1=0, double y1=0, double x2=1, double y2=1)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate alpha ...
static bool parseLineLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context)
Parses a line layer.
void parseRasterSource(const QVariantMap &source, const QString &name, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse a raster source from JSON.
static void colorAsHslaComponents(const QColor &color, int &hue, int &saturation, int &lightness, int &alpha)
Takes a QColor object and returns HSLA components in required format for QGIS color_hsla() expression...
static Qt::PenCapStyle parseCapStyle(const QString &style)
Converts a value to Qt::PenCapStyle enum from JSON value.
static QString parseStringStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, const QVariantMap &conversionMap, QString *defaultString=nullptr)
Parses a list of interpolation stops containing string values.
static QgsProperty parseMatchList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a match function value list.
static QString parseStops(double base, const QVariantList &stops, double multiplier, QgsMapBoxGlStyleConversionContext &context, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential, double x1=0, double y1=0, double x2=1, double y2=1)
Parses a list of interpolation stops.
static QgsProperty parseValueList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a value list (e.g.
static QgsProperty parseInterpolatePointByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, QPointF *defaultPoint=nullptr, QgsMapBoxGlStyleConverter::InterpolationType type=QgsMapBoxGlStyleConverter::InterpolationType::Exponential)
Interpolates a point/offset with either scale_linear() or scale_exp() (depending on base value).
static QString parseArrayStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier=1)
Takes numerical arrays from stops.
Encapsulates a MapBox GL style raster source.
Qgis::MapBoxGlStyleSourceType type() const override
Returns the source type.
QgsMapBoxGlStyleRasterSource(const QString &name)
Constructor for QgsMapBoxGlStyleRasterSource.
QgsRasterLayer * toRasterLayer() const
Returns a new raster layer representing the raster source, or nullptr if the source cannot be represe...
bool setFromJson(const QVariantMap &json, QgsMapBoxGlStyleConversionContext *context) override
Sets the source's state from a json map.
QStringList tiles() const
Returns the list of tile sources.
Encapsulates a MapBox GL style raster sub layer.
QString source() const
Returns the layer's source.
QString id() const
Returns the layer's ID.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the layer's data defined properties.
QgsMapBoxGlStyleRasterSubLayer(const QString &id, const QString &source)
Constructor for QgsMapBoxGlStyleRasterSubLayer, with the given id and source.
Line symbol layer type which draws repeating marker symbols along a line feature.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
virtual void setSize(double size)
Sets the symbol size.
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's offset.
void setAngle(double angle)
Sets the rotation angle for the marker.
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's size.
A marker symbol type, for rendering Point and MultiPoint geometries.
void setEnabled(bool enabled)
Sets whether the effect is enabled.
Contains settings for how a map layer will be labeled.
double yOffset
Vertical offset of label.
const QgsLabelObstacleSettings & obstacleSettings() const
Returns the label obstacle settings.
const QgsLabelPlacementSettings & placementSettings() const
Returns the label placement settings.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
double xOffset
Horizontal offset of label.
Qgis::LabelPlacement placement
Label placement mode.
Qgis::LabelMultiLineAlignment multilineAlign
Horizontal alignment of multi-line labels.
int priority
Label priority.
double angleOffset
Label rotation, in degrees clockwise.
const QgsLabelThinningSettings & thinningSettings() const
Returns the label thinning settings.
void setThinningSettings(const QgsLabelThinningSettings &settings)
Sets the label thinning settings.
Qgis::RenderUnit offsetUnits
Units for offsets of label.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the label's property collection, used for data defined overrides.
bool isExpression
true if this label is made from a expression string, e.g., FieldName || 'mm'
void setPlacementSettings(const QgsLabelPlacementSettings &settings)
Sets the label placement settings.
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
double dist
Distance from feature to the label.
Qgis::RenderUnit distUnits
Units the distance from feature to the label.
@ LinePlacementOptions
Line placement flags.
@ LabelRotation
Label rotation.
@ FontStyle
Font style name.
@ RemoveDuplicateLabelDistance
Minimum distance from labels for this feature to other labels with duplicate text.
@ FontLetterSpacing
Letter spacing.
QString fieldName
Name of field (or an expression) to use for label text.
int autoWrapLength
If non-zero, indicates that label text should be automatically wrapped to (ideally) the specified num...
const QgsLabelPointSettings & pointSettings() const
Returns the label point settings, which contain settings related to how the label engine places and f...
A grouped map of multiple QgsProperty objects, each referenced by an integer key value.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const final
Returns the calculated value of the property with the specified key from within the collection.
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
QgsProperty property(int key) const final
Returns a matching property from the collection, if one exists.
A store for object properties.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
QString expressionString() const
Returns the expression used for the property value.
QVariant value(const QgsExpressionContext &context, const QVariant &defaultValue=QVariant(), bool *ok=nullptr) const
Calculates the current value of the property, including any transforms which are set for the property...
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
void setExpressionString(const QString &expression)
Sets the expression to use for the property value.
static QgsProperty fromValue(const QVariant &value, bool isActive=true)
Returns a new StaticProperty created from the specified value.
void setActive(bool active)
Sets whether the property is currently active.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
A fill symbol layer which fills polygons with a repeated raster image.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the unit for the image's width and height.
void setOpacity(double opacity)
Sets the opacity for the raster image used in the fill.
void setImageFilePath(const QString &imagePath)
Sets the path to the raster image used for the fill.
void setWidth(double width)
Sets the width for scaling the image used in the fill.
void setCoordinateMode(Qgis::SymbolCoordinateReference mode)
Set the coordinate mode for fill.
Represents a raster layer.
Line symbol layer type which draws line sections using a raster image file.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Raster marker symbol layer class.
void setOpacity(double opacity)
Set the marker opacity.
void setPath(const QString &path)
Set the marker raster image path.
@ RendererOpacity
Raster renderer global opacity.
Renders polygons using a single fill and stroke color.
void setBrushStyle(Qt::BrushStyle style)
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setStrokeWidth(double strokeWidth)
void setStrokeStyle(Qt::PenStyle strokeStyle)
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the unit for the fill's offset.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
void setOffset(QPointF offset)
Sets an offset by which polygons will be translated during rendering.
void setStrokeColor(const QColor &strokeColor) override
Sets the stroke color for the symbol layer.
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
void setPenCapStyle(Qt::PenCapStyle style)
Sets the pen cap style used to render the line (e.g.
void setUseCustomDashPattern(bool b)
Sets whether the line uses a custom dash pattern.
void setCustomDashVector(const QVector< qreal > &vector)
Sets the custom dash vector, which is the pattern of alternating drawn/skipped lengths used while ren...
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the pen join style used to render the line (e.g.
Simple marker symbol layer, consisting of a rendered shape with solid fill color and a stroke.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
void setStrokeWidthUnit(Qgis::RenderUnit u)
Sets the unit for the width of the marker's stroke.
void setStrokeWidth(double w)
Sets the width of the marker's stroke.
void setStrokeColor(const QColor &color) override
Sets the marker's stroke color.
static QColor parseColor(const QString &colorStr, bool strictEval=false)
Attempts to parse a string as a color using a variety of common formats, including hex codes,...
@ File
Filename, eg for svg files.
@ CustomDash
Custom dash pattern.
@ Name
Name, eg shape name for simple markers.
@ StrokeColor
Stroke color.
@ Interval
Line marker interval.
@ StrokeWidth
Stroke width.
@ LayerEnabled
Whether symbol layer is enabled.
virtual void setColor(const QColor &color)
Sets the "representative" color for the symbol layer.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the symbol layer's property collection, used for data defined overrides.
void setPlacements(Qgis::MarkerLinePlacements placements)
Sets the placement of the symbols.
Container for settings relating to a text background object.
void setMarkerSymbol(QgsMarkerSymbol *symbol)
Sets the current marker symbol for the background shape.
void setSizeType(SizeType type)
Sets the method used to determine the size of the background shape (e.g., fixed size or buffer around...
@ ShapeMarkerSymbol
Marker symbol.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units used for the shape's size.
void setType(ShapeType type)
Sets the type of background shape to draw (e.g., square, ellipse, SVG).
void setEnabled(bool enabled)
Sets whether the text background will be drawn.
void setSize(QSizeF size)
Sets the size of the background shape.
void setColor(const QColor &color)
Sets the color for the buffer.
void setOpacity(double opacity)
Sets the buffer opacity.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units used for the buffer size.
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the buffer.
void setSize(double size)
Sets the size of the buffer.
Container for all settings relating to text rendering.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void setSize(double size)
Sets the size for rendered text.
void setFont(const QFont &font)
Sets the font used for rendering text.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the size of rendered text.
void setBackground(const QgsTextBackgroundSettings &backgroundSettings)
Sets the text's background settings.q.
void setNamedStyle(const QString &style)
Sets the named style for the font used for rendering text.
QFont font() const
Returns the font used for rendering text.
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
Configuration of a single style within QgsVectorTileBasicLabeling.
void setLayerName(const QString &name)
Sets name of the sub-layer to render (empty layer means that all layers match).
void setMinZoomLevel(int minZoom)
Sets minimum zoom level index (negative number means no limit).
void setFilterExpression(const QString &expr)
Sets filter expression (empty filter means that all features match).
void setMaxZoomLevel(int maxZoom)
Sets maximum zoom level index (negative number means no limit).
void setStyleName(const QString &name)
Sets human readable name of this style.
void setGeometryType(Qgis::GeometryType geomType)
Sets type of the geometry that will be used (point / line / polygon).
void setLabelSettings(const QgsPalLayerSettings &settings)
Sets labeling configuration of this style.
void setEnabled(bool enabled)
Sets whether this style is enabled (used for rendering).
Definition of map rendering of a subset of vector tile data.
void setEnabled(bool enabled)
Sets whether this style is enabled (used for rendering).
void setMinZoomLevel(int minZoom)
Sets minimum zoom level index (negative number means no limit).
void setLayerName(const QString &name)
Sets name of the sub-layer to render (empty layer means that all layers match).
void setFilterExpression(const QString &expr)
Sets filter expression (empty filter means that all features match).
void setSymbol(QgsSymbol *sym)
Sets symbol for rendering. Takes ownership of the symbol.
void setStyleName(const QString &name)
Sets human readable name of this style.
void setMaxZoomLevel(int maxZoom)
Sets maximum zoom level index (negative number means no limit).
void setGeometryType(Qgis::GeometryType geomType)
Sets type of the geometry that will be used (point / line / polygon).
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