48#include <QRegularExpression>
51#include "moc_qgsmapboxglstyleconverter.cpp"
53using namespace Qt::StringLiterals;
64 if ( style.contains( u
"sources"_s ) )
66 parseSources( style.value( u
"sources"_s ).toMap(), context );
69 if ( style.contains( u
"layers"_s ) )
71 parseLayers( style.value( u
"layers"_s ).toList(), context );
75 mError = QObject::tr(
"Could not find layers list in JSON" );
88 qDeleteAll( mSources );
93 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
96 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
97 context = tmpContext.get();
100 QList<QgsVectorTileBasicRendererStyle> rendererStyles;
101 QList<QgsVectorTileBasicLabelingStyle> labelingStyles;
104 bool hasRendererBackgroundStyle =
false;
106 for (
const QVariant &layer : layers )
108 const QVariantMap jsonLayer = layer.toMap();
110 const QString layerType = jsonLayer.value( u
"type"_s ).toString();
111 if ( layerType ==
"background"_L1 )
113 hasRendererBackgroundStyle =
parseFillLayer( jsonLayer, rendererBackgroundStyle, *context,
true );
114 if ( hasRendererBackgroundStyle )
124 const QString styleId = jsonLayer.value( u
"id"_s ).toString();
127 if ( layerType.compare(
"raster"_L1, Qt::CaseInsensitive ) == 0 )
130 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
131 if ( jsonPaint.contains( u
"raster-opacity"_s ) )
133 const QVariant jsonRasterOpacity = jsonPaint.value( u
"raster-opacity"_s );
134 double defaultOpacity = 1;
138 mRasterSubLayers.append( raster );
142 const QString layerName = jsonLayer.value( u
"source-layer"_s ).toString();
144 const int minZoom = jsonLayer.value( u
"minzoom"_s, u
"-1"_s ).toInt();
153 int maxZoom = jsonLayer.value( u
"maxzoom"_s, u
"-1"_s ).toInt();
158 if ( jsonLayer.contains( u
"visibility"_s ) )
160 visibilyStr = jsonLayer.value( u
"visibility"_s ).toString();
162 else if ( jsonLayer.contains( u
"layout"_s ) && jsonLayer.value( u
"layout"_s ).userType() == QMetaType::Type::QVariantMap )
164 const QVariantMap jsonLayout = jsonLayer.value( u
"layout"_s ).toMap();
165 visibilyStr = jsonLayout.value( u
"visibility"_s ).toString();
168 const bool enabled = visibilyStr !=
"none"_L1;
170 QString filterExpression;
171 if ( jsonLayer.contains( u
"filter"_s ) )
173 filterExpression =
parseExpression( jsonLayer.value( u
"filter"_s ).toList(), *context );
179 bool hasRendererStyle =
false;
180 bool hasLabelingStyle =
false;
181 if ( layerType ==
"fill"_L1 )
183 hasRendererStyle =
parseFillLayer( jsonLayer, rendererStyle, *context );
185 else if ( layerType ==
"line"_L1 )
187 hasRendererStyle =
parseLineLayer( jsonLayer, rendererStyle, *context );
189 else if ( layerType ==
"circle"_L1 )
193 else if ( layerType ==
"symbol"_L1 )
195 parseSymbolLayer( jsonLayer, rendererStyle, hasRendererStyle, labelingStyle, hasLabelingStyle, *context );
199 mWarnings << QObject::tr(
"%1: Skipping unknown layer type %2" ).arg( context->
layerId(), layerType );
204 if ( hasRendererStyle )
212 rendererStyles.append( rendererStyle );
215 if ( hasLabelingStyle )
223 labelingStyles.append( labelingStyle );
226 mWarnings.append( context->
warnings() );
230 if ( hasRendererBackgroundStyle )
231 rendererStyles.prepend( rendererBackgroundStyle );
233 auto renderer = std::make_unique< QgsVectorTileBasicRenderer >();
234 renderer->setStyles( rendererStyles );
237 auto labeling = std::make_unique< QgsVectorTileBasicLabeling >();
238 labeling->setStyles( labelingStyles );
244 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
249 bool colorIsDataDefined =
false;
251 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsFillSymbol >() );
255 if ( jsonPaint.contains( isBackgroundStyle ? u
"background-color"_s : u
"fill-color"_s ) )
257 const QVariant jsonFillColor = jsonPaint.value( isBackgroundStyle ? u
"background-color"_s : u
"fill-color"_s );
258 switch ( jsonFillColor.userType() )
260 case QMetaType::Type::QVariantMap:
264 case QMetaType::Type::QVariantList:
265 case QMetaType::Type::QStringList:
266 colorIsDataDefined =
true;
270 case QMetaType::Type::QString:
271 fillColor =
parseColor( jsonFillColor.toString(), context );
276 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillColor.userType() ) ) ) );
284 fillColor = QColor( 0, 0, 0 );
287 QColor fillOutlineColor;
288 if ( !isBackgroundStyle )
290 if ( !jsonPaint.contains( u
"fill-outline-color"_s ) )
292 if ( fillColor.isValid() )
293 fillOutlineColor = fillColor;
301 const QVariant jsonFillOutlineColor = jsonPaint.value( u
"fill-outline-color"_s );
302 switch ( jsonFillOutlineColor.userType() )
304 case QMetaType::Type::QVariantMap:
308 case QMetaType::Type::QVariantList:
309 case QMetaType::Type::QStringList:
313 case QMetaType::Type::QString:
314 fillOutlineColor =
parseColor( jsonFillOutlineColor.toString(), context );
318 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-outline-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillOutlineColor.userType() ) ) ) );
324 double fillOpacity = -1.0;
325 double rasterOpacity = -1.0;
326 if ( jsonPaint.contains( isBackgroundStyle ? u
"background-opacity"_s : u
"fill-opacity"_s ) )
328 const QVariant jsonFillOpacity = jsonPaint.value( isBackgroundStyle ? u
"background-opacity"_s : u
"fill-opacity"_s );
329 switch ( jsonFillOpacity.userType() )
331 case QMetaType::Type::Int:
332 case QMetaType::Type::LongLong:
333 case QMetaType::Type::Double:
334 fillOpacity = jsonFillOpacity.toDouble();
335 rasterOpacity = fillOpacity;
338 case QMetaType::Type::QVariantMap:
351 case QMetaType::Type::QVariantList:
352 case QMetaType::Type::QStringList:
366 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillOpacity.userType() ) ) ) );
372 QPointF fillTranslate;
373 if ( jsonPaint.contains( u
"fill-translate"_s ) )
375 const QVariant jsonFillTranslate = jsonPaint.value( u
"fill-translate"_s );
376 switch ( jsonFillTranslate.userType() )
379 case QMetaType::Type::QVariantMap:
383 case QMetaType::Type::QVariantList:
384 case QMetaType::Type::QStringList:
390 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-translate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillTranslate.userType() ) ) ) );
396 Q_ASSERT( fillSymbol );
399 symbol->setOutputUnit( context.
targetUnit() );
402 if ( !fillTranslate.isNull() )
408 if ( jsonPaint.contains( isBackgroundStyle ? u
"background-pattern"_s : u
"fill-pattern"_s ) )
412 const QVariant fillPatternJson = jsonPaint.value( isBackgroundStyle ? u
"background-pattern"_s : u
"fill-pattern"_s );
415 fillColor = QColor();
416 fillOutlineColor = QColor();
423 QString spriteProperty, spriteSizeProperty;
425 if ( !sprite.isEmpty() )
430 rasterFill->
setWidth( spriteSize.width() );
434 if ( rasterOpacity >= 0 )
439 if ( !spriteProperty.isEmpty() )
446 symbol->appendSymbolLayer( rasterFill );
452 if ( fillOpacity != -1 )
454 symbol->setOpacity( fillOpacity );
465 if ( fillOutlineColor.isValid() && ( fillOutlineColor.alpha() == 255 || fillOutlineColor != fillColor ) )
476 if ( fillColor.isValid() )
480 else if ( colorIsDataDefined )
496 if ( !jsonLayer.contains( u
"paint"_s ) )
498 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
503 QString rasterLineSprite;
505 const QVariantMap jsonPaint = jsonLayer.
value( u
"paint"_s ).toMap();
506 if ( jsonPaint.contains( u
"line-pattern"_s ) )
508 const QVariant jsonLinePattern = jsonPaint.value( u
"line-pattern"_s );
509 switch ( jsonLinePattern.userType() )
511 case QMetaType::Type::QVariantMap:
512 case QMetaType::Type::QString:
515 QString spriteProperty, spriteSizeProperty;
521 case QMetaType::Type::QVariantList:
522 case QMetaType::Type::QStringList:
527 if ( rasterLineSprite.isEmpty() )
530 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-pattern property" ).arg( context.
layerId() ) );
537 if ( jsonPaint.contains( u
"line-color"_s ) )
539 const QVariant jsonLineColor = jsonPaint.value( u
"line-color"_s );
540 switch ( jsonLineColor.userType() )
542 case QMetaType::Type::QVariantMap:
547 case QMetaType::Type::QVariantList:
548 case QMetaType::Type::QStringList:
553 case QMetaType::Type::QString:
554 lineColor =
parseColor( jsonLineColor.toString(), context );
558 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineColor.userType() ) ) ) );
565 lineColor = QColor( 0, 0, 0 );
571 if ( jsonPaint.contains( u
"line-width"_s ) )
573 const QVariant jsonLineWidth = jsonPaint.value( u
"line-width"_s );
574 switch ( jsonLineWidth.userType() )
576 case QMetaType::Type::Int:
577 case QMetaType::Type::LongLong:
578 case QMetaType::Type::Double:
582 case QMetaType::Type::QVariantMap:
594 case QMetaType::Type::QVariantList:
595 case QMetaType::Type::QStringList:
607 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineWidth.userType() ) ) ) );
612 double lineOffset = 0.0;
613 if ( jsonPaint.contains( u
"line-offset"_s ) )
615 const QVariant jsonLineOffset = jsonPaint.value( u
"line-offset"_s );
616 switch ( jsonLineOffset.userType() )
618 case QMetaType::Type::Int:
619 case QMetaType::Type::LongLong:
620 case QMetaType::Type::Double:
624 case QMetaType::Type::QVariantMap:
629 case QMetaType::Type::QVariantList:
630 case QMetaType::Type::QStringList:
635 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineOffset.userType() ) ) ) );
640 double lineOpacity = -1.0;
642 if ( jsonPaint.contains( u
"line-opacity"_s ) )
644 const QVariant jsonLineOpacity = jsonPaint.value( u
"line-opacity"_s );
645 switch ( jsonLineOpacity.userType() )
647 case QMetaType::Type::Int:
648 case QMetaType::Type::LongLong:
649 case QMetaType::Type::Double:
650 lineOpacity = jsonLineOpacity.toDouble();
653 case QMetaType::Type::QVariantMap:
656 double defaultValue = 1.0;
665 case QMetaType::Type::QVariantList:
666 case QMetaType::Type::QStringList:
669 double defaultValue = 1.0;
680 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineOpacity.userType() ) ) ) );
685 QVector< double > dashVector;
686 if ( jsonPaint.contains( u
"line-dasharray"_s ) )
688 const QVariant jsonLineDashArray = jsonPaint.value( u
"line-dasharray"_s );
689 switch ( jsonLineDashArray.userType() )
691 case QMetaType::Type::QVariantMap:
693 QString arrayExpression;
696 arrayExpression = u
"array_to_string(array_foreach(%1,@element * (%2)), ';')"_s
697 .arg(
parseArrayStops( jsonLineDashArray.toMap().value( u
"stops"_s ).toList(), context, 1 ),
702 arrayExpression = u
"array_to_string(%1, ';')"_s.arg(
parseArrayStops( jsonLineDashArray.toMap().value( u
"stops"_s ).toList(), context, lineWidth ) );
706 const QVariantList dashSource = jsonLineDashArray.toMap().value( u
"stops"_s ).toList().first().toList().value( 1 ).toList();
707 for (
const QVariant &v : dashSource )
709 dashVector << v.toDouble() * lineWidth;
714 case QMetaType::Type::QVariantList:
715 case QMetaType::Type::QStringList:
717 const QVariantList dashSource = jsonLineDashArray.toList();
719 if ( dashSource.at( 0 ).userType() == QMetaType::Type::QString )
725 .arg( property.asExpression(), lineWidthProperty.
asExpression() ) );
735 QVector< double > rawDashVectorSizes;
736 rawDashVectorSizes.reserve( dashSource.size() );
737 for (
const QVariant &v : dashSource )
739 rawDashVectorSizes << v.toDouble();
743 if ( rawDashVectorSizes.size() == 1 )
746 rawDashVectorSizes.clear();
748 else if ( rawDashVectorSizes.size() % 2 == 1 )
752 rawDashVectorSizes[0] = rawDashVectorSizes[0] + rawDashVectorSizes[rawDashVectorSizes.size() - 1];
753 rawDashVectorSizes.resize( rawDashVectorSizes.size() - 1 );
756 if ( !rawDashVectorSizes.isEmpty() && ( !lineWidthProperty.
asExpression().isEmpty() ) )
758 QStringList dashArrayStringParts;
759 dashArrayStringParts.reserve( rawDashVectorSizes.size() );
760 for (
double v : std::as_const( rawDashVectorSizes ) )
765 QString arrayExpression = u
"array_to_string(array_foreach(array(%1),@element * (%2)), ';')"_s
766 .arg( dashArrayStringParts.join(
',' ),
772 for (
double v : std::as_const( rawDashVectorSizes ) )
774 dashVector << v *lineWidth;
781 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-dasharray type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineDashArray.userType() ) ) ) );
786 Qt::PenCapStyle penCapStyle = Qt::FlatCap;
787 Qt::PenJoinStyle penJoinStyle = Qt::MiterJoin;
788 if ( jsonLayer.contains( u
"layout"_s ) )
790 const QVariantMap jsonLayout = jsonLayer.value( u
"layout"_s ).toMap();
791 if ( jsonLayout.contains( u
"line-cap"_s ) )
793 penCapStyle =
parseCapStyle( jsonLayout.value( u
"line-cap"_s ).toString() );
795 if ( jsonLayout.contains( u
"line-join"_s ) )
797 penJoinStyle =
parseJoinStyle( jsonLayout.value( u
"line-join"_s ).toString() );
801 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsLineSymbol >() );
802 symbol->setOutputUnit( context.
targetUnit() );
804 if ( !rasterLineSprite.isEmpty() )
814 if ( lineOpacity != -1 )
816 symbol->setOpacity( lineOpacity );
822 symbol->setDataDefinedProperties( ddProperties );
824 if ( lineWidth != -1 )
828 symbol->changeSymbolLayer( 0, lineSymbol );
833 Q_ASSERT( lineSymbol );
843 if ( lineOpacity != -1 )
845 symbol->setOpacity( lineOpacity );
851 symbol->setDataDefinedProperties( ddProperties );
853 if ( lineColor.isValid() )
857 if ( lineWidth != -1 )
861 if ( !dashVector.empty() )
875 if ( !jsonLayer.contains( u
"paint"_s ) )
877 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
881 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
886 QColor circleFillColor;
887 if ( jsonPaint.contains( u
"circle-color"_s ) )
889 const QVariant jsonCircleColor = jsonPaint.value( u
"circle-color"_s );
890 switch ( jsonCircleColor.userType() )
892 case QMetaType::Type::QVariantMap:
896 case QMetaType::Type::QVariantList:
897 case QMetaType::Type::QStringList:
901 case QMetaType::Type::QString:
902 circleFillColor =
parseColor( jsonCircleColor.toString(), context );
906 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleColor.userType() ) ) ) );
913 circleFillColor = QColor( 0, 0, 0 );
917 double circleDiameter = 10.0;
918 if ( jsonPaint.contains( u
"circle-radius"_s ) )
920 const QVariant jsonCircleRadius = jsonPaint.value( u
"circle-radius"_s );
921 switch ( jsonCircleRadius.userType() )
923 case QMetaType::Type::Int:
924 case QMetaType::Type::LongLong:
925 case QMetaType::Type::Double:
929 case QMetaType::Type::QVariantMap:
934 case QMetaType::Type::QVariantList:
935 case QMetaType::Type::QStringList:
940 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-radius type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleRadius.userType() ) ) ) );
945 double circleOpacity = -1.0;
946 if ( jsonPaint.contains( u
"circle-opacity"_s ) )
948 const QVariant jsonCircleOpacity = jsonPaint.value( u
"circle-opacity"_s );
949 switch ( jsonCircleOpacity.userType() )
951 case QMetaType::Type::Int:
952 case QMetaType::Type::LongLong:
953 case QMetaType::Type::Double:
954 circleOpacity = jsonCircleOpacity.toDouble();
957 case QMetaType::Type::QVariantMap:
961 case QMetaType::Type::QVariantList:
962 case QMetaType::Type::QStringList:
967 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleOpacity.userType() ) ) ) );
971 if ( ( circleOpacity != -1 ) && circleFillColor.isValid() )
973 circleFillColor.setAlphaF( circleOpacity );
977 QColor circleStrokeColor;
978 if ( jsonPaint.contains( u
"circle-stroke-color"_s ) )
980 const QVariant jsonCircleStrokeColor = jsonPaint.value( u
"circle-stroke-color"_s );
981 switch ( jsonCircleStrokeColor.userType() )
983 case QMetaType::Type::QVariantMap:
987 case QMetaType::Type::QVariantList:
988 case QMetaType::Type::QStringList:
992 case QMetaType::Type::QString:
993 circleStrokeColor =
parseColor( jsonCircleStrokeColor.toString(), context );
997 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-stroke-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleStrokeColor.userType() ) ) ) );
1003 double circleStrokeWidth = -1.0;
1004 if ( jsonPaint.contains( u
"circle-stroke-width"_s ) )
1006 const QVariant circleStrokeWidthJson = jsonPaint.value( u
"circle-stroke-width"_s );
1007 switch ( circleStrokeWidthJson.userType() )
1009 case QMetaType::Type::Int:
1010 case QMetaType::Type::LongLong:
1011 case QMetaType::Type::Double:
1015 case QMetaType::Type::QVariantMap:
1016 circleStrokeWidth = -1.0;
1020 case QMetaType::Type::QVariantList:
1021 case QMetaType::Type::QStringList:
1026 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-stroke-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( circleStrokeWidthJson.userType() ) ) ) );
1031 double circleStrokeOpacity = -1.0;
1032 if ( jsonPaint.contains( u
"circle-stroke-opacity"_s ) )
1034 const QVariant jsonCircleStrokeOpacity = jsonPaint.value( u
"circle-stroke-opacity"_s );
1035 switch ( jsonCircleStrokeOpacity.userType() )
1037 case QMetaType::Type::Int:
1038 case QMetaType::Type::LongLong:
1039 case QMetaType::Type::Double:
1040 circleStrokeOpacity = jsonCircleStrokeOpacity.toDouble();
1043 case QMetaType::Type::QVariantMap:
1047 case QMetaType::Type::QVariantList:
1048 case QMetaType::Type::QStringList:
1053 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-stroke-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleStrokeOpacity.userType() ) ) ) );
1057 if ( ( circleStrokeOpacity != -1 ) && circleStrokeColor.isValid() )
1059 circleStrokeColor.setAlphaF( circleStrokeOpacity );
1063 QPointF circleTranslate;
1064 if ( jsonPaint.contains( u
"circle-translate"_s ) )
1066 const QVariant jsonCircleTranslate = jsonPaint.value( u
"circle-translate"_s );
1067 switch ( jsonCircleTranslate.userType() )
1070 case QMetaType::Type::QVariantMap:
1074 case QMetaType::Type::QVariantList:
1075 case QMetaType::Type::QStringList:
1081 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-translate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleTranslate.userType() ) ) ) );
1086 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsMarkerSymbol >() );
1088 Q_ASSERT( markerSymbolLayer );
1091 symbol->setOutputUnit( context.
targetUnit() );
1094 if ( !circleTranslate.isNull() )
1096 markerSymbolLayer->
setOffset( circleTranslate );
1100 if ( circleFillColor.isValid() )
1104 if ( circleDiameter != -1 )
1106 markerSymbolLayer->
setSize( circleDiameter );
1109 if ( circleStrokeColor.isValid() )
1113 if ( circleStrokeWidth != -1 )
1126 hasLabeling =
false;
1127 hasRenderer =
false;
1129 if ( !jsonLayer.contains( u
"layout"_s ) )
1131 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
1134 const QVariantMap jsonLayout = jsonLayer.value( u
"layout"_s ).toMap();
1135 if ( !jsonLayout.contains( u
"text-field"_s ) )
1141 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
1147 if ( jsonLayout.contains( u
"text-size"_s ) )
1149 const QVariant jsonTextSize = jsonLayout.value( u
"text-size"_s );
1150 switch ( jsonTextSize.userType() )
1152 case QMetaType::Type::Int:
1153 case QMetaType::Type::LongLong:
1154 case QMetaType::Type::Double:
1158 case QMetaType::Type::QVariantMap:
1164 case QMetaType::Type::QVariantList:
1165 case QMetaType::Type::QStringList:
1171 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextSize.userType() ) ) ) );
1175 if ( textSizeProperty )
1182 constexpr double EM_TO_CHARS = 2.0;
1184 double textMaxWidth = -1;
1185 if ( jsonLayout.contains( u
"text-max-width"_s ) )
1187 const QVariant jsonTextMaxWidth = jsonLayout.value( u
"text-max-width"_s );
1188 switch ( jsonTextMaxWidth.userType() )
1190 case QMetaType::Type::Int:
1191 case QMetaType::Type::LongLong:
1192 case QMetaType::Type::Double:
1193 textMaxWidth = jsonTextMaxWidth.toDouble() * EM_TO_CHARS;
1196 case QMetaType::Type::QVariantMap:
1200 case QMetaType::Type::QVariantList:
1201 case QMetaType::Type::QStringList:
1206 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-max-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextMaxWidth.userType() ) ) ) );
1213 textMaxWidth = 10 * EM_TO_CHARS;
1216 double textLetterSpacing = -1;
1217 if ( jsonLayout.contains( u
"text-letter-spacing"_s ) )
1219 const QVariant jsonTextLetterSpacing = jsonLayout.value( u
"text-letter-spacing"_s );
1220 switch ( jsonTextLetterSpacing.userType() )
1222 case QMetaType::Type::Int:
1223 case QMetaType::Type::LongLong:
1224 case QMetaType::Type::Double:
1225 textLetterSpacing = jsonTextLetterSpacing.toDouble();
1228 case QMetaType::Type::QVariantMap:
1232 case QMetaType::Type::QVariantList:
1233 case QMetaType::Type::QStringList:
1238 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-letter-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextLetterSpacing.userType() ) ) ) );
1244 bool foundFont =
false;
1246 QString fontStyleName;
1248 bool allowOverlap = jsonLayout.contains( u
"text-allow-overlap"_s ) && jsonLayout.
value( u
"text-allow-overlap"_s ).toBool();
1250 if ( jsonLayout.contains( u
"text-font"_s ) )
1252 auto splitFontFamily = [](
const QString & fontName, QString & family, QString & style ) ->
bool
1254 QString matchedFamily;
1255 const QStringList textFontParts = fontName.split(
' ' );
1256 for (
int i = textFontParts.size() - 1; i >= 1; --i )
1258 const QString candidateFontFamily = textFontParts.mid( 0, i ).join(
' ' );
1259 const QString candidateFontStyle = textFontParts.mid( i ).join(
' ' );
1264 family = processedFontFamily;
1265 style = candidateFontStyle;
1270 if ( processedFontFamily == matchedFamily )
1272 family = processedFontFamily;
1273 style = candidateFontStyle;
1277 family = matchedFamily;
1278 style = processedFontFamily;
1279 style.replace( matchedFamily, QString() );
1280 style = style.trimmed();
1281 if ( !style.isEmpty() && !candidateFontStyle.isEmpty() )
1283 style += u
" %1"_s.arg( candidateFontStyle );
1291 if ( QFontDatabase().hasFamily( processedFontFamily ) )
1294 family = processedFontFamily;
1300 family = matchedFamily;
1307 const QVariant jsonTextFont = jsonLayout.value( u
"text-font"_s );
1308 if ( jsonTextFont.userType() != QMetaType::Type::QVariantList && jsonTextFont.userType() != QMetaType::Type::QStringList && jsonTextFont.userType() != QMetaType::Type::QString
1309 && jsonTextFont.userType() != QMetaType::Type::QVariantMap )
1311 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-font type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextFont.userType() ) ) ) );
1315 switch ( jsonTextFont.userType() )
1317 case QMetaType::Type::QVariantList:
1318 case QMetaType::Type::QStringList:
1319 fontName = jsonTextFont.toList().value( 0 ).toString();
1322 case QMetaType::Type::QString:
1323 fontName = jsonTextFont.toString();
1326 case QMetaType::Type::QVariantMap:
1328 QString familyCaseString = u
"CASE "_s;
1329 QString styleCaseString = u
"CASE "_s;
1331 const QVariantList stops = jsonTextFont.toMap().value( u
"stops"_s ).toList();
1334 for (
int i = 0; i < stops.length() - 1; ++i )
1337 const QVariant bz = stops.value( i ).toList().value( 0 );
1338 const QString bv = stops.value( i ).toList().value( 1 ).userType() == QMetaType::Type::QString ? stops.value( i ).toList().value( 1 ).toString() : stops.value( i ).toList().value( 1 ).toList().value( 0 ).toString();
1339 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
1341 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1347 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
1348 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
1350 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1355 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1357 familyCaseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1358 "THEN %3 " ).arg( bz.toString(),
1361 styleCaseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1362 "THEN %3 " ).arg( bz.toString(),
1368 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1374 const QString bv = stops.constLast().toList().value( 1 ).userType() == QMetaType::Type::QString ? stops.constLast().toList().value( 1 ).toString() : stops.constLast().toList().value( 1 ).toList().value( 0 ).toString();
1375 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1382 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1389 fontName = fontFamily;
1399 if ( splitFontFamily( fontName, fontFamily, fontStyleName ) )
1402 if ( !fontStyleName.isEmpty() )
1403 textFont.setStyleName( fontStyleName );
1413 fontName = u
"Open Sans"_s;
1415 textFont.setStyleName( u
"Regular"_s );
1416 fontStyleName = u
"Regular"_s;
1421 fontName = u
"Arial Unicode MS"_s;
1423 textFont.setStyleName( u
"Regular"_s );
1424 fontStyleName = u
"Regular"_s;
1429 fontName = u
"Open Sans, Arial Unicode MS"_s;
1432 if ( !foundFont && !fontName.isEmpty() )
1434 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), fontName ) );
1439 if ( jsonPaint.contains( u
"text-color"_s ) )
1441 const QVariant jsonTextColor = jsonPaint.value( u
"text-color"_s );
1442 switch ( jsonTextColor.userType() )
1444 case QMetaType::Type::QVariantMap:
1448 case QMetaType::Type::QVariantList:
1449 case QMetaType::Type::QStringList:
1453 case QMetaType::Type::QString:
1454 textColor =
parseColor( jsonTextColor.toString(), context );
1458 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextColor.userType() ) ) ) );
1465 textColor = QColor( 0, 0, 0 );
1469 QColor bufferColor( 0, 0, 0, 0 );
1470 if ( jsonPaint.contains( u
"text-halo-color"_s ) )
1472 const QVariant jsonBufferColor = jsonPaint.value( u
"text-halo-color"_s );
1473 switch ( jsonBufferColor.userType() )
1475 case QMetaType::Type::QVariantMap:
1479 case QMetaType::Type::QVariantList:
1480 case QMetaType::Type::QStringList:
1484 case QMetaType::Type::QString:
1485 bufferColor =
parseColor( jsonBufferColor.toString(), context );
1489 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-halo-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonBufferColor.userType() ) ) ) );
1494 double bufferSize = 0.0;
1498 constexpr double BUFFER_SIZE_SCALE = 2.0;
1499 if ( jsonPaint.contains( u
"text-halo-width"_s ) )
1501 const QVariant jsonHaloWidth = jsonPaint.value( u
"text-halo-width"_s );
1502 QString bufferSizeDataDefined;
1503 switch ( jsonHaloWidth.userType() )
1505 case QMetaType::Type::Int:
1506 case QMetaType::Type::LongLong:
1507 case QMetaType::Type::Double:
1511 case QMetaType::Type::QVariantMap:
1516 case QMetaType::Type::QVariantList:
1517 case QMetaType::Type::QStringList:
1523 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-halo-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonHaloWidth.userType() ) ) ) );
1529 if ( bufferSize > 0 )
1531 if ( textSize > 0 && bufferSizeDataDefined.isEmpty() )
1533 bufferSize = std::min( bufferSize, textSize * BUFFER_SIZE_SCALE / 4 );
1535 else if ( textSize > 0 && !bufferSizeDataDefined.isEmpty() )
1537 bufferSizeDataDefined = u
"min(%1/4, %2)"_s.arg( textSize * BUFFER_SIZE_SCALE ).arg( bufferSizeDataDefined );
1540 else if ( !bufferSizeDataDefined.isEmpty() )
1542 bufferSizeDataDefined = u
"min(%1*%2/4, %3)"_s
1544 .arg( BUFFER_SIZE_SCALE )
1545 .arg( bufferSizeDataDefined );
1548 else if ( bufferSizeDataDefined.isEmpty() )
1550 bufferSizeDataDefined = u
"min(%1*%2/4, %3)"_s
1552 .arg( BUFFER_SIZE_SCALE )
1559 double haloBlurSize = 0;
1560 if ( jsonPaint.contains( u
"text-halo-blur"_s ) )
1562 const QVariant jsonTextHaloBlur = jsonPaint.value( u
"text-halo-blur"_s );
1563 switch ( jsonTextHaloBlur.userType() )
1565 case QMetaType::Type::Int:
1566 case QMetaType::Type::LongLong:
1567 case QMetaType::Type::Double:
1574 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-halo-blur type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextHaloBlur.userType() ) ) ) );
1581 if ( textColor.isValid() )
1583 if ( textSize >= 0 )
1588 if ( !fontStyleName.isEmpty() )
1591 if ( textLetterSpacing > 0 )
1593 QFont f = format.
font();
1594 f.setLetterSpacing( QFont::AbsoluteSpacing, textLetterSpacing );
1598 if ( bufferSize > 0 )
1601 const double opacity = bufferColor.alphaF();
1602 bufferColor.setAlphaF( 1.0 );
1610 if ( haloBlurSize > 0 )
1633 if ( textMaxWidth > 0 )
1639 if ( jsonLayout.contains( u
"text-field"_s ) )
1641 const QVariant jsonTextField = jsonLayout.value( u
"text-field"_s );
1642 switch ( jsonTextField.userType() )
1644 case QMetaType::Type::QString:
1646 labelSettings.
fieldName = processLabelField( jsonTextField.toString(), labelSettings.
isExpression );
1650 case QMetaType::Type::QVariantList:
1651 case QMetaType::Type::QStringList:
1653 const QVariantList textFieldList = jsonTextField.toList();
1661 if ( textFieldList.size() > 2 && textFieldList.at( 0 ).toString() ==
"format"_L1 )
1664 for (
int i = 1; i < textFieldList.size(); ++i )
1666 bool isExpression =
false;
1667 const QString part = processLabelField( textFieldList.at( i ).toString(), isExpression );
1668 if ( !isExpression )
1675 labelSettings.
fieldName = u
"concat(%1)"_s.arg( parts.join(
',' ) );
1690 case QMetaType::Type::QVariantMap:
1692 const QVariantList stops = jsonTextField.toMap().value( u
"stops"_s ).toList();
1693 if ( !stops.empty() )
1700 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-field dictionary" ).arg( context.
layerId() ) );
1706 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-field type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextField.userType() ) ) ) );
1711 if ( jsonLayout.contains( u
"text-rotate"_s ) )
1713 const QVariant jsonTextRotate = jsonLayout.value( u
"text-rotate"_s );
1714 switch ( jsonTextRotate.userType() )
1716 case QMetaType::Type::Double:
1717 case QMetaType::Type::Int:
1719 labelSettings.
angleOffset = jsonTextRotate.toDouble();
1723 case QMetaType::Type::QVariantList:
1724 case QMetaType::Type::QStringList:
1731 case QMetaType::Type::QVariantMap:
1733 QVariantMap rotateMap = jsonTextRotate.toMap();
1734 if ( rotateMap.contains( u
"property"_s ) && rotateMap[u
"type"_s].toString() ==
"identity"_L1 )
1740 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-rotate map content (%2)" ).arg( context.
layerId(), QString( QJsonDocument::fromVariant( rotateMap ).toJson() ) ) );
1745 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextRotate.userType() ) ) ) );
1750 if ( jsonLayout.contains( u
"text-transform"_s ) )
1752 const QString textTransform = jsonLayout.value( u
"text-transform"_s ).toString();
1753 if ( textTransform ==
"uppercase"_L1 )
1757 else if ( textTransform ==
"lowercase"_L1 )
1766 if ( jsonLayout.contains( u
"symbol-placement"_s ) )
1768 const QString symbolPlacement = jsonLayout.value( u
"symbol-placement"_s ).toString();
1769 if ( symbolPlacement ==
"line"_L1 )
1775 if ( jsonLayout.contains( u
"text-rotation-alignment"_s ) )
1777 const QString textRotationAlignment = jsonLayout.value( u
"text-rotation-alignment"_s ).toString();
1778 if ( textRotationAlignment ==
"viewport"_L1 )
1788 if ( jsonLayout.contains( u
"text-offset"_s ) )
1790 const QVariant jsonTextOffset = jsonLayout.value( u
"text-offset"_s );
1793 switch ( jsonTextOffset.userType() )
1795 case QMetaType::Type::QVariantMap:
1796 textOffsetProperty =
parseInterpolatePointByZoom( jsonTextOffset.toMap(), context, !textSizeProperty ? textSize : 1.0, &textOffset );
1797 if ( !textSizeProperty )
1808 case QMetaType::Type::QVariantList:
1809 case QMetaType::Type::QStringList:
1810 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1811 jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1815 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextOffset.userType() ) ) ) );
1819 if ( !textOffset.isNull() )
1822 labelSettings.
dist = std::abs( textOffset.y() ) - textSize;
1824 if ( textSizeProperty && !textOffsetProperty )
1831 if ( textOffset.isNull() )
1839 if ( jsonLayout.contains( u
"text-justify"_s ) )
1841 const QVariant jsonTextJustify = jsonLayout.value( u
"text-justify"_s );
1844 QString textAlign = u
"center"_s;
1846 const QVariantMap conversionMap
1848 { u
"left"_s, u
"left"_s },
1849 { u
"center"_s, u
"center"_s },
1850 { u
"right"_s, u
"right"_s },
1851 { u
"auto"_s, u
"follow"_s }
1854 switch ( jsonTextJustify.userType() )
1856 case QMetaType::Type::QString:
1857 textAlign = jsonTextJustify.toString();
1860 case QMetaType::Type::QVariantList:
1864 case QMetaType::Type::QVariantMap:
1869 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-justify type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextJustify.userType() ) ) ) );
1873 if ( textAlign ==
"left"_L1 )
1875 else if ( textAlign ==
"right"_L1 )
1877 else if ( textAlign ==
"center"_L1 )
1879 else if ( textAlign ==
"follow"_L1 )
1889 if ( jsonLayout.contains( u
"text-anchor"_s ) )
1891 const QVariant jsonTextAnchor = jsonLayout.value( u
"text-anchor"_s );
1894 const QVariantMap conversionMap
1901 { u
"top-left"_s, 8 },
1902 { u
"top-right"_s, 6 },
1903 { u
"bottom-left"_s, 2 },
1904 { u
"bottom-right"_s, 0 },
1907 switch ( jsonTextAnchor.userType() )
1909 case QMetaType::Type::QString:
1910 textAnchor = jsonTextAnchor.toString();
1913 case QMetaType::Type::QVariantList:
1917 case QMetaType::Type::QVariantMap:
1922 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-anchor type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextAnchor.userType() ) ) ) );
1926 if ( textAnchor ==
"center"_L1 )
1928 else if ( textAnchor ==
"left"_L1 )
1930 else if ( textAnchor ==
"right"_L1 )
1932 else if ( textAnchor ==
"top"_L1 )
1934 else if ( textAnchor ==
"bottom"_L1 )
1936 else if ( textAnchor ==
"top-left"_L1 )
1938 else if ( textAnchor ==
"top-right"_L1 )
1940 else if ( textAnchor ==
"bottom-left"_L1 )
1942 else if ( textAnchor ==
"bottom-right"_L1 )
1947 if ( jsonLayout.contains( u
"text-offset"_s ) )
1949 const QVariant jsonTextOffset = jsonLayout.value( u
"text-offset"_s );
1952 switch ( jsonTextOffset.userType() )
1954 case QMetaType::Type::QVariantMap:
1958 case QMetaType::Type::QVariantList:
1959 case QMetaType::Type::QStringList:
1960 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1961 jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1965 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextOffset.userType() ) ) ) );
1969 if ( !textOffset.isNull() )
1972 labelSettings.
xOffset = textOffset.x();
1973 labelSettings.
yOffset = textOffset.y();
1978 if ( jsonLayout.contains( u
"icon-image"_s ) &&
1982 QString spriteProperty, spriteSizeProperty;
1984 if ( !sprite.isEmpty() )
1987 if ( jsonLayout.contains( u
"icon-size"_s ) )
1990 const QVariant jsonIconSize = jsonLayout.
value( u
"icon-size"_s );
1991 switch ( jsonIconSize.userType() )
1993 case QMetaType::Type::Int:
1994 case QMetaType::Type::LongLong:
1995 case QMetaType::Type::Double:
1997 size = jsonIconSize.toDouble();
1998 if ( !spriteSizeProperty.isEmpty() )
2006 case QMetaType::Type::QVariantMap:
2010 case QMetaType::Type::QVariantList:
2011 case QMetaType::Type::QStringList:
2015 context.
pushWarning( QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) ) );
2021 if ( !spriteSizeProperty.isEmpty() )
2035 markerLayer->
setPath( sprite );
2036 markerLayer->
setSize( spriteSize.width() );
2039 if ( !spriteProperty.isEmpty() )
2049 backgroundSettings.
setSize( spriteSize * size );
2057 if ( textSize >= 0 )
2080 if ( !jsonLayer.contains( u
"layout"_s ) )
2082 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
2085 const QVariantMap jsonLayout = jsonLayer.value( u
"layout"_s ).toMap();
2087 if ( jsonLayout.value( u
"symbol-placement"_s ).toString() ==
"line"_L1 && !jsonLayout.contains( u
"text-field"_s ) )
2091 double spacing = -1.0;
2092 if ( jsonLayout.contains( u
"symbol-spacing"_s ) )
2094 const QVariant jsonSpacing = jsonLayout.value( u
"symbol-spacing"_s );
2095 switch ( jsonSpacing.userType() )
2097 case QMetaType::Type::Int:
2098 case QMetaType::Type::LongLong:
2099 case QMetaType::Type::Double:
2103 case QMetaType::Type::QVariantMap:
2107 case QMetaType::Type::QVariantList:
2108 case QMetaType::Type::QStringList:
2113 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported symbol-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonSpacing.userType() ) ) ) );
2123 bool rotateMarkers =
true;
2124 if ( jsonLayout.contains( u
"icon-rotation-alignment"_s ) )
2126 const QString alignment = jsonLayout.value( u
"icon-rotation-alignment"_s ).toString();
2127 if ( alignment ==
"map"_L1 || alignment ==
"auto"_L1 )
2129 rotateMarkers =
true;
2131 else if ( alignment ==
"viewport"_L1 )
2133 rotateMarkers =
false;
2138 double rotation = 0.0;
2139 if ( jsonLayout.contains( u
"icon-rotate"_s ) )
2141 const QVariant jsonIconRotate = jsonLayout.value( u
"icon-rotate"_s );
2142 switch ( jsonIconRotate.userType() )
2144 case QMetaType::Type::Int:
2145 case QMetaType::Type::LongLong:
2146 case QMetaType::Type::Double:
2147 rotation = jsonIconRotate.toDouble();
2150 case QMetaType::Type::QVariantMap:
2154 case QMetaType::Type::QVariantList:
2155 case QMetaType::Type::QStringList:
2160 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconRotate.userType() ) ) ) );
2176 QString spriteProperty, spriteSizeProperty;
2178 if ( !sprite.isNull() )
2180 markerLayer->
setPath( sprite );
2181 markerLayer->
setSize( spriteSize.width() );
2184 if ( !spriteProperty.isEmpty() )
2191 if ( jsonLayout.contains( u
"icon-size"_s ) )
2193 const QVariant jsonIconSize = jsonLayout.value( u
"icon-size"_s );
2196 switch ( jsonIconSize.userType() )
2198 case QMetaType::Type::Int:
2199 case QMetaType::Type::LongLong:
2200 case QMetaType::Type::Double:
2202 size = jsonIconSize.toDouble();
2203 if ( !spriteSizeProperty.isEmpty() )
2211 case QMetaType::Type::QVariantMap:
2215 case QMetaType::Type::QVariantList:
2216 case QMetaType::Type::QStringList:
2220 context.
pushWarning( QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) ) );
2223 markerLayer->
setSize( size * spriteSize.width() );
2226 if ( !spriteSizeProperty.isEmpty() )
2243 std::unique_ptr< QgsSymbol > symbol = std::make_unique< QgsLineSymbol >(
QgsSymbolLayerList() << lineSymbol );
2246 symbol->setOutputUnit( context.
targetUnit() );
2250 rendererStyle.
setSymbol( symbol.release() );
2253 else if ( jsonLayout.contains( u
"icon-image"_s ) )
2255 const QVariantMap jsonPaint = jsonLayer.value( u
"paint"_s ).toMap();
2258 QString spriteProperty, spriteSizeProperty;
2260 if ( !sprite.isEmpty() || !spriteProperty.isEmpty() )
2263 rasterMarker->
setPath( sprite );
2264 rasterMarker->
setSize( spriteSize.width() );
2268 if ( !spriteProperty.isEmpty() )
2274 if ( jsonLayout.contains( u
"icon-size"_s ) )
2276 const QVariant jsonIconSize = jsonLayout.value( u
"icon-size"_s );
2279 switch ( jsonIconSize.userType() )
2281 case QMetaType::Type::Int:
2282 case QMetaType::Type::LongLong:
2283 case QMetaType::Type::Double:
2285 size = jsonIconSize.toDouble();
2286 if ( !spriteSizeProperty.isEmpty() )
2294 case QMetaType::Type::QVariantMap:
2298 case QMetaType::Type::QVariantList:
2299 case QMetaType::Type::QStringList:
2303 context.
pushWarning( QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) ) );
2306 rasterMarker->
setSize( size * spriteSize.width() );
2309 if ( !spriteSizeProperty.isEmpty() )
2322 double rotation = 0.0;
2323 if ( jsonLayout.contains( u
"icon-rotate"_s ) )
2325 const QVariant jsonIconRotate = jsonLayout.value( u
"icon-rotate"_s );
2326 switch ( jsonIconRotate.userType() )
2328 case QMetaType::Type::Int:
2329 case QMetaType::Type::LongLong:
2330 case QMetaType::Type::Double:
2331 rotation = jsonIconRotate.toDouble();
2334 case QMetaType::Type::QVariantMap:
2338 case QMetaType::Type::QVariantList:
2339 case QMetaType::Type::QStringList:
2344 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconRotate.userType() ) ) ) );
2349 double iconOpacity = -1.0;
2350 if ( jsonPaint.contains( u
"icon-opacity"_s ) )
2352 const QVariant jsonIconOpacity = jsonPaint.value( u
"icon-opacity"_s );
2353 switch ( jsonIconOpacity.userType() )
2355 case QMetaType::Type::Int:
2356 case QMetaType::Type::LongLong:
2357 case QMetaType::Type::Double:
2358 iconOpacity = jsonIconOpacity.toDouble();
2361 case QMetaType::Type::QVariantMap:
2365 case QMetaType::Type::QVariantList:
2366 case QMetaType::Type::QStringList:
2371 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported icon-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconOpacity.userType() ) ) ) );
2377 rasterMarker->
setAngle( rotation );
2378 if ( iconOpacity >= 0 )
2382 rendererStyle.
setSymbol( markerSymbol );
2393 const double base = json.
value( u
"base"_s, u
"1"_s ).toDouble();
2394 const QVariantList stops = json.value( u
"stops"_s ).toList();
2395 if ( stops.empty() )
2398 QString caseString = u
"CASE "_s;
2399 const QString colorComponent(
"color_part(%1,'%2')" );
2401 for (
int i = 0; i < stops.length() - 1; ++i )
2404 const QString bz = stops.at( i ).toList().value( 0 ).toString();
2406 const QString tz = stops.at( i + 1 ).toList().value( 0 ).toString();
2408 const QVariant bcVariant = stops.at( i ).toList().value( 1 );
2409 const QVariant tcVariant = stops.at( i + 1 ).toList().value( 1 );
2411 const QColor bottomColor =
parseColor( bcVariant.toString(), context );
2412 const QColor topColor =
parseColor( tcVariant.toString(), context );
2414 if ( i == 0 && bottomColor.isValid() )
2421 caseString += u
"WHEN @vector_tile_zoom < %1 THEN color_hsla(%2, %3, %4, %5) "_s
2422 .arg( bz ).arg( bcHue ).arg( bcSat ).arg( bcLight ).arg( bcAlpha );
2425 if ( bottomColor.isValid() && topColor.isValid() )
2437 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2438 "%3, %4, %5, %6) " ).arg( bz, tz,
2449 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2450 "%3, %4, %5, %6) " ).arg( bz, tz,
2451 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"hsl_hue" ), colorComponent.arg( topColorExpr ).arg(
"hsl_hue" ), base, 1, &context ),
2452 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"hsl_saturation" ), colorComponent.arg( topColorExpr ).arg(
"hsl_saturation" ), base, 1, &context ),
2453 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"lightness" ), colorComponent.arg( topColorExpr ).arg(
"lightness" ), base, 1, &context ),
2454 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"alpha" ), colorComponent.arg( topColorExpr ).arg(
"alpha" ), base, 1, &context ) );
2459 const QString tz = stops.last().toList().value( 0 ).toString();
2460 const QVariant tcVariant = stops.last().toList().value( 1 );
2462 if ( tcVariant.userType() == QMetaType::Type::QString )
2465 if ( topColor.isValid() )
2472 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2473 "ELSE color_hsla(%2, %3, %4, %5) END" ).arg( tz ).arg( tcHue ).arg( tcSat ).arg( tcLight ).arg( tcAlpha );
2476 else if ( tcVariant.userType() == QMetaType::QVariantList )
2480 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2481 "ELSE color_hsla(%2, %3, %4, %5) END" ).arg( tz )
2482 .arg( colorComponent.arg( topColorExpr ).arg(
"hsl_hue" ) ).arg( colorComponent.arg( topColorExpr ).arg(
"hsl_saturation" ) ).arg( colorComponent.arg( topColorExpr ).arg(
"lightness" ) ).arg( colorComponent.arg( topColorExpr ).arg(
"alpha" ) );
2485 if ( !stops.empty() && defaultColor )
2486 *defaultColor =
parseColor( stops.value( 0 ).toList().value( 1 ).toString(), context );
2493 const double base = json.
value( u
"base"_s, u
"1"_s ).toDouble();
2494 const QVariantList stops = json.value( u
"stops"_s ).toList();
2495 if ( stops.empty() )
2498 QString scaleExpression;
2499 if ( stops.size() <= 2 )
2502 stops.value( 0 ).toList().value( 0 ).toDouble(),
2503 stops.last().toList().value( 0 ).toDouble(),
2504 stops.value( 0 ).toList().value( 1 ),
2505 stops.last().toList().value( 1 ),
2506 base, multiplier, &context );
2510 scaleExpression =
parseStops( base, stops, multiplier, context );
2513 if ( !stops.empty() && defaultNumber )
2514 *defaultNumber = stops.value( 0 ).toList().value( 1 ).toDouble() * multiplier;
2524 context = *contextPtr;
2526 const double base = json.value( u
"base"_s, u
"1"_s ).toDouble();
2527 const QVariantList stops = json.value( u
"stops"_s ).toList();
2528 if ( stops.empty() )
2531 QString scaleExpression;
2532 if ( stops.length() <= 2 )
2534 const QVariant bv = stops.value( 0 ).toList().value( 1 );
2535 const QVariant tv = stops.last().toList().value( 1 );
2536 double bottom = 0.0;
2538 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2539 scaleExpression = u
"set_color_part(@symbol_color, 'alpha', %1)"_s
2541 stops.value( 0 ).toList().value( 0 ).toDouble(),
2542 stops.last().toList().value( 0 ).toDouble(),
2543 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2544 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ), base, 1, &context ) );
2555 QString caseString = u
"CASE WHEN @vector_tile_zoom < %1 THEN set_color_part(@symbol_color, 'alpha', %2)"_s
2556 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
2557 .arg( stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity );
2559 for (
int i = 0; i < stops.size() - 1; ++i )
2561 const QVariant bv = stops.value( i ).toList().value( 1 );
2562 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2563 double bottom = 0.0;
2565 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2567 caseString += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
2568 "THEN set_color_part(@symbol_color, 'alpha', %3)" )
2569 .arg( stops.value( i ).toList().value( 0 ).toString(),
2570 stops.value( i + 1 ).toList().value( 0 ).toString(),
2572 stops.value( i ).toList().value( 0 ).toDouble(),
2573 stops.value( i + 1 ).toList().value( 0 ).toDouble(),
2574 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2575 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ),
2576 base, 1, &context ) );
2580 bool numeric =
false;
2581 const QVariant vv = stops.last().toList().value( 1 );
2582 double dv = vv.toDouble( &numeric );
2584 caseString += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
2585 "THEN set_color_part(@symbol_color, 'alpha', %2) END" ).arg(
2586 stops.last().toList().value( 0 ).toString(),
2587 numeric ? QString::number( dv * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( vv, context ) ).arg( maxOpacity )
2594 const double base = json.
value( u
"base"_s, u
"1"_s ).toDouble();
2595 const QVariantList stops = json.value( u
"stops"_s ).toList();
2596 if ( stops.empty() )
2599 QString scaleExpression;
2600 if ( stops.size() <= 2 )
2602 scaleExpression = u
"array(%1,%2)"_s.arg(
interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(),
2603 stops.last().toList().value( 0 ).toDouble(),
2604 stops.value( 0 ).toList().value( 1 ).toList().value( 0 ),
2605 stops.last().toList().value( 1 ).toList().value( 0 ), base, multiplier, &context ),
2607 stops.last().toList().value( 0 ).toDouble(),
2608 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ),
2609 stops.last().toList().value( 1 ).toList().value( 1 ), base, multiplier, &context )
2614 scaleExpression =
parsePointStops( base, stops, context, multiplier );
2617 if ( !stops.empty() && defaultPoint )
2618 *defaultPoint = QPointF( stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble() * multiplier,
2619 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble() * multiplier );
2625 const QVariantMap &conversionMap, QString *defaultString )
2627 const QVariantList stops = json.
value( u
"stops"_s ).toList();
2628 if ( stops.empty() )
2631 const QString scaleExpression =
parseStringStops( stops, context, conversionMap, defaultString );
2638 QString caseString = u
"CASE "_s;
2640 for (
int i = 0; i < stops.length() - 1; ++i )
2643 const QVariant bz = stops.value( i ).toList().value( 0 );
2644 const QVariant bv = stops.value( i ).toList().value( 1 );
2645 if ( bv.userType() != QMetaType::Type::QVariantList && bv.userType() != QMetaType::Type::QStringList )
2647 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( bz.userType() ) ) ) );
2652 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2653 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2654 if ( tv.userType() != QMetaType::Type::QVariantList && tv.userType() != QMetaType::Type::QStringList )
2656 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( tz.userType() ) ) ) );
2660 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2661 "THEN array(%3,%4)" ).arg( bz.toString(),
2663 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 0 ), tv.toList().value( 0 ), base, multiplier, &context ),
2664 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 1 ), tv.toList().value( 1 ), base, multiplier, &context ) );
2666 caseString +=
"END"_L1;
2672 if ( stops.length() < 2 )
2675 QString caseString = u
"CASE"_s;
2677 for (
int i = 0; i < stops.length(); ++i )
2679 caseString +=
" WHEN "_L1;
2680 QStringList conditions;
2683 const QVariant bottomZoom = stops.value( i ).toList().value( 0 );
2684 conditions << u
"@vector_tile_zoom > %1"_s.arg( bottomZoom.toString() );
2686 if ( i < stops.length() - 1 )
2688 const QVariant topZoom = stops.value( i + 1 ).toList().value( 0 );
2689 conditions << u
"@vector_tile_zoom <= %1"_s.arg( topZoom.toString() );
2692 const QVariantList values = stops.value( i ).toList().value( 1 ).toList();
2693 QStringList valuesFixed;
2695 for (
const QVariant &value : values )
2697 const double number = value.toDouble( &ok );
2699 valuesFixed << QString::number( number * multiplier );
2703 caseString += u
"%1 THEN array(%3)"_s.arg(
2704 conditions.join(
" AND "_L1 ),
2705 valuesFixed.join(
',' )
2708 caseString +=
" END"_L1;
2714 QString caseString = u
"CASE "_s;
2716 for (
int i = 0; i < stops.length() - 1; ++i )
2719 const QVariant bz = stops.value( i ).toList().value( 0 );
2720 const QVariant bv = stops.value( i ).toList().value( 1 );
2721 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2723 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2728 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2729 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2730 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2732 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2736 const QString lowerComparator = i == 0 ? u
">="_s : u
">"_s;
2738 caseString += QStringLiteral(
"WHEN @vector_tile_zoom %1 %2 AND @vector_tile_zoom <= %3 "
2739 "THEN %4 " ).arg( lowerComparator,
2745 const QVariant z = stops.last().toList().value( 0 );
2746 const QVariant v = stops.last().toList().value( 1 );
2747 QString vStr = v.toString();
2748 if ( ( QMetaType::Type )v.userType() == QMetaType::QVariantList )
2751 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 "
2752 "THEN ( ( %2 ) * %3 ) END" ).arg( z.toString() ).arg( vStr ).arg( multiplier );
2756 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 "
2757 "THEN %2 END" ).arg( z.toString() ).arg( v.toDouble() * multiplier );
2765 QString caseString = u
"CASE "_s;
2767 for (
int i = 0; i < stops.length() - 1; ++i )
2770 const QVariant bz = stops.value( i ).toList().value( 0 );
2771 const QString bv = stops.value( i ).toList().value( 1 ).toString();
2772 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2774 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2779 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2780 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2782 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2786 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2787 "THEN %3 " ).arg( bz.toString(),
2791 caseString += u
"ELSE %1 END"_s.arg(
QgsExpression::quotedValue( conversionMap.value( stops.constLast().toList().value( 1 ).toString(),
2792 stops.constLast().toList().value( 1 ) ) ) );
2793 if ( defaultString )
2794 *defaultString = stops.constLast().toList().value( 1 ).toString();
2800 QString caseString = u
"CASE "_s;
2802 bool isExpression =
false;
2803 for (
int i = 0; i < stops.length() - 1; ++i )
2806 const QVariant bz = stops.value( i ).toList().value( 0 );
2807 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2809 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2814 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2815 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2817 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2821 QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
2822 if ( fieldPart.isEmpty() )
2823 fieldPart = u
"''"_s;
2824 else if ( !isExpression )
2827 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom < %2 "
2828 "THEN %3 " ).arg( bz.toString(),
2834 const QVariant bz = stops.constLast().toList().value( 0 );
2835 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2837 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2841 QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
2842 if ( fieldPart.isEmpty() )
2843 fieldPart = u
"''"_s;
2844 else if ( !isExpression )
2847 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 "
2848 "THEN %3 " ).arg( bz.toString(),
2852 QString defaultPart = processLabelField( stops.constFirst().toList().value( 1 ).toString(), isExpression );
2853 if ( defaultPart.isEmpty() )
2854 defaultPart = u
"''"_s;
2855 else if ( !isExpression )
2857 caseString += u
"ELSE %1 END"_s.arg( defaultPart );
2864 const QString method = json.
value( 0 ).toString();
2865 if ( method ==
"interpolate"_L1 )
2869 else if ( method ==
"match"_L1 )
2871 return parseMatchList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
2873 else if ( method ==
"step"_L1 )
2875 return parseStepList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
2885 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
2886 if ( attribute.isEmpty() )
2888 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
2892 QString caseString = u
"CASE "_s;
2894 for (
int i = 2; i < json.length() - 1; i += 2 )
2897 QVariant variantKeys = json.value( i );
2898 if ( variantKeys.userType() == QMetaType::Type::QVariantList || variantKeys.userType() == QMetaType::Type::QStringList )
2899 keys = variantKeys.toList();
2901 keys = {variantKeys};
2903 QStringList matchString;
2904 for (
const QVariant &key : keys )
2909 const QVariant value = json.value( i + 1 );
2911 QString valueString;
2916 if ( value.userType() == QMetaType::Type::QVariantList || value.userType() == QMetaType::Type::QStringList )
2922 const QColor color =
parseColor( value, context );
2930 const double v = value.toDouble() * multiplier;
2931 valueString = QString::number( v );
2937 const double v = value.toDouble() * maxOpacity;
2938 valueString = QString::number( v );
2944 valueString = u
"array(%1,%2)"_s.arg( value.toList().value( 0 ).toDouble() * multiplier,
2945 value.toList().value( 0 ).toDouble() * multiplier );
2951 if ( value.toList().count() == 2 && value.toList().first().toString() ==
"literal"_L1 )
2953 valueString = u
"array(%1)"_s.arg( value.toList().at( 1 ).toStringList().join(
',' ) );
2957 valueString = u
"array(%1)"_s.arg( value.toStringList().join(
',' ) );
2963 if ( matchString.count() == 1 )
2965 caseString += u
"WHEN %1 IS %2 THEN %3 "_s.arg( attribute, matchString.at( 0 ), valueString );
2969 caseString += u
"WHEN %1 IN (%2) THEN %3 "_s.arg( attribute, matchString.join(
',' ), valueString );
2973 QVariant lastValue = json.constLast();
2976 switch ( lastValue.userType() )
2978 case QMetaType::Type::QVariantList:
2979 case QMetaType::Type::QStringList:
2980 elseValue =
parseValueList( lastValue.toList(), type, context, multiplier, maxOpacity, defaultColor, defaultNumber ).
asExpression();
2989 const QColor color =
parseColor( lastValue, context );
2991 *defaultColor = color;
2999 const double v = json.constLast().toDouble() * multiplier;
3000 if ( defaultNumber )
3002 elseValue = QString::number( v );
3008 const double v = json.constLast().toDouble() * maxOpacity;
3009 if ( defaultNumber )
3011 elseValue = QString::number( v );
3017 elseValue = u
"array(%1,%2)"_s
3018 .arg( json.constLast().toList().value( 0 ).toDouble() * multiplier )
3019 .arg( json.constLast().toList().value( 0 ).toDouble() * multiplier );
3025 if ( json.constLast().toList().count() == 2 && json.constLast().toList().first().toString() ==
"literal"_L1 )
3027 elseValue = u
"array(%1)"_s.arg( json.constLast().toList().at( 1 ).toStringList().join(
',' ) );
3031 elseValue = u
"array(%1)"_s.arg( json.constLast().toStringList().join(
',' ) );
3041 caseString += u
"ELSE %1 END"_s.arg( elseValue );
3047 const QString expression =
parseExpression( json.value( 1 ).toList(), context );
3048 if ( expression.isEmpty() )
3050 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3054 QString caseString = u
"CASE "_s;
3057 for (
int i = json.length() - 2; i > 0; i -= 2 )
3059 const QVariant stepValue = json.value( i + 1 );
3061 QString valueString;
3062 if ( stepValue.canConvert<QVariantList>()
3074 const QColor color =
parseColor( stepValue, context );
3081 const double v = stepValue.toDouble() * multiplier;
3082 valueString = QString::number( v );
3088 const double v = stepValue.toDouble() * maxOpacity;
3089 valueString = QString::number( v );
3095 valueString = u
"array(%1,%2)"_s.arg(
3096 stepValue.toList().value( 0 ).toDouble() * multiplier ).arg(
3097 stepValue.toList().value( 0 ).toDouble() * multiplier
3104 if ( stepValue.toList().count() == 2 && stepValue.toList().first().toString() ==
"literal"_L1 )
3106 valueString = u
"array(%1)"_s.arg( stepValue.toList().at( 1 ).toStringList().join(
',' ) );
3110 valueString = u
"array(%1)"_s.arg( stepValue.toStringList().join(
',' ) );
3120 caseString += u
" WHEN %1 >= %2 THEN (%3) "_s.arg( expression, stepKey, valueString );
3124 caseString += u
"ELSE (%1) END"_s.arg( valueString );
3132 if ( json.value( 0 ).toString() !=
"interpolate"_L1 )
3134 context.
pushWarning( QObject::tr(
"%1: Could not interpret value list" ).arg( context.
layerId() ) );
3139 const QString technique = json.
value( 1 ).toList().value( 0 ).toString();
3140 if ( technique ==
"linear"_L1 )
3142 else if ( technique ==
"exponential"_L1 )
3143 base = json.value( 1 ).toList(). value( 1 ).toDouble();
3144 else if ( technique ==
"cubic-bezier"_L1 )
3146 context.
pushWarning( QObject::tr(
"%1: Cubic-bezier interpolation is not supported, linear used instead." ).arg( context.
layerId() ) );
3151 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation method %2" ).arg( context.
layerId(), technique ) );
3155 if ( json.value( 2 ).toList().value( 0 ).toString() !=
"zoom"_L1 )
3157 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation input %2" ).arg( context.
layerId(), json.value( 2 ).toString() ) );
3163 for (
int i = 3; i < json.length(); i += 2 )
3165 stops.push_back( QVariantList() << json.value( i ).toString() << json.value( i + 1 ) );
3169 props.insert( u
"stops"_s, stops );
3170 props.insert( u
"base"_s, base );
3186 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported numeric array in interpolate" ).arg( context.
layerId() ) );
3195 if ( ( QMetaType::Type )colorExpression.userType() == QMetaType::QVariantList )
3199 return parseValue( colorExpression, context,
true );
3204 if ( color.userType() != QMetaType::Type::QString )
3206 context.
pushWarning( QObject::tr(
"%1: Could not parse non-string color %2, skipping" ).arg( context.
layerId(), color.toString() ) );
3215 hue = std::max( 0, color.hslHue() );
3216 saturation = color.hslSaturation() / 255.0 * 100;
3217 lightness = color.lightness() / 255.0 * 100;
3218 alpha = color.alpha();
3226 context = *contextPtr;
3230 if ( valueMin.canConvert( QMetaType::Double ) && valueMax.canConvert( QMetaType::Double ) )
3232 bool minDoubleOk =
true;
3233 const double min = valueMin.toDouble( &minDoubleOk );
3234 bool maxDoubleOk =
true;
3235 const double max = valueMax.toDouble( &maxDoubleOk );
3236 if ( minDoubleOk && maxDoubleOk &&
qgsDoubleNear( min, max ) )
3238 return QString::number( min * multiplier );
3242 QString minValueExpr = valueMin.toString();
3243 QString maxValueExpr = valueMax.toString();
3244 if ( valueMin.userType() == QMetaType::Type::QVariantList )
3248 if ( valueMax.userType() == QMetaType::Type::QVariantList )
3254 if ( minValueExpr == maxValueExpr )
3256 expression = minValueExpr;
3262 expression = u
"scale_linear(@vector_tile_zoom,%1,%2,%3,%4)"_s.arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr );
3266 expression = u
"scale_exponential(@vector_tile_zoom,%1,%2,%3,%4,%5)"_s.arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr ).arg( base );
3270 if ( multiplier != 1 )
3271 return u
"(%1) * %2"_s.arg( expression ).arg( multiplier );
3278 if ( style ==
"round"_L1 )
3279 return Qt::RoundCap;
3280 else if ( style ==
"square"_L1 )
3281 return Qt::SquareCap;
3288 if ( style ==
"bevel"_L1 )
3289 return Qt::BevelJoin;
3290 else if ( style ==
"round"_L1 )
3291 return Qt::RoundJoin;
3293 return Qt::MiterJoin;
3298 QString op = expression.value( 0 ).toString();
3299 if ( ( op ==
"%"_L1 || op ==
"/"_L1 || op ==
"-"_L1 || op ==
"^"_L1 ) && expression.size() >= 3 )
3301 if ( expression.size() != 3 )
3303 context.
pushWarning( QObject::tr(
"%1: Operator %2 requires exactly two operands, skipping extra operands" ).arg( context.
layerId() ).arg( op ) );
3305 QString v1 = parseValue( expression.value( 1 ), context, colorExpected );
3306 QString v2 = parseValue( expression.value( 2 ), context, colorExpected );
3307 return u
"(%1 %2 %3)"_s.arg( v1, op, v2 );
3309 else if ( ( op ==
"*"_L1 || op ==
"+"_L1 ) && expression.size() >= 3 )
3311 QStringList operands;
3312 std::transform( std::next( expression.begin() ), expression.end(),
3313 std::back_inserter( operands ),
3314 [&context, colorExpected](
const QVariant & val )
3316 return parseValue( val, context, colorExpected );
3318 return u
"(%1)"_s.arg( operands.join( u
" %1 "_s.arg( op ) ) );
3320 else if ( op ==
"to-number"_L1 )
3322 return u
"to_real(%1)"_s.arg( parseValue( expression.value( 1 ), context ) );
3324 if ( op ==
"literal"_L1 )
3326 return expression.value( 1 ).toString();
3328 else if ( op ==
"all"_L1
3330 || op ==
"none"_L1 )
3333 for (
int i = 1; i < expression.size(); ++i )
3335 const QString part = parseValue( expression.at( i ), context );
3336 if ( part.isEmpty() )
3338 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3344 if ( op ==
"none"_L1 )
3345 return u
"NOT (%1)"_s.arg( parts.join(
") AND NOT ("_L1 ) );
3347 QString operatorString;
3348 if ( op ==
"all"_L1 )
3349 operatorString = u
") AND ("_s;
3350 else if ( op ==
"any"_L1 )
3351 operatorString = u
") OR ("_s;
3353 return u
"(%1)"_s.arg( parts.join( operatorString ) );
3355 else if ( op ==
'!' )
3358 QVariantList contraJsonExpr = expression.value( 1 ).toList();
3359 contraJsonExpr[0] = QString( op + contraJsonExpr[0].toString() );
3361 return parseKey( contraJsonExpr, context );
3363 else if ( op ==
"=="_L1
3371 if ( op ==
"=="_L1 )
3373 else if ( op ==
"!="_L1 )
3375 return u
"%1 %2 %3"_s.arg( parseKey( expression.value( 1 ), context ),
3376 op, parseValue( expression.value( 2 ), context ) );
3378 else if ( op ==
"has"_L1 )
3380 return parseKey( expression.value( 1 ), context ) + u
" IS NOT NULL"_s;
3382 else if ( op ==
"!has"_L1 )
3384 return parseKey( expression.value( 1 ), context ) + u
" IS NULL"_s;
3386 else if ( op ==
"in"_L1 || op ==
"!in"_L1 )
3388 const QString key = parseKey( expression.value( 1 ), context );
3391 QVariantList values = expression.mid( 2 );
3392 if ( expression.size() == 3
3393 && expression.at( 2 ).userType() == QMetaType::Type::QVariantList && expression.at( 2 ).toList().count() > 1
3394 && expression.at( 2 ).toList().at( 0 ).toString() ==
"literal"_L1 )
3396 values = expression.at( 2 ).toList().at( 1 ).toList();
3399 for (
const QVariant &value : std::as_const( values ) )
3401 const QString part = parseValue( value, context );
3402 if ( part.isEmpty() )
3404 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3410 if ( parts.size() == 1 )
3412 if ( op ==
"in"_L1 )
3413 return u
"%1 IS %2"_s.arg( key, parts.at( 0 ) );
3415 return u
"(%1 IS NULL OR %1 IS NOT %2)"_s.arg( key, parts.at( 0 ) );
3419 if ( op ==
"in"_L1 )
3420 return u
"%1 IN (%2)"_s.arg( key, parts.join(
", "_L1 ) );
3422 return u
"(%1 IS NULL OR %1 NOT IN (%2))"_s.arg( key, parts.join(
", "_L1 ) );
3425 else if ( op ==
"get"_L1 )
3427 return parseKey( expression.value( 1 ), context );
3429 else if ( op ==
"match"_L1 )
3431 const QString attribute = expression.value( 1 ).toList().value( 1 ).toString();
3433 if ( expression.size() == 5
3434 && expression.at( 3 ).userType() == QMetaType::Type::Bool && expression.at( 3 ).toBool() ==
true
3435 && expression.at( 4 ).userType() == QMetaType::Type::Bool && expression.at( 4 ).toBool() ==
false )
3438 if ( expression.at( 2 ).userType() == QMetaType::Type::QVariantList || expression.at( 2 ).userType() == QMetaType::Type::QStringList )
3441 for (
const QVariant &p : expression.at( 2 ).toList() )
3443 parts << parseValue( p, context );
3446 if ( parts.size() > 1 )
3451 else if ( expression.at( 2 ).userType() == QMetaType::Type::QString || expression.at( 2 ).userType() == QMetaType::Type::Int
3452 || expression.at( 2 ).userType() == QMetaType::Type::Double || expression.at( 2 ).userType() == QMetaType::Type::LongLong )
3458 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3464 QString caseString = u
"CASE "_s;
3465 for (
int i = 2; i < expression.size() - 2; i += 2 )
3467 if ( expression.at( i ).userType() == QMetaType::Type::QVariantList || expression.at( i ).userType() == QMetaType::Type::QStringList )
3470 for (
const QVariant &p : expression.at( i ).toList() )
3475 if ( parts.size() > 1 )
3480 else if ( expression.at( i ).userType() == QMetaType::Type::QString || expression.at( i ).userType() == QMetaType::Type::Int
3481 || expression.at( i ).userType() == QMetaType::Type::Double || expression.at( i ).userType() == QMetaType::Type::LongLong )
3486 caseString += u
"THEN %1 "_s.arg( parseValue( expression.at( i + 1 ), context, colorExpected ) );
3488 caseString += u
"ELSE %1 END"_s.arg( parseValue( expression.last(), context, colorExpected ) );
3492 else if ( op ==
"to-string"_L1 )
3494 return u
"to_string(%1)"_s.arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3496 else if ( op ==
"to-boolean"_L1 )
3498 return u
"to_bool(%1)"_s.arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3500 else if ( op ==
"case"_L1 )
3502 QString caseString = u
"CASE"_s;
3503 for (
int i = 1; i < expression.size() - 2; i += 2 )
3505 const QString condition =
parseExpression( expression.value( i ).toList(), context );
3506 const QString value = parseValue( expression.value( i + 1 ), context );
3507 caseString += u
" WHEN (%1) THEN %2"_s.arg( condition, value );
3509 const QString value = parseValue( expression.constLast(), context );
3510 caseString += u
" ELSE %1 END"_s.arg( value );
3513 else if ( op ==
"zoom"_L1 && expression.count() == 1 )
3515 return u
"@vector_tile_zoom"_s;
3517 else if ( op ==
"concat"_L1 )
3519 QString concatString = u
"concat("_s;
3520 for (
int i = 1; i < expression.size(); i++ )
3523 concatString +=
", "_L1;
3524 concatString += parseValue( expression.value( i ), context );
3526 concatString +=
')'_L1;
3527 return concatString;
3529 else if ( op ==
"length"_L1 )
3531 return u
"length(%1)"_s.arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3533 else if ( op ==
"step"_L1 )
3535 const QString stepExpression =
parseExpression( expression.value( 1 ).toList(), context );
3536 if ( stepExpression.isEmpty() )
3538 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3542 QString caseString = u
"CASE "_s;
3544 for (
int i = expression.length() - 2; i > 0; i -= 2 )
3546 const QString stepValue = parseValue( expression.value( i + 1 ), context, colorExpected );
3550 caseString += u
" WHEN %1 >= %2 THEN (%3) "_s.arg( stepExpression, stepKey, stepValue );
3554 caseString += u
"ELSE (%1) END"_s.arg( stepValue );
3561 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression \"%2\"" ).arg( context.
layerId(), op ) );
3570 QString actualName = name;
3571 const int categorySeparator = name.indexOf(
':' );
3572 if ( categorySeparator > 0 )
3574 category = name.left( categorySeparator );
3577 actualName = name.mid( categorySeparator + 1 );
3586 if ( category.isEmpty() )
3591 if ( spriteImage.isNull() )
3593 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3597 const QVariantMap spriteDefinition = context.
spriteDefinitions( category ).value( actualName ).toMap();
3598 if ( spriteDefinition.size() == 0 )
3600 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3604 const QImage sprite = spriteImage.copy( spriteDefinition.value( u
"x"_s ).toInt(),
3605 spriteDefinition.value( u
"y"_s ).toInt(),
3606 spriteDefinition.value( u
"width"_s ).toInt(),
3607 spriteDefinition.value( u
"height"_s ).toInt() );
3608 if ( sprite.isNull() )
3610 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3614 spriteSize = sprite.size() / spriteDefinition.value( u
"pixelRatio"_s ).toDouble() * context.
pixelSizeConversionFactor();
3622 auto prepareBase64 = [](
const QImage & sprite )
3625 if ( !sprite.isNull() )
3628 QBuffer buffer( &blob );
3629 buffer.open( QIODevice::WriteOnly );
3630 sprite.save( &buffer,
"PNG" );
3632 const QByteArray encoded = blob.toBase64();
3633 path = QString( encoded );
3634 path.prepend(
"base64:"_L1 );
3639 switch ( value.userType() )
3641 case QMetaType::Type::QString:
3643 QString spriteName = value.toString();
3644 const thread_local QRegularExpression fieldNameMatch( u
"{([^}]+)}"_s );
3645 QRegularExpressionMatch match = fieldNameMatch.match( spriteName );
3646 if ( match.hasMatch() )
3648 const QString fieldName = match.captured( 1 );
3649 spriteProperty = u
"CASE"_s;
3650 spriteSizeProperty = u
"CASE"_s;
3652 spriteName.replace(
"(",
"\\("_L1 );
3653 spriteName.replace(
")",
"\\)"_L1 );
3654 spriteName.replace( fieldNameMatch, u
"([^\\/\\\\]+)"_s );
3655 const QRegularExpression fieldValueMatch( spriteName );
3657 for (
const QString &name : spriteNames )
3659 match = fieldValueMatch.match( name );
3660 if ( match.hasMatch() )
3664 const QString fieldValue = match.captured( 1 );
3666 path = prepareBase64( sprite );
3667 if ( spritePath.isEmpty() && !path.isEmpty() )
3673 spriteProperty += u
" WHEN \"%1\" = '%2' THEN '%3'"_s
3674 .arg( fieldName, fieldValue, path );
3675 spriteSizeProperty += u
" WHEN \"%1\" = '%2' THEN %3"_s
3676 .arg( fieldName ).arg( fieldValue ).arg( size.width() );
3680 spriteProperty +=
" END"_L1;
3681 spriteSizeProperty +=
" END"_L1;
3685 spriteProperty.clear();
3686 spriteSizeProperty.clear();
3687 const QImage sprite =
retrieveSprite( spriteName, context, spriteSize );
3688 spritePath = prepareBase64( sprite );
3693 case QMetaType::Type::QVariantMap:
3695 const QVariantList stops = value.toMap().value( u
"stops"_s ).toList();
3696 if ( stops.size() == 0 )
3703 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, spriteSize );
3704 spritePath = prepareBase64( sprite );
3706 spriteProperty = u
"CASE WHEN @vector_tile_zoom < %1 THEN '%2'"_s
3707 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
3709 spriteSizeProperty = u
"CASE WHEN @vector_tile_zoom < %1 THEN %2"_s
3710 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
3711 .arg( spriteSize.width() );
3713 for (
int i = 0; i < stops.size() - 1; ++i )
3716 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, size );
3717 path = prepareBase64( sprite );
3719 spriteProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
3721 .arg( stops.value( i ).toList().value( 0 ).toString(),
3722 stops.value( i + 1 ).toList().value( 0 ).toString(),
3724 spriteSizeProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
3726 .arg( stops.value( i ).toList().value( 0 ).toString(),
3727 stops.value( i + 1 ).toList().value( 0 ).toString() )
3728 .arg( size.width() );
3730 sprite =
retrieveSprite( stops.last().toList().value( 1 ).toString(), context, size );
3731 path = prepareBase64( sprite );
3733 spriteProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
3735 .arg( stops.last().toList().value( 0 ).toString() )
3737 spriteSizeProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
3739 .arg( stops.last().toList().value( 0 ).toString() )
3740 .arg( size.width() );
3744 case QMetaType::Type::QVariantList:
3746 const QVariantList json = value.toList();
3747 const QString method = json.value( 0 ).toString();
3749 if ( method ==
"match"_L1 )
3751 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
3752 if ( attribute.isEmpty() )
3754 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
3758 spriteProperty = u
"CASE"_s;
3759 spriteSizeProperty = u
"CASE"_s;
3761 for (
int i = 2; i < json.length() - 1; i += 2 )
3763 const QVariant matchKey = json.value( i );
3764 const QVariant matchValue = json.value( i + 1 );
3765 QString matchString;
3766 switch ( matchKey.userType() )
3768 case QMetaType::Type::QVariantList:
3769 case QMetaType::Type::QStringList:
3771 const QVariantList keys = matchKey.toList();
3772 QStringList matchStringList;
3773 for (
const QVariant &key : keys )
3777 matchString = matchStringList.join(
',' );
3781 case QMetaType::Type::Bool:
3782 case QMetaType::Type::QString:
3783 case QMetaType::Type::Int:
3784 case QMetaType::Type::LongLong:
3785 case QMetaType::Type::Double:
3792 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported sprite type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( value.userType() ) ) ) );
3797 const QImage sprite =
retrieveSprite( matchValue.toString(), context, spriteSize );
3798 spritePath = prepareBase64( sprite );
3800 spriteProperty += QStringLiteral(
" WHEN %1 IN (%2) "
3801 "THEN '%3'" ).arg( attribute,
3805 spriteSizeProperty += QStringLiteral(
" WHEN %1 IN (%2) "
3806 "THEN %3" ).arg( attribute,
3807 matchString ).arg( spriteSize.width() );
3810 if ( !json.constLast().toString().isEmpty() )
3812 const QImage sprite =
retrieveSprite( json.constLast().toString(), context, spriteSize );
3813 spritePath = prepareBase64( sprite );
3817 spritePath = QString();
3820 spriteProperty += u
" ELSE '%1' END"_s.arg( spritePath );
3821 spriteSizeProperty += u
" ELSE %3 END"_s.arg( spriteSize.width() );
3824 else if ( method ==
"step"_L1 )
3826 const QString expression =
parseExpression( json.value( 1 ).toList(), context );
3827 if ( expression.isEmpty() )
3829 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3833 spriteProperty = u
"CASE"_s;
3834 spriteSizeProperty = u
"CASE"_s;
3835 for (
int i = json.length() - 2; i > 2; i -= 2 )
3838 const QString stepValue = json.value( i + 1 ).toString();
3840 const QImage sprite =
retrieveSprite( stepValue, context, spriteSize );
3841 spritePath = prepareBase64( sprite );
3843 spriteProperty += u
" WHEN %1 >= %2 THEN '%3' "_s.arg( expression, stepKey, spritePath );
3844 spriteSizeProperty += u
" WHEN %1 >= %2 THEN %3 "_s.arg( expression ).arg( stepKey ).arg( spriteSize.width() );
3847 const QImage sprite =
retrieveSprite( json.at( 2 ).toString(), context, spriteSize );
3848 spritePath = prepareBase64( sprite );
3850 spriteProperty += u
"ELSE '%1' END"_s.arg( spritePath );
3851 spriteSizeProperty += u
"ELSE %3 END"_s.arg( spriteSize.width() );
3854 else if ( method ==
"case"_L1 )
3856 spriteProperty = u
"CASE"_s;
3857 spriteSizeProperty = u
"CASE"_s;
3858 for (
int i = 1; i < json.length() - 2; i += 2 )
3860 const QString caseExpression =
parseExpression( json.value( i ).toList(), context );
3861 const QString caseValue = json.value( i + 1 ).toString();
3863 const QImage sprite =
retrieveSprite( caseValue, context, spriteSize );
3864 spritePath = prepareBase64( sprite );
3866 spriteProperty += u
" WHEN %1 THEN '%2' "_s.arg( caseExpression, spritePath );
3867 spriteSizeProperty += u
" WHEN %1 THEN %2 "_s.arg( caseExpression ).arg( spriteSize.width() );
3869 const QImage sprite =
retrieveSprite( json.last().toString(), context, spriteSize );
3870 spritePath = prepareBase64( sprite );
3872 spriteProperty += u
"ELSE '%1' END"_s.arg( spritePath );
3873 spriteSizeProperty += u
"ELSE %3 END"_s.arg( spriteSize.width() );
3878 context.
pushWarning( QObject::tr(
"%1: Could not interpret sprite value list with method %2" ).arg( context.
layerId(), method ) );
3884 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported sprite type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( value.userType() ) ) ) );
3894 switch ( value.userType() )
3896 case QMetaType::Type::QVariantList:
3897 case QMetaType::Type::QStringList:
3900 case QMetaType::Type::Bool:
3901 case QMetaType::Type::QString:
3902 if ( colorExpected )
3907 return parseValue(
c, context );
3912 case QMetaType::Type::Int:
3913 case QMetaType::Type::LongLong:
3914 case QMetaType::Type::Double:
3915 return value.toString();
3917 case QMetaType::Type::QColor:
3918 c = value.value<QColor>();
3919 return QString(
"color_rgba(%1,%2,%3,%4)" ).arg(
c.red() ).arg(
c.green() ).arg(
c.blue() ).arg(
c.alpha() );
3922 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression part" ).arg( context.
layerId() ) );
3930 if ( value.toString() ==
"$type"_L1 )
3932 return u
"_geom_type"_s;
3934 if ( value.toString() ==
"level"_L1 )
3938 else if ( ( value.userType() == QMetaType::Type::QVariantList && value.toList().size() == 1 ) || value.userType() == QMetaType::Type::QStringList )
3940 if ( value.toList().size() > 1 )
3941 return value.toList().at( 1 ).toString();
3944 QString valueString = value.toList().value( 0 ).toString();
3945 if ( valueString ==
"geometry-type"_L1 )
3947 return u
"_geom_type"_s;
3952 else if ( value.userType() == QMetaType::Type::QVariantList && value.toList().size() > 1 )
3959QString QgsMapBoxGlStyleConverter::processLabelField(
const QString &
string,
bool &isExpression )
3963 const thread_local QRegularExpression singleFieldRx( u
"^{([^}]+)}$"_s );
3964 const QRegularExpressionMatch match = singleFieldRx.match(
string );
3965 if ( match.hasMatch() )
3967 isExpression =
false;
3968 return match.captured( 1 );
3971 const thread_local QRegularExpression multiFieldRx( u
"(?={[^}]+})"_s );
3972 const QStringList parts =
string.split( multiFieldRx );
3973 if ( parts.size() > 1 )
3975 isExpression =
true;
3978 for (
const QString &part : parts )
3980 if ( part.isEmpty() )
3983 if ( !part.contains(
'{' ) )
3990 const QStringList split = part.split(
'}' );
3992 if ( !split.at( 1 ).isEmpty() )
3995 return u
"concat(%1)"_s.arg( res.join(
',' ) );
3999 isExpression =
false;
4006 return mRenderer ? mRenderer->
clone() :
nullptr;
4011 return mLabeling ? mLabeling->
clone() :
nullptr;
4021 return mRasterSubLayers;
4026 QList<QgsMapLayer *> subLayers;
4029 const QString sourceName = subLayer.source();
4030 std::unique_ptr< QgsRasterLayer > rl;
4037 rl->pipe()->setDataDefinedProperties( subLayer.dataDefinedProperties() );
4044 subLayers.append( rl.release() );
4053 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
4056 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
4057 context = tmpContext.get();
4062 if (
string.compare(
"vector"_L1, Qt::CaseInsensitive ) == 0 )
4064 else if (
string.compare(
"raster"_L1, Qt::CaseInsensitive ) == 0 )
4066 else if (
string.compare(
"raster-dem"_L1, Qt::CaseInsensitive ) == 0 )
4068 else if (
string.compare(
"geojson"_L1, Qt::CaseInsensitive ) == 0 )
4070 else if (
string.compare(
"image"_L1, Qt::CaseInsensitive ) == 0 )
4072 else if (
string.compare(
"video"_L1, Qt::CaseInsensitive ) == 0 )
4074 context->
pushWarning( QObject::tr(
"Invalid source type \"%1\" for source \"%2\"" ).arg(
string, name ) );
4080 const QString name = it.key();
4081 const QVariantMap jsonSource = it.value().toMap();
4082 const QString typeString = jsonSource.value( u
"type"_s ).toString();
4105 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
4108 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
4109 context = tmpContext.get();
4112 auto raster = std::make_unique< QgsMapBoxGlStyleRasterSource >( name );
4113 if ( raster->setFromJson( source, context ) )
4114 mSources.append( raster.release() );
4117bool QgsMapBoxGlStyleConverter::numericArgumentsOnly(
const QVariant &bottomVariant,
const QVariant &topVariant,
double &bottom,
double &top )
4119 if ( bottomVariant.canConvert( QMetaType::Double ) && topVariant.canConvert( QMetaType::Double ) )
4121 bool bDoubleOk, tDoubleOk;
4122 bottom = bottomVariant.toDouble( &bDoubleOk );
4123 top = topVariant.toDouble( &tDoubleOk );
4124 return ( bDoubleOk && tDoubleOk );
4135 mWarnings << warning;
4150 return mSizeConversionFactor;
4155 mSizeConversionFactor = sizeConversionFactor;
4160 return mSpriteImage.keys();
4165 return mSpriteImage.contains( category ) ? mSpriteImage[category] : QImage();
4170 return mSpriteDefinitions.contains( category ) ? mSpriteDefinitions[category] : QVariantMap();
4175 mSpriteImage[category] = image;
4176 mSpriteDefinitions[category] = definitions;
4226 mAttribution = json.value( u
"attribution"_s ).toString();
4228 const QString scheme = json.value( u
"scheme"_s, u
"xyz"_s ).toString();
4229 if ( scheme.compare(
"xyz"_L1 ) == 0 )
4235 context->
pushWarning( QObject::tr(
"%1 scheme is not supported for raster source %2" ).arg( scheme,
name() ) );
4239 mMinZoom = json.value( u
"minzoom"_s, u
"0"_s ).toInt();
4240 mMaxZoom = json.value( u
"maxzoom"_s, u
"22"_s ).toInt();
4241 mTileSize = json.value( u
"tileSize"_s, u
"512"_s ).toInt();
4243 const QVariantList
tiles = json.value( u
"tiles"_s ).toList();
4244 for (
const QVariant &tile :
tiles )
4246 mTiles.append( tile.toString() );
4255 parts.insert( u
"type"_s, u
"xyz"_s );
4256 parts.insert( u
"url"_s, mTiles.value( 0 ) );
4258 if ( mTileSize == 256 )
4259 parts.insert( u
"tilePixelRation"_s, u
"1"_s );
4260 else if ( mTileSize == 512 )
4261 parts.insert( u
"tilePixelRation"_s, u
"2"_s );
4263 parts.insert( u
"zmax"_s, QString::number( mMaxZoom ) );
4264 parts.insert( u
"zmin"_s, QString::number( mMinZoom ) );
4267 return rl.release();
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
@ OnLine
Labels can be placed directly over a line feature.
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
@ CentralPoint
Place symbols at the mid point of the line.
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ Curved
Arranges candidates following the curvature of a line feature. Applies to line layers only.
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
@ FollowPlacement
Alignment follows placement of label, e.g., labels to the left of a feature will be drawn with right ...
RenderUnit
Rendering size units.
MapBoxGlStyleSourceType
Available MapBox GL style source types.
@ RasterDem
Raster DEM source.
@ Unknown
Other/unknown source type.
@ Viewport
Relative to the whole viewport/output device.
@ AllowOverlapAtNoCost
Labels may freely overlap other labels, at no cost.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the pen join style used to render the line (e.g.
void setPenCapStyle(Qt::PenCapStyle style)
Sets the pen cap style used to render the line (e.g.
static QgsFontManager * fontManager()
Returns the application font manager, which manages available fonts and font installation for the QGI...
A paint effect which blurs a source picture, using a number of different blur methods.
void setBlurUnit(const Qgis::RenderUnit unit)
Sets the units used for the blur level (radius).
@ StackBlur
Stack blur, a fast but low quality blur. Valid blur level values are between 0 - 16.
void setBlurMethod(const BlurMethod method)
Sets the blur method (algorithm) to use for performing the blur.
void setBlurLevel(const double level)
Sets blur level (radius).
A paint effect which consists of a stack of other chained paint effects.
void appendEffect(QgsPaintEffect *effect)
Appends an effect to the end of the stack.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes).
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
Create an expression allowing to evaluate if a field is equal to a value.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes).
QString processFontFamilyName(const QString &name) const
Processes a font family name, applying any matching fontFamilyReplacements() to the name.
static QFont createFont(const QString &family, int pointSize=-1, int weight=-1, bool italic=false)
Creates a font with the specified family.
static bool fontFamilyHasStyle(const QString &family, const QString &style)
Check whether font family on system has specific style.
static QVariant parseJson(const std::string &jsonString)
Converts JSON jsonString to a QVariant, in case of parsing error an invalid QVariant is returned and ...
void setPlacementFlags(Qgis::LabelLinePlacementFlags flags)
Returns the line placement flags, which dictate how line labels can be placed above or below the line...
void setFactor(double factor)
Sets the obstacle factor, where 1.0 = default, < 1.0 more likely to be covered by labels,...
Contains general settings related to how labels are placed.
void setOverlapHandling(Qgis::LabelOverlapHandling handling)
Sets the technique used to handle overlapping labels.
void setAllowDegradedPlacement(bool allow)
Sets whether labels can be placed in inferior fallback positions if they cannot otherwise be placed.
void setQuadrant(Qgis::LabelQuadrantPosition quadrant)
Sets the quadrant in which to offset labels from the point.
virtual void setWidth(double width)
Sets the width of the line symbol layer.
void setOffset(double offset)
Sets the line's offset.
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the unit for the line's offset.
Abstract base class for MapBox GL style sources.
QString name() const
Returns the source's name.
virtual ~QgsMapBoxGlStyleAbstractSource()
QgsMapBoxGlStyleAbstractSource(const QString &name)
Constructor for QgsMapBoxGlStyleAbstractSource.
Context for a MapBox GL style conversion operation.
void setLayerId(const QString &value)
Sets the layer ID of the layer currently being converted.
QStringList warnings() const
Returns a list of warning messages generated during the conversion.
void pushWarning(const QString &warning)
Pushes a warning message generated during the conversion.
QImage spriteImage(const QString &category=QString()) const
Returns the sprite image for a given category to use during conversion, or an invalid image if this i...
double pixelSizeConversionFactor() const
Returns the pixel size conversion factor, used to scale the original pixel sizes when converting styl...
void setTargetUnit(Qgis::RenderUnit targetUnit)
Sets the target unit type.
void setSprites(const QImage &image, const QVariantMap &definitions, const QString &category=QString())
Sets the sprite image and definitions JSON for a given category to use during conversion.
void setPixelSizeConversionFactor(double sizeConversionFactor)
Sets the pixel size conversion factor, used to scale the original pixel sizes when converting styles.
QVariantMap spriteDefinitions(const QString &category=QString()) const
Returns the sprite definitions for a given category to use during conversion.
Qgis::RenderUnit targetUnit() const
Returns the target unit type.
QString layerId() const
Returns the layer ID of the layer currently being converted.
QStringList spriteCategories() const
Returns the list of sprite categories to use during conversion, or an empty list of none is set.
void clearWarnings()
Clears the list of warning messages.
static QString parseOpacityStops(double base, const QVariantList &stops, int maxOpacity, QgsMapBoxGlStyleConversionContext &context)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate alpha ...
static QString parseColorExpression(const QVariant &colorExpression, QgsMapBoxGlStyleConversionContext &context)
Converts an expression representing a color to a string (can be color string or an expression where a...
static QgsProperty parseInterpolateOpacityByZoom(const QVariantMap &json, int maxOpacity, QgsMapBoxGlStyleConversionContext *contextPtr=nullptr)
Interpolates opacity with either scale_linear() or scale_exp() (depending on base value).
static QString parseStops(double base, const QVariantList &stops, double multiplier, QgsMapBoxGlStyleConversionContext &context)
Parses a list of interpolation stops.
QgsVectorTileRenderer * renderer() const
Returns a new instance of a vector tile renderer representing the converted style,...
static QString parseExpression(const QVariantList &expression, QgsMapBoxGlStyleConversionContext &context, bool colorExpected=false)
Converts a MapBox GL expression to a QGIS expression.
PropertyType
Property types, for interpolated value conversion.
@ Point
Point/offset property.
@ Numeric
Numeric property (e.g. line width, text size).
@ Opacity
Opacity property.
@ NumericArray
Numeric array for dash arrays or such.
QList< QgsMapBoxGlStyleAbstractSource * > sources()
Returns the list of converted sources.
QgsVectorTileLabeling * labeling() const
Returns a new instance of a vector tile labeling representing the converted style,...
QList< QgsMapBoxGlStyleRasterSubLayer > rasterSubLayers() const
Returns a list of raster sub layers contained in the style.
static QgsProperty parseInterpolateByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, double *defaultNumber=nullptr)
Parses a numeric value which is interpolated by zoom range.
static Qt::PenJoinStyle parseJoinStyle(const QString &style)
Converts a value to Qt::PenJoinStyle enum from JSON value.
static QgsProperty parseInterpolateStringByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, const QVariantMap &conversionMap, QString *defaultString=nullptr)
Interpolates a string by zoom.
static QgsProperty parseStepList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a match function value list.
static QgsProperty parseInterpolatePointByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, QPointF *defaultPoint=nullptr)
Interpolates a point/offset with either scale_linear() or scale_exp() (depending on base value).
static bool parseCircleLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context)
Parses a circle layer.
Result convert(const QVariantMap &style, QgsMapBoxGlStyleConversionContext *context=nullptr)
Converts a JSON style map, and returns the resultant status of the conversion.
static QgsProperty parseInterpolateListByZoom(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Interpolates a list which starts with the interpolate function.
~QgsMapBoxGlStyleConverter()
QList< QgsMapLayer * > createSubLayers() const
Returns a list of new map layers corresponding to sublayers of the style, e.g.
Result
Result of conversion.
@ Success
Conversion was successful.
@ NoLayerList
No layer list was found in JSON input.
QgsMapBoxGlStyleConverter()
Constructor for QgsMapBoxGlStyleConverter.
static QImage retrieveSprite(const QString &name, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize)
Retrieves the sprite image with the specified name, taken from the specified context.
static QString parseLabelStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context)
Parses a list of interpolation stops containing label values.
void parseLayers(const QVariantList &layers, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse list of layers from JSON.
static QgsProperty parseInterpolateColorByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, QColor *defaultColor=nullptr)
Parses a color value which is interpolated by zoom range.
static QString retrieveSpriteAsBase64WithProperties(const QVariant &value, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize, QString &spriteProperty, QString &spriteSizeProperty)
Retrieves the sprite image with the specified name, taken from the specified context as a base64 enco...
void parseSources(const QVariantMap &sources, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse list of sources from JSON.
static QString interpolateExpression(double zoomMin, double zoomMax, QVariant valueMin, QVariant valueMax, double base, double multiplier=1, QgsMapBoxGlStyleConversionContext *contextPtr=nullptr)
Generates an interpolation for values between valueMin and valueMax, scaled between the ranges zoomMi...
static QColor parseColor(const QVariant &color, QgsMapBoxGlStyleConversionContext &context)
Parses a color in one of these supported formats:
static bool parseSymbolLayerAsRenderer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as a renderer.
static bool parseFillLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context, bool isBackgroundStyle=false)
Parses a fill layer.
static void parseSymbolLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, bool &hasRenderer, QgsVectorTileBasicLabelingStyle &labelingStyle, bool &hasLabeling, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as renderer or labeling.
static bool parseLineLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context)
Parses a line layer.
void parseRasterSource(const QVariantMap &source, const QString &name, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse a raster source from JSON.
static void colorAsHslaComponents(const QColor &color, int &hue, int &saturation, int &lightness, int &alpha)
Takes a QColor object and returns HSLA components in required format for QGIS color_hsla() expression...
static Qt::PenCapStyle parseCapStyle(const QString &style)
Converts a value to Qt::PenCapStyle enum from JSON value.
static QString parseStringStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, const QVariantMap &conversionMap, QString *defaultString=nullptr)
Parses a list of interpolation stops containing string values.
static QgsProperty parseMatchList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a match function value list.
static QgsProperty parseValueList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a value list (e.g.
static QString parseArrayStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier=1)
Takes numerical arrays from stops.
static QString parsePointStops(double base, const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier=1)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate point/...
Encapsulates a MapBox GL style raster source.
Qgis::MapBoxGlStyleSourceType type() const override
Returns the source type.
QgsMapBoxGlStyleRasterSource(const QString &name)
Constructor for QgsMapBoxGlStyleRasterSource.
QgsRasterLayer * toRasterLayer() const
Returns a new raster layer representing the raster source, or nullptr if the source cannot be represe...
bool setFromJson(const QVariantMap &json, QgsMapBoxGlStyleConversionContext *context) override
Sets the source's state from a json map.
QStringList tiles() const
Returns the list of tile sources.
Encapsulates a MapBox GL style raster sub layer.
QString source() const
Returns the layer's source.
QString id() const
Returns the layer's ID.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the layer's data defined properties.
QgsMapBoxGlStyleRasterSubLayer(const QString &id, const QString &source)
Constructor for QgsMapBoxGlStyleRasterSubLayer, with the given id and source.
Line symbol layer type which draws repeating marker symbols along a line feature.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
virtual void setSize(double size)
Sets the symbol size.
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's offset.
void setAngle(double angle)
Sets the rotation angle for the marker.
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's size.
A marker symbol type, for rendering Point and MultiPoint geometries.
void setEnabled(bool enabled)
Sets whether the effect is enabled.
Contains settings for how a map layer will be labeled.
double yOffset
Vertical offset of label.
const QgsLabelObstacleSettings & obstacleSettings() const
Returns the label obstacle settings.
const QgsLabelPlacementSettings & placementSettings() const
Returns the label placement settings.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
double xOffset
Horizontal offset of label.
Qgis::LabelPlacement placement
Label placement mode.
Qgis::LabelMultiLineAlignment multilineAlign
Horizontal alignment of multi-line labels.
int priority
Label priority.
double angleOffset
Label rotation, in degrees clockwise.
Qgis::RenderUnit offsetUnits
Units for offsets of label.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the label's property collection, used for data defined overrides.
bool isExpression
true if this label is made from a expression string, e.g., FieldName || 'mm'
void setPlacementSettings(const QgsLabelPlacementSettings &settings)
Sets the label placement settings.
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
double dist
Distance from feature to the label.
Qgis::RenderUnit distUnits
Units the distance from feature to the label.
@ LinePlacementOptions
Line placement flags.
@ LabelRotation
Label rotation.
@ FontStyle
Font style name.
@ FontLetterSpacing
Letter spacing.
QString fieldName
Name of field (or an expression) to use for label text.
int autoWrapLength
If non-zero, indicates that label text should be automatically wrapped to (ideally) the specified num...
const QgsLabelPointSettings & pointSettings() const
Returns the label point settings, which contain settings related to how the label engine places and f...
A grouped map of multiple QgsProperty objects, each referenced by an integer key value.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const final
Returns the calculated value of the property with the specified key from within the collection.
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
QgsProperty property(int key) const final
Returns a matching property from the collection, if one exists.
A store for object properties.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
QString expressionString() const
Returns the expression used for the property value.
QVariant value(const QgsExpressionContext &context, const QVariant &defaultValue=QVariant(), bool *ok=nullptr) const
Calculates the current value of the property, including any transforms which are set for the property...
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
void setExpressionString(const QString &expression)
Sets the expression to use for the property value.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
A fill symbol layer which fills polygons with a repeated raster image.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the unit for the image's width and height.
void setOpacity(double opacity)
Sets the opacity for the raster image used in the fill.
void setImageFilePath(const QString &imagePath)
Sets the path to the raster image used for the fill.
void setWidth(double width)
Sets the width for scaling the image used in the fill.
void setCoordinateMode(Qgis::SymbolCoordinateReference mode)
Set the coordinate mode for fill.
Represents a raster layer.
Line symbol layer type which draws line sections using a raster image file.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Raster marker symbol layer class.
void setOpacity(double opacity)
Set the marker opacity.
void setPath(const QString &path)
Set the marker raster image path.
@ RendererOpacity
Raster renderer global opacity.
Renders polygons using a single fill and stroke color.
void setBrushStyle(Qt::BrushStyle style)
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setStrokeWidth(double strokeWidth)
void setStrokeStyle(Qt::PenStyle strokeStyle)
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the unit for the fill's offset.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
void setOffset(QPointF offset)
Sets an offset by which polygons will be translated during rendering.
void setStrokeColor(const QColor &strokeColor) override
Sets the stroke color for the symbol layer.
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
void setPenCapStyle(Qt::PenCapStyle style)
Sets the pen cap style used to render the line (e.g.
void setUseCustomDashPattern(bool b)
Sets whether the line uses a custom dash pattern.
void setCustomDashVector(const QVector< qreal > &vector)
Sets the custom dash vector, which is the pattern of alternating drawn/skipped lengths used while ren...
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the pen join style used to render the line (e.g.
Simple marker symbol layer, consisting of a rendered shape with solid fill color and a stroke.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
void setStrokeWidthUnit(Qgis::RenderUnit u)
Sets the unit for the width of the marker's stroke.
void setStrokeWidth(double w)
Sets the width of the marker's stroke.
void setStrokeColor(const QColor &color) override
Sets the marker's stroke color.
static QColor parseColor(const QString &colorStr, bool strictEval=false)
Attempts to parse a string as a color using a variety of common formats, including hex codes,...
@ File
Filename, eg for svg files.
@ CustomDash
Custom dash pattern.
@ Name
Name, eg shape name for simple markers.
@ StrokeColor
Stroke color.
@ Interval
Line marker interval.
@ StrokeWidth
Stroke width.
@ LayerEnabled
Whether symbol layer is enabled.
virtual void setColor(const QColor &color)
Sets the "representative" color for the symbol layer.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the symbol layer's property collection, used for data defined overrides.
void setPlacements(Qgis::MarkerLinePlacements placements)
Sets the placement of the symbols.
Container for settings relating to a text background object.
void setMarkerSymbol(QgsMarkerSymbol *symbol)
Sets the current marker symbol for the background shape.
void setSizeType(SizeType type)
Sets the method used to determine the size of the background shape (e.g., fixed size or buffer around...
@ ShapeMarkerSymbol
Marker symbol.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units used for the shape's size.
void setType(ShapeType type)
Sets the type of background shape to draw (e.g., square, ellipse, SVG).
void setEnabled(bool enabled)
Sets whether the text background will be drawn.
void setSize(QSizeF size)
Sets the size of the background shape.
void setColor(const QColor &color)
Sets the color for the buffer.
void setOpacity(double opacity)
Sets the buffer opacity.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units used for the buffer size.
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the buffer.
void setSize(double size)
Sets the size of the buffer.
Container for all settings relating to text rendering.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void setSize(double size)
Sets the size for rendered text.
void setFont(const QFont &font)
Sets the font used for rendering text.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the size of rendered text.
void setBackground(const QgsTextBackgroundSettings &backgroundSettings)
Sets the text's background settings.q.
void setNamedStyle(const QString &style)
Sets the named style for the font used for rendering text.
QFont font() const
Returns the font used for rendering text.
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
Configuration of a single style within QgsVectorTileBasicLabeling.
void setLayerName(const QString &name)
Sets name of the sub-layer to render (empty layer means that all layers match).
void setMinZoomLevel(int minZoom)
Sets minimum zoom level index (negative number means no limit).
void setFilterExpression(const QString &expr)
Sets filter expression (empty filter means that all features match).
void setMaxZoomLevel(int maxZoom)
Sets maximum zoom level index (negative number means no limit).
void setStyleName(const QString &name)
Sets human readable name of this style.
void setGeometryType(Qgis::GeometryType geomType)
Sets type of the geometry that will be used (point / line / polygon).
void setLabelSettings(const QgsPalLayerSettings &settings)
Sets labeling configuration of this style.
void setEnabled(bool enabled)
Sets whether this style is enabled (used for rendering).
Definition of map rendering of a subset of vector tile data.
void setEnabled(bool enabled)
Sets whether this style is enabled (used for rendering).
void setMinZoomLevel(int minZoom)
Sets minimum zoom level index (negative number means no limit).
void setLayerName(const QString &name)
Sets name of the sub-layer to render (empty layer means that all layers match).
void setFilterExpression(const QString &expr)
Sets filter expression (empty filter means that all features match).
void setSymbol(QgsSymbol *sym)
Sets symbol for rendering. Takes ownership of the symbol.
void setStyleName(const QString &name)
Sets human readable name of this style.
void setMaxZoomLevel(int maxZoom)
Sets maximum zoom level index (negative number means no limit).
void setGeometryType(Qgis::GeometryType geomType)
Sets type of the geometry that will be used (point / line / polygon).
Base class for labeling configuration classes for vector tile layers.
virtual QgsVectorTileLabeling * clone() const =0SIP_FACTORY
Returns a new copy of the object.
Abstract base class for all vector tile renderer implementations.
virtual QgsVectorTileRenderer * clone() const =0
Returns a clone of the renderer.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
#define QgsDebugError(str)
QList< QgsSymbolLayer * > QgsSymbolLayerList