47#include <QRegularExpression>
58 if ( style.contains( QStringLiteral(
"sources" ) ) )
60 parseSources( style.value( QStringLiteral(
"sources" ) ).toMap(), context );
63 if ( style.contains( QStringLiteral(
"layers" ) ) )
65 parseLayers( style.value( QStringLiteral(
"layers" ) ).toList(), context );
69 mError = QObject::tr(
"Could not find layers list in JSON" );
82 qDeleteAll( mSources );
87 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
90 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
91 context = tmpContext.get();
94 QList<QgsVectorTileBasicRendererStyle> rendererStyles;
95 QList<QgsVectorTileBasicLabelingStyle> labelingStyles;
98 bool hasRendererBackgroundStyle =
false;
100 for (
const QVariant &layer : layers )
102 const QVariantMap jsonLayer = layer.toMap();
104 const QString layerType = jsonLayer.value( QStringLiteral(
"type" ) ).toString();
105 if ( layerType == QLatin1String(
"background" ) )
107 hasRendererBackgroundStyle =
parseFillLayer( jsonLayer, rendererBackgroundStyle, *context,
true );
108 if ( hasRendererBackgroundStyle )
118 const QString styleId = jsonLayer.value( QStringLiteral(
"id" ) ).toString();
121 if ( layerType.compare( QLatin1String(
"raster" ), Qt::CaseInsensitive ) == 0 )
124 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
125 if ( jsonPaint.contains( QStringLiteral(
"raster-opacity" ) ) )
127 const QVariant jsonRasterOpacity = jsonPaint.value( QStringLiteral(
"raster-opacity" ) );
128 double defaultOpacity = 1;
132 mRasterSubLayers.append( raster );
136 const QString layerName = jsonLayer.value( QStringLiteral(
"source-layer" ) ).toString();
138 const int minZoom = jsonLayer.value( QStringLiteral(
"minzoom" ), QStringLiteral(
"-1" ) ).toInt();
147 int maxZoom = jsonLayer.value( QStringLiteral(
"maxzoom" ), QStringLiteral(
"-1" ) ).toInt();
151 const bool enabled = jsonLayer.value( QStringLiteral(
"visibility" ) ).toString() != QLatin1String(
"none" );
153 QString filterExpression;
154 if ( jsonLayer.contains( QStringLiteral(
"filter" ) ) )
156 filterExpression =
parseExpression( jsonLayer.value( QStringLiteral(
"filter" ) ).toList(), *context );
162 bool hasRendererStyle =
false;
163 bool hasLabelingStyle =
false;
164 if ( layerType == QLatin1String(
"fill" ) )
166 hasRendererStyle =
parseFillLayer( jsonLayer, rendererStyle, *context );
168 else if ( layerType == QLatin1String(
"line" ) )
170 hasRendererStyle =
parseLineLayer( jsonLayer, rendererStyle, *context );
172 else if ( layerType == QLatin1String(
"circle" ) )
176 else if ( layerType == QLatin1String(
"symbol" ) )
178 parseSymbolLayer( jsonLayer, rendererStyle, hasRendererStyle, labelingStyle, hasLabelingStyle, *context );
182 mWarnings << QObject::tr(
"%1: Skipping unknown layer type %2" ).arg( context->
layerId(), layerType );
187 if ( hasRendererStyle )
195 rendererStyles.append( rendererStyle );
198 if ( hasLabelingStyle )
206 labelingStyles.append( labelingStyle );
209 mWarnings.append( context->
warnings() );
213 if ( hasRendererBackgroundStyle )
214 rendererStyles.prepend( rendererBackgroundStyle );
216 mRenderer = std::make_unique< QgsVectorTileBasicRenderer >();
218 renderer->setStyles( rendererStyles );
220 mLabeling = std::make_unique< QgsVectorTileBasicLabeling >();
222 labeling->setStyles( labelingStyles );
227 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
232 bool colorIsDataDefined =
false;
234 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsFillSymbol >() );
238 if ( jsonPaint.contains( isBackgroundStyle ? QStringLiteral(
"background-color" ) : QStringLiteral(
"fill-color" ) ) )
240 const QVariant jsonFillColor = jsonPaint.value( isBackgroundStyle ? QStringLiteral(
"background-color" ) : QStringLiteral(
"fill-color" ) );
241 switch ( jsonFillColor.userType() )
243 case QMetaType::Type::QVariantMap:
247 case QMetaType::Type::QVariantList:
248 case QMetaType::Type::QStringList:
249 colorIsDataDefined =
true;
253 case QMetaType::Type::QString:
254 fillColor =
parseColor( jsonFillColor.toString(), context );
259 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillColor.userType() ) ) ) );
267 fillColor = QColor( 0, 0, 0 );
270 QColor fillOutlineColor;
271 if ( !isBackgroundStyle )
273 if ( !jsonPaint.contains( QStringLiteral(
"fill-outline-color" ) ) )
275 if ( fillColor.isValid() )
276 fillOutlineColor = fillColor;
284 const QVariant jsonFillOutlineColor = jsonPaint.value( QStringLiteral(
"fill-outline-color" ) );
285 switch ( jsonFillOutlineColor.userType() )
287 case QMetaType::Type::QVariantMap:
291 case QMetaType::Type::QVariantList:
292 case QMetaType::Type::QStringList:
296 case QMetaType::Type::QString:
297 fillOutlineColor =
parseColor( jsonFillOutlineColor.toString(), context );
301 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-outline-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillOutlineColor.userType() ) ) ) );
307 double fillOpacity = -1.0;
308 double rasterOpacity = -1.0;
309 if ( jsonPaint.contains( isBackgroundStyle ? QStringLiteral(
"background-opacity" ) : QStringLiteral(
"fill-opacity" ) ) )
311 const QVariant jsonFillOpacity = jsonPaint.value( isBackgroundStyle ? QStringLiteral(
"background-opacity" ) : QStringLiteral(
"fill-opacity" ) );
312 switch ( jsonFillOpacity.userType() )
314 case QMetaType::Type::Int:
315 case QMetaType::Type::LongLong:
316 case QMetaType::Type::Double:
317 fillOpacity = jsonFillOpacity.toDouble();
318 rasterOpacity = fillOpacity;
321 case QMetaType::Type::QVariantMap:
334 case QMetaType::Type::QVariantList:
335 case QMetaType::Type::QStringList:
349 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillOpacity.userType() ) ) ) );
355 QPointF fillTranslate;
356 if ( jsonPaint.contains( QStringLiteral(
"fill-translate" ) ) )
358 const QVariant jsonFillTranslate = jsonPaint.value( QStringLiteral(
"fill-translate" ) );
359 switch ( jsonFillTranslate.userType() )
362 case QMetaType::Type::QVariantMap:
366 case QMetaType::Type::QVariantList:
367 case QMetaType::Type::QStringList:
373 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-translate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillTranslate.userType() ) ) ) );
379 Q_ASSERT( fillSymbol );
382 symbol->setOutputUnit( context.
targetUnit() );
385 if ( !fillTranslate.isNull() )
391 if ( jsonPaint.contains( isBackgroundStyle ? QStringLiteral(
"background-pattern" ) : QStringLiteral(
"fill-pattern" ) ) )
395 const QVariant fillPatternJson = jsonPaint.value( isBackgroundStyle ? QStringLiteral(
"background-pattern" ) : QStringLiteral(
"fill-pattern" ) );
398 fillColor = QColor();
399 fillOutlineColor = QColor();
406 QString spriteProperty, spriteSizeProperty;
408 if ( !sprite.isEmpty() )
413 rasterFill->
setWidth( spriteSize.width() );
417 if ( rasterOpacity >= 0 )
422 if ( !spriteProperty.isEmpty() )
429 symbol->appendSymbolLayer( rasterFill );
435 if ( fillOpacity != -1 )
437 symbol->setOpacity( fillOpacity );
448 if ( fillOutlineColor.isValid() && ( fillOutlineColor.alpha() == 255 || fillOutlineColor != fillColor ) )
459 if ( fillColor.isValid() )
463 else if ( colorIsDataDefined )
479 if ( !jsonLayer.contains( QStringLiteral(
"paint" ) ) )
481 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
486 QString rasterLineSprite;
488 const QVariantMap jsonPaint = jsonLayer.
value( QStringLiteral(
"paint" ) ).toMap();
489 if ( jsonPaint.contains( QStringLiteral(
"line-pattern" ) ) )
491 const QVariant jsonLinePattern = jsonPaint.value( QStringLiteral(
"line-pattern" ) );
492 switch ( jsonLinePattern.userType() )
494 case QMetaType::Type::QVariantMap:
495 case QMetaType::Type::QString:
498 QString spriteProperty, spriteSizeProperty;
504 case QMetaType::Type::QVariantList:
505 case QMetaType::Type::QStringList:
510 if ( rasterLineSprite.isEmpty() )
513 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-pattern property" ).arg( context.
layerId() ) );
520 if ( jsonPaint.contains( QStringLiteral(
"line-color" ) ) )
522 const QVariant jsonLineColor = jsonPaint.value( QStringLiteral(
"line-color" ) );
523 switch ( jsonLineColor.userType() )
525 case QMetaType::Type::QVariantMap:
530 case QMetaType::Type::QVariantList:
531 case QMetaType::Type::QStringList:
536 case QMetaType::Type::QString:
537 lineColor =
parseColor( jsonLineColor.toString(), context );
541 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineColor.userType() ) ) ) );
548 lineColor = QColor( 0, 0, 0 );
554 if ( jsonPaint.contains( QStringLiteral(
"line-width" ) ) )
556 const QVariant jsonLineWidth = jsonPaint.
value( QStringLiteral(
"line-width" ) );
557 switch ( jsonLineWidth.userType() )
559 case QMetaType::Type::Int:
560 case QMetaType::Type::LongLong:
561 case QMetaType::Type::Double:
565 case QMetaType::Type::QVariantMap:
577 case QMetaType::Type::QVariantList:
578 case QMetaType::Type::QStringList:
590 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineWidth.userType() ) ) ) );
595 double lineOffset = 0.0;
596 if ( jsonPaint.contains( QStringLiteral(
"line-offset" ) ) )
598 const QVariant jsonLineOffset = jsonPaint.value( QStringLiteral(
"line-offset" ) );
599 switch ( jsonLineOffset.userType() )
601 case QMetaType::Type::Int:
602 case QMetaType::Type::LongLong:
603 case QMetaType::Type::Double:
607 case QMetaType::Type::QVariantMap:
612 case QMetaType::Type::QVariantList:
613 case QMetaType::Type::QStringList:
618 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineOffset.userType() ) ) ) );
623 double lineOpacity = -1.0;
625 if ( jsonPaint.contains( QStringLiteral(
"line-opacity" ) ) )
627 const QVariant jsonLineOpacity = jsonPaint.
value( QStringLiteral(
"line-opacity" ) );
628 switch ( jsonLineOpacity.userType() )
630 case QMetaType::Type::Int:
631 case QMetaType::Type::LongLong:
632 case QMetaType::Type::Double:
633 lineOpacity = jsonLineOpacity.toDouble();
636 case QMetaType::Type::QVariantMap:
639 double defaultValue = 1.0;
648 case QMetaType::Type::QVariantList:
649 case QMetaType::Type::QStringList:
652 double defaultValue = 1.0;
663 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineOpacity.userType() ) ) ) );
668 QVector< double > dashVector;
669 if ( jsonPaint.contains( QStringLiteral(
"line-dasharray" ) ) )
671 const QVariant jsonLineDashArray = jsonPaint.value( QStringLiteral(
"line-dasharray" ) );
672 switch ( jsonLineDashArray.userType() )
674 case QMetaType::Type::QVariantMap:
676 QString arrayExpression;
679 arrayExpression = QStringLiteral(
"array_to_string(array_foreach(%1,@element * (%2)), ';')" )
680 .arg(
parseArrayStops( jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList(), context, 1 ),
685 arrayExpression = QStringLiteral(
"array_to_string(%1, ';')" ).arg(
parseArrayStops( jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList(), context, lineWidth ) );
689 const QVariantList dashSource = jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList().first().toList().value( 1 ).toList();
690 for (
const QVariant &v : dashSource )
692 dashVector << v.toDouble() * lineWidth;
697 case QMetaType::Type::QVariantList:
698 case QMetaType::Type::QStringList:
700 const QVariantList dashSource = jsonLineDashArray.toList();
702 QVector< double > rawDashVectorSizes;
703 rawDashVectorSizes.reserve( dashSource.size() );
704 for (
const QVariant &v : dashSource )
706 rawDashVectorSizes << v.toDouble();
710 if ( rawDashVectorSizes.size() == 1 )
713 rawDashVectorSizes.clear();
715 else if ( rawDashVectorSizes.size() % 2 == 1 )
719 rawDashVectorSizes[0] = rawDashVectorSizes[0] + rawDashVectorSizes[rawDashVectorSizes.size() - 1];
720 rawDashVectorSizes.resize( rawDashVectorSizes.size() - 1 );
723 if ( !rawDashVectorSizes.isEmpty() && ( !lineWidthProperty.
asExpression().isEmpty() ) )
725 QStringList dashArrayStringParts;
726 dashArrayStringParts.reserve( rawDashVectorSizes.size() );
727 for (
double v : std::as_const( rawDashVectorSizes ) )
732 QString arrayExpression = QStringLiteral(
"array_to_string(array_foreach(array(%1),@element * (%2)), ';')" )
733 .arg( dashArrayStringParts.join(
',' ),
739 for (
double v : std::as_const( rawDashVectorSizes ) )
741 dashVector << v *lineWidth;
748 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-dasharray type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineDashArray.userType() ) ) ) );
753 Qt::PenCapStyle penCapStyle = Qt::FlatCap;
754 Qt::PenJoinStyle penJoinStyle = Qt::MiterJoin;
755 if ( jsonLayer.contains( QStringLiteral(
"layout" ) ) )
757 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
758 if ( jsonLayout.contains( QStringLiteral(
"line-cap" ) ) )
760 penCapStyle =
parseCapStyle( jsonLayout.value( QStringLiteral(
"line-cap" ) ).toString() );
762 if ( jsonLayout.contains( QStringLiteral(
"line-join" ) ) )
764 penJoinStyle =
parseJoinStyle( jsonLayout.value( QStringLiteral(
"line-join" ) ).toString() );
768 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsLineSymbol >() );
769 symbol->setOutputUnit( context.
targetUnit() );
771 if ( !rasterLineSprite.isEmpty() )
781 if ( lineOpacity != -1 )
783 symbol->setOpacity( lineOpacity );
789 symbol->setDataDefinedProperties( ddProperties );
791 if ( lineWidth != -1 )
795 symbol->changeSymbolLayer( 0, lineSymbol );
800 Q_ASSERT( lineSymbol );
810 if ( lineOpacity != -1 )
812 symbol->setOpacity( lineOpacity );
818 symbol->setDataDefinedProperties( ddProperties );
820 if ( lineColor.isValid() )
824 if ( lineWidth != -1 )
828 if ( !dashVector.empty() )
842 if ( !jsonLayer.contains( QStringLiteral(
"paint" ) ) )
844 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
848 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
852 QColor circleFillColor;
853 if ( jsonPaint.contains( QStringLiteral(
"circle-color" ) ) )
855 const QVariant jsonCircleColor = jsonPaint.
value( QStringLiteral(
"circle-color" ) );
856 switch ( jsonCircleColor.userType() )
858 case QMetaType::Type::QVariantMap:
862 case QMetaType::Type::QVariantList:
863 case QMetaType::Type::QStringList:
867 case QMetaType::Type::QString:
868 circleFillColor =
parseColor( jsonCircleColor.toString(), context );
872 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleColor.userType() ) ) ) );
879 circleFillColor = QColor( 0, 0, 0 );
883 double circleDiameter = 10.0;
884 if ( jsonPaint.contains( QStringLiteral(
"circle-radius" ) ) )
886 const QVariant jsonCircleRadius = jsonPaint.value( QStringLiteral(
"circle-radius" ) );
887 switch ( jsonCircleRadius.userType() )
889 case QMetaType::Type::Int:
890 case QMetaType::Type::LongLong:
891 case QMetaType::Type::Double:
895 case QMetaType::Type::QVariantMap:
900 case QMetaType::Type::QVariantList:
901 case QMetaType::Type::QStringList:
906 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-radius type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleRadius.userType() ) ) ) );
911 double circleOpacity = -1.0;
912 if ( jsonPaint.contains( QStringLiteral(
"circle-opacity" ) ) )
914 const QVariant jsonCircleOpacity = jsonPaint.value( QStringLiteral(
"circle-opacity" ) );
915 switch ( jsonCircleOpacity.userType() )
917 case QMetaType::Type::Int:
918 case QMetaType::Type::LongLong:
919 case QMetaType::Type::Double:
920 circleOpacity = jsonCircleOpacity.toDouble();
923 case QMetaType::Type::QVariantMap:
927 case QMetaType::Type::QVariantList:
928 case QMetaType::Type::QStringList:
933 context.pushWarning( QObject::tr(
"%1: Skipping unsupported circle-opacity type (%2)" ).arg( context.layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleOpacity.userType() ) ) ) );
937 if ( ( circleOpacity != -1 ) && circleFillColor.isValid() )
939 circleFillColor.setAlphaF( circleOpacity );
943 QColor circleStrokeColor;
944 if ( jsonPaint.contains( QStringLiteral(
"circle-stroke-color" ) ) )
946 const QVariant jsonCircleStrokeColor = jsonPaint.value( QStringLiteral(
"circle-stroke-color" ) );
947 switch ( jsonCircleStrokeColor.userType() )
949 case QMetaType::Type::QVariantMap:
953 case QMetaType::Type::QVariantList:
954 case QMetaType::Type::QStringList:
958 case QMetaType::Type::QString:
959 circleStrokeColor =
parseColor( jsonCircleStrokeColor.toString(), context );
963 context.pushWarning( QObject::tr(
"%1: Skipping unsupported circle-stroke-color type (%2)" ).arg( context.layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleStrokeColor.userType() ) ) ) );
969 double circleStrokeWidth = -1.0;
970 if ( jsonPaint.contains( QStringLiteral(
"circle-stroke-width" ) ) )
972 const QVariant circleStrokeWidthJson = jsonPaint.value( QStringLiteral(
"circle-stroke-width" ) );
973 switch ( circleStrokeWidthJson.userType() )
975 case QMetaType::Type::Int:
976 case QMetaType::Type::LongLong:
977 case QMetaType::Type::Double:
981 case QMetaType::Type::QVariantMap:
982 circleStrokeWidth = -1.0;
986 case QMetaType::Type::QVariantList:
987 case QMetaType::Type::QStringList:
992 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-stroke-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( circleStrokeWidthJson.userType() ) ) ) );
997 double circleStrokeOpacity = -1.0;
998 if ( jsonPaint.contains( QStringLiteral(
"circle-stroke-opacity" ) ) )
1000 const QVariant jsonCircleStrokeOpacity = jsonPaint.value( QStringLiteral(
"circle-stroke-opacity" ) );
1001 switch ( jsonCircleStrokeOpacity.userType() )
1003 case QMetaType::Type::Int:
1004 case QMetaType::Type::LongLong:
1005 case QMetaType::Type::Double:
1006 circleStrokeOpacity = jsonCircleStrokeOpacity.toDouble();
1009 case QMetaType::Type::QVariantMap:
1013 case QMetaType::Type::QVariantList:
1014 case QMetaType::Type::QStringList:
1019 context.pushWarning( QObject::tr(
"%1: Skipping unsupported circle-stroke-opacity type (%2)" ).arg( context.layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleStrokeOpacity.userType() ) ) ) );
1023 if ( ( circleStrokeOpacity != -1 ) && circleStrokeColor.isValid() )
1025 circleStrokeColor.setAlphaF( circleStrokeOpacity );
1029 QPointF circleTranslate;
1030 if ( jsonPaint.contains( QStringLiteral(
"circle-translate" ) ) )
1032 const QVariant jsonCircleTranslate = jsonPaint.value( QStringLiteral(
"circle-translate" ) );
1033 switch ( jsonCircleTranslate.userType() )
1036 case QMetaType::Type::QVariantMap:
1040 case QMetaType::Type::QVariantList:
1041 case QMetaType::Type::QStringList:
1047 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-translate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleTranslate.userType() ) ) ) );
1052 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsMarkerSymbol >() );
1054 Q_ASSERT( markerSymbolLayer );
1057 symbol->setOutputUnit( context.
targetUnit() );
1058 symbol->setDataDefinedProperties( ddProperties );
1060 if ( !circleTranslate.isNull() )
1062 markerSymbolLayer->
setOffset( circleTranslate );
1066 if ( circleFillColor.isValid() )
1070 if ( circleDiameter != -1 )
1072 markerSymbolLayer->
setSize( circleDiameter );
1075 if ( circleStrokeColor.isValid() )
1079 if ( circleStrokeWidth != -1 )
1092 hasLabeling =
false;
1093 hasRenderer =
false;
1095 if ( !jsonLayer.contains( QStringLiteral(
"layout" ) ) )
1097 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
1100 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
1101 if ( !jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
1107 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
1113 if ( jsonLayout.contains( QStringLiteral(
"text-size" ) ) )
1115 const QVariant jsonTextSize = jsonLayout.
value( QStringLiteral(
"text-size" ) );
1116 switch ( jsonTextSize.userType() )
1118 case QMetaType::Type::Int:
1119 case QMetaType::Type::LongLong:
1120 case QMetaType::Type::Double:
1124 case QMetaType::Type::QVariantMap:
1130 case QMetaType::Type::QVariantList:
1131 case QMetaType::Type::QStringList:
1137 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextSize.userType() ) ) ) );
1141 if ( textSizeProperty )
1148 constexpr double EM_TO_CHARS = 2.0;
1150 double textMaxWidth = -1;
1151 if ( jsonLayout.contains( QStringLiteral(
"text-max-width" ) ) )
1153 const QVariant jsonTextMaxWidth = jsonLayout.value( QStringLiteral(
"text-max-width" ) );
1154 switch ( jsonTextMaxWidth.userType() )
1156 case QMetaType::Type::Int:
1157 case QMetaType::Type::LongLong:
1158 case QMetaType::Type::Double:
1159 textMaxWidth = jsonTextMaxWidth.toDouble() * EM_TO_CHARS;
1162 case QMetaType::Type::QVariantMap:
1166 case QMetaType::Type::QVariantList:
1167 case QMetaType::Type::QStringList:
1172 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-max-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextMaxWidth.userType() ) ) ) );
1179 textMaxWidth = 10 * EM_TO_CHARS;
1182 double textLetterSpacing = -1;
1183 if ( jsonLayout.contains( QStringLiteral(
"text-letter-spacing" ) ) )
1185 const QVariant jsonTextLetterSpacing = jsonLayout.value( QStringLiteral(
"text-letter-spacing" ) );
1186 switch ( jsonTextLetterSpacing.userType() )
1188 case QMetaType::Type::Int:
1189 case QMetaType::Type::LongLong:
1190 case QMetaType::Type::Double:
1191 textLetterSpacing = jsonTextLetterSpacing.toDouble();
1194 case QMetaType::Type::QVariantMap:
1198 case QMetaType::Type::QVariantList:
1199 case QMetaType::Type::QStringList:
1204 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-letter-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextLetterSpacing.userType() ) ) ) );
1210 bool foundFont =
false;
1212 QString fontStyleName;
1214 if ( jsonLayout.contains( QStringLiteral(
"text-font" ) ) )
1216 auto splitFontFamily = [](
const QString & fontName, QString & family, QString & style ) ->
bool
1218 QString matchedFamily;
1219 const QStringList textFontParts = fontName.split(
' ' );
1220 for (
int i = textFontParts.size() - 1; i >= 1; --i )
1222 const QString candidateFontFamily = textFontParts.mid( 0, i ).join(
' ' );
1223 const QString candidateFontStyle = textFontParts.mid( i ).join(
' ' );
1228 family = processedFontFamily;
1229 style = candidateFontStyle;
1234 if ( processedFontFamily == matchedFamily )
1236 family = processedFontFamily;
1237 style = candidateFontStyle;
1241 family = matchedFamily;
1242 style = processedFontFamily;
1243 style.replace( matchedFamily, QString() );
1244 style = style.trimmed();
1245 if ( !style.isEmpty() && !candidateFontStyle.isEmpty() )
1247 style += QStringLiteral(
" %1" ).arg( candidateFontStyle );
1255 if ( QFontDatabase().hasFamily( processedFontFamily ) )
1258 family = processedFontFamily;
1264 family = matchedFamily;
1271 const QVariant jsonTextFont = jsonLayout.value( QStringLiteral(
"text-font" ) );
1272 if ( jsonTextFont.userType() != QMetaType::Type::QVariantList && jsonTextFont.userType() != QMetaType::Type::QStringList && jsonTextFont.userType() != QMetaType::Type::QString
1273 && jsonTextFont.userType() != QMetaType::Type::QVariantMap )
1275 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-font type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextFont.userType() ) ) ) );
1279 switch ( jsonTextFont.userType() )
1281 case QMetaType::Type::QVariantList:
1282 case QMetaType::Type::QStringList:
1283 fontName = jsonTextFont.toList().value( 0 ).toString();
1286 case QMetaType::Type::QString:
1287 fontName = jsonTextFont.toString();
1290 case QMetaType::Type::QVariantMap:
1292 QString familyCaseString = QStringLiteral(
"CASE " );
1293 QString styleCaseString = QStringLiteral(
"CASE " );
1295 const QVariantList stops = jsonTextFont.toMap().value( QStringLiteral(
"stops" ) ).toList();
1298 for (
int i = 0; i < stops.length() - 1; ++i )
1301 const QVariant bz = stops.value( i ).toList().value( 0 );
1302 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();
1303 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
1305 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1311 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
1312 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
1314 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1319 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1321 familyCaseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1322 "THEN %3 " ).arg( bz.toString(),
1325 styleCaseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1326 "THEN %3 " ).arg( bz.toString(),
1332 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1338 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();
1339 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1346 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1353 fontName = fontFamily;
1363 if ( splitFontFamily( fontName, fontFamily, fontStyleName ) )
1366 if ( !fontStyleName.isEmpty() )
1367 textFont.setStyleName( fontStyleName );
1377 fontName = QStringLiteral(
"Open Sans" );
1379 textFont.setStyleName( QStringLiteral(
"Regular" ) );
1380 fontStyleName = QStringLiteral(
"Regular" );
1385 fontName = QStringLiteral(
"Arial Unicode MS" );
1387 textFont.setStyleName( QStringLiteral(
"Regular" ) );
1388 fontStyleName = QStringLiteral(
"Regular" );
1393 fontName = QStringLiteral(
"Open Sans, Arial Unicode MS" );
1396 if ( !foundFont && !fontName.isEmpty() )
1398 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), fontName ) );
1403 if ( jsonPaint.contains( QStringLiteral(
"text-color" ) ) )
1405 const QVariant jsonTextColor = jsonPaint.value( QStringLiteral(
"text-color" ) );
1406 switch ( jsonTextColor.userType() )
1408 case QMetaType::Type::QVariantMap:
1412 case QMetaType::Type::QVariantList:
1413 case QMetaType::Type::QStringList:
1417 case QMetaType::Type::QString:
1418 textColor =
parseColor( jsonTextColor.toString(), context );
1422 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextColor.userType() ) ) ) );
1429 textColor = QColor( 0, 0, 0 );
1433 QColor bufferColor( 0, 0, 0, 0 );
1434 if ( jsonPaint.contains( QStringLiteral(
"text-halo-color" ) ) )
1436 const QVariant jsonBufferColor = jsonPaint.value( QStringLiteral(
"text-halo-color" ) );
1437 switch ( jsonBufferColor.userType() )
1439 case QMetaType::Type::QVariantMap:
1443 case QMetaType::Type::QVariantList:
1444 case QMetaType::Type::QStringList:
1448 case QMetaType::Type::QString:
1449 bufferColor =
parseColor( jsonBufferColor.toString(), context );
1453 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-halo-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonBufferColor.userType() ) ) ) );
1458 double bufferSize = 0.0;
1462 constexpr double BUFFER_SIZE_SCALE = 2.0;
1463 if ( jsonPaint.contains( QStringLiteral(
"text-halo-width" ) ) )
1465 const QVariant jsonHaloWidth = jsonPaint.value( QStringLiteral(
"text-halo-width" ) );
1466 QString bufferSizeDataDefined;
1467 switch ( jsonHaloWidth.userType() )
1469 case QMetaType::Type::Int:
1470 case QMetaType::Type::LongLong:
1471 case QMetaType::Type::Double:
1475 case QMetaType::Type::QVariantMap:
1480 case QMetaType::Type::QVariantList:
1481 case QMetaType::Type::QStringList:
1487 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-halo-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonHaloWidth.userType() ) ) ) );
1493 if ( bufferSize > 0 )
1495 if ( textSize > 0 && bufferSizeDataDefined.isEmpty() )
1497 bufferSize = std::min( bufferSize, textSize * BUFFER_SIZE_SCALE / 4 );
1499 else if ( textSize > 0 && !bufferSizeDataDefined.isEmpty() )
1501 bufferSizeDataDefined = QStringLiteral(
"min(%1/4, %2)" ).arg( textSize * BUFFER_SIZE_SCALE ).arg( bufferSizeDataDefined );
1504 else if ( !bufferSizeDataDefined.isEmpty() )
1506 bufferSizeDataDefined = QStringLiteral(
"min(%1*%2/4, %3)" )
1508 .arg( BUFFER_SIZE_SCALE )
1509 .arg( bufferSizeDataDefined );
1512 else if ( bufferSizeDataDefined.isEmpty() )
1514 bufferSizeDataDefined = QStringLiteral(
"min(%1*%2/4, %3)" )
1516 .arg( BUFFER_SIZE_SCALE )
1523 double haloBlurSize = 0;
1524 if ( jsonPaint.contains( QStringLiteral(
"text-halo-blur" ) ) )
1526 const QVariant jsonTextHaloBlur = jsonPaint.value( QStringLiteral(
"text-halo-blur" ) );
1527 switch ( jsonTextHaloBlur.userType() )
1529 case QMetaType::Type::Int:
1530 case QMetaType::Type::LongLong:
1531 case QMetaType::Type::Double:
1538 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-halo-blur type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextHaloBlur.userType() ) ) ) );
1545 if ( textColor.isValid() )
1547 if ( textSize >= 0 )
1552 if ( !fontStyleName.isEmpty() )
1555 if ( textLetterSpacing > 0 )
1557 QFont f = format.
font();
1558 f.setLetterSpacing( QFont::AbsoluteSpacing, textLetterSpacing );
1562 if ( bufferSize > 0 )
1565 const double opacity = bufferColor.alphaF();
1566 bufferColor.setAlphaF( 1.0 );
1574 if ( haloBlurSize > 0 )
1590 if ( textMaxWidth > 0 )
1596 if ( jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
1598 const QVariant jsonTextField = jsonLayout.value( QStringLiteral(
"text-field" ) );
1599 switch ( jsonTextField.userType() )
1601 case QMetaType::Type::QString:
1603 labelSettings.
fieldName = processLabelField( jsonTextField.toString(), labelSettings.
isExpression );
1607 case QMetaType::Type::QVariantList:
1608 case QMetaType::Type::QStringList:
1610 const QVariantList textFieldList = jsonTextField.toList();
1618 if ( textFieldList.size() > 2 && textFieldList.at( 0 ).toString() == QLatin1String(
"format" ) )
1621 for (
int i = 1; i < textFieldList.size(); ++i )
1623 bool isExpression =
false;
1624 const QString part = processLabelField( textFieldList.at( i ).toString(), isExpression );
1625 if ( !isExpression )
1632 labelSettings.
fieldName = QStringLiteral(
"concat(%1)" ).arg( parts.join(
',' ) );
1647 case QMetaType::Type::QVariantMap:
1649 const QVariantList stops = jsonTextField.toMap().value( QStringLiteral(
"stops" ) ).toList();
1650 if ( !stops.empty() )
1657 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-field dictionary" ).arg( context.
layerId() ) );
1663 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-field type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextField.userType() ) ) ) );
1668 if ( jsonLayout.contains( QStringLiteral(
"text-transform" ) ) )
1670 const QString textTransform = jsonLayout.value( QStringLiteral(
"text-transform" ) ).toString();
1671 if ( textTransform == QLatin1String(
"uppercase" ) )
1675 else if ( textTransform == QLatin1String(
"lowercase" ) )
1684 if ( jsonLayout.contains( QStringLiteral(
"symbol-placement" ) ) )
1686 const QString symbolPlacement = jsonLayout.value( QStringLiteral(
"symbol-placement" ) ).toString();
1687 if ( symbolPlacement == QLatin1String(
"line" ) )
1693 if ( jsonLayout.contains( QStringLiteral(
"text-rotation-alignment" ) ) )
1695 const QString textRotationAlignment = jsonLayout.value( QStringLiteral(
"text-rotation-alignment" ) ).toString();
1696 if ( textRotationAlignment == QLatin1String(
"viewport" ) )
1706 if ( jsonLayout.contains( QStringLiteral(
"text-offset" ) ) )
1708 const QVariant jsonTextOffset = jsonLayout.
value( QStringLiteral(
"text-offset" ) );
1711 switch ( jsonTextOffset.userType() )
1713 case QMetaType::Type::QVariantMap:
1714 textOffsetProperty =
parseInterpolatePointByZoom( jsonTextOffset.toMap(), context, !textSizeProperty ? textSize : 1.0, &textOffset );
1715 if ( !textSizeProperty )
1726 case QMetaType::Type::QVariantList:
1727 case QMetaType::Type::QStringList:
1728 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1729 jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1733 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextOffset.userType() ) ) ) );
1737 if ( !textOffset.isNull() )
1740 labelSettings.
dist = std::abs( textOffset.y() ) - textSize;
1742 if ( textSizeProperty && !textOffsetProperty )
1749 if ( textOffset.isNull() )
1757 if ( jsonLayout.contains( QStringLiteral(
"text-justify" ) ) )
1759 const QVariant jsonTextJustify = jsonLayout.value( QStringLiteral(
"text-justify" ) );
1762 QString textAlign = QStringLiteral(
"center" );
1764 const QVariantMap conversionMap
1766 { QStringLiteral(
"left" ), QStringLiteral(
"left" ) },
1767 { QStringLiteral(
"center" ), QStringLiteral(
"center" ) },
1768 { QStringLiteral(
"right" ), QStringLiteral(
"right" ) },
1769 { QStringLiteral(
"auto" ), QStringLiteral(
"follow" ) }
1772 switch ( jsonTextJustify.userType() )
1774 case QMetaType::Type::QString:
1775 textAlign = jsonTextJustify.toString();
1778 case QMetaType::Type::QVariantList:
1782 case QMetaType::Type::QVariantMap:
1787 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-justify type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextJustify.userType() ) ) ) );
1791 if ( textAlign == QLatin1String(
"left" ) )
1793 else if ( textAlign == QLatin1String(
"right" ) )
1795 else if ( textAlign == QLatin1String(
"center" ) )
1797 else if ( textAlign == QLatin1String(
"follow" ) )
1807 if ( jsonLayout.contains( QStringLiteral(
"text-anchor" ) ) )
1809 const QVariant jsonTextAnchor = jsonLayout.value( QStringLiteral(
"text-anchor" ) );
1812 const QVariantMap conversionMap
1814 { QStringLiteral(
"center" ), 4 },
1815 { QStringLiteral(
"left" ), 5 },
1816 { QStringLiteral(
"right" ), 3 },
1817 { QStringLiteral(
"top" ), 7 },
1818 { QStringLiteral(
"bottom" ), 1 },
1819 { QStringLiteral(
"top-left" ), 8 },
1820 { QStringLiteral(
"top-right" ), 6 },
1821 { QStringLiteral(
"bottom-left" ), 2 },
1822 { QStringLiteral(
"bottom-right" ), 0 },
1825 switch ( jsonTextAnchor.userType() )
1827 case QMetaType::Type::QString:
1828 textAnchor = jsonTextAnchor.toString();
1831 case QMetaType::Type::QVariantList:
1835 case QMetaType::Type::QVariantMap:
1840 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-anchor type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextAnchor.userType() ) ) ) );
1844 if ( textAnchor == QLatin1String(
"center" ) )
1846 else if ( textAnchor == QLatin1String(
"left" ) )
1848 else if ( textAnchor == QLatin1String(
"right" ) )
1850 else if ( textAnchor == QLatin1String(
"top" ) )
1852 else if ( textAnchor == QLatin1String(
"bottom" ) )
1854 else if ( textAnchor == QLatin1String(
"top-left" ) )
1856 else if ( textAnchor == QLatin1String(
"top-right" ) )
1858 else if ( textAnchor == QLatin1String(
"bottom-left" ) )
1860 else if ( textAnchor == QLatin1String(
"bottom-right" ) )
1865 if ( jsonLayout.contains( QStringLiteral(
"text-offset" ) ) )
1867 const QVariant jsonTextOffset = jsonLayout.value( QStringLiteral(
"text-offset" ) );
1870 switch ( jsonTextOffset.userType() )
1872 case QMetaType::Type::QVariantMap:
1876 case QMetaType::Type::QVariantList:
1877 case QMetaType::Type::QStringList:
1878 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1879 jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1883 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextOffset.userType() ) ) ) );
1887 if ( !textOffset.isNull() )
1890 labelSettings.
xOffset = textOffset.x();
1891 labelSettings.
yOffset = textOffset.y();
1896 if ( jsonLayout.contains( QStringLiteral(
"icon-image" ) ) &&
1900 QString spriteProperty, spriteSizeProperty;
1902 if ( !sprite.isEmpty() )
1905 if ( jsonLayout.contains( QStringLiteral(
"icon-size" ) ) )
1908 const QVariant jsonIconSize = jsonLayout.
value( QStringLiteral(
"icon-size" ) );
1909 switch ( jsonIconSize.userType() )
1911 case QMetaType::Type::Int:
1912 case QMetaType::Type::LongLong:
1913 case QMetaType::Type::Double:
1915 size = jsonIconSize.toDouble();
1916 if ( !spriteSizeProperty.isEmpty() )
1919 QgsProperty::fromExpression( QStringLiteral(
"with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
1924 case QMetaType::Type::QVariantMap:
1928 case QMetaType::Type::QVariantList:
1929 case QMetaType::Type::QStringList:
1933 context.
pushWarning( QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) ) );
1939 if ( !spriteSizeProperty.isEmpty() )
1953 markerLayer->
setPath( sprite );
1954 markerLayer->
setSize( spriteSize.width() );
1957 if ( !spriteProperty.isEmpty() )
1967 backgroundSettings.
setSize( spriteSize * size );
1975 if ( textSize >= 0 )
1998 if ( !jsonLayer.contains( QStringLiteral(
"layout" ) ) )
2000 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
2003 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
2005 if ( jsonLayout.value( QStringLiteral(
"symbol-placement" ) ).toString() == QLatin1String(
"line" ) && !jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
2009 double spacing = -1.0;
2010 if ( jsonLayout.contains( QStringLiteral(
"symbol-spacing" ) ) )
2012 const QVariant jsonSpacing = jsonLayout.
value( QStringLiteral(
"symbol-spacing" ) );
2013 switch ( jsonSpacing.userType() )
2015 case QMetaType::Type::Int:
2016 case QMetaType::Type::LongLong:
2017 case QMetaType::Type::Double:
2021 case QMetaType::Type::QVariantMap:
2025 case QMetaType::Type::QVariantList:
2026 case QMetaType::Type::QStringList:
2031 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported symbol-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonSpacing.userType() ) ) ) );
2041 bool rotateMarkers =
true;
2042 if ( jsonLayout.contains( QStringLiteral(
"icon-rotation-alignment" ) ) )
2044 const QString alignment = jsonLayout.value( QStringLiteral(
"icon-rotation-alignment" ) ).toString();
2045 if ( alignment == QLatin1String(
"map" ) || alignment == QLatin1String(
"auto" ) )
2047 rotateMarkers =
true;
2049 else if ( alignment == QLatin1String(
"viewport" ) )
2051 rotateMarkers =
false;
2056 double rotation = 0.0;
2057 if ( jsonLayout.contains( QStringLiteral(
"icon-rotate" ) ) )
2059 const QVariant jsonIconRotate = jsonLayout.
value( QStringLiteral(
"icon-rotate" ) );
2060 switch ( jsonIconRotate.userType() )
2062 case QMetaType::Type::Int:
2063 case QMetaType::Type::LongLong:
2064 case QMetaType::Type::Double:
2065 rotation = jsonIconRotate.toDouble();
2068 case QMetaType::Type::QVariantMap:
2072 case QMetaType::Type::QVariantList:
2073 case QMetaType::Type::QStringList:
2078 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconRotate.userType() ) ) ) );
2094 QString spriteProperty, spriteSizeProperty;
2096 if ( !sprite.isNull() )
2098 markerLayer->
setPath( sprite );
2099 markerLayer->
setSize( spriteSize.width() );
2102 if ( !spriteProperty.isEmpty() )
2109 if ( jsonLayout.contains( QStringLiteral(
"icon-size" ) ) )
2111 const QVariant jsonIconSize = jsonLayout.value( QStringLiteral(
"icon-size" ) );
2114 switch ( jsonIconSize.userType() )
2116 case QMetaType::Type::Int:
2117 case QMetaType::Type::LongLong:
2118 case QMetaType::Type::Double:
2120 size = jsonIconSize.toDouble();
2121 if ( !spriteSizeProperty.isEmpty() )
2124 QgsProperty::fromExpression( QStringLiteral(
"with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
2129 case QMetaType::Type::QVariantMap:
2133 case QMetaType::Type::QVariantList:
2134 case QMetaType::Type::QStringList:
2138 context.
pushWarning( QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) ) );
2141 markerLayer->
setSize( size * spriteSize.width() );
2144 if ( !spriteSizeProperty.isEmpty() )
2161 std::unique_ptr< QgsSymbol > symbol = std::make_unique< QgsLineSymbol >(
QgsSymbolLayerList() << lineSymbol );
2164 symbol->setOutputUnit( context.
targetUnit() );
2168 rendererStyle.
setSymbol( symbol.release() );
2171 else if ( jsonLayout.contains( QStringLiteral(
"icon-image" ) ) )
2173 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
2176 QString spriteProperty, spriteSizeProperty;
2178 if ( !sprite.isEmpty() || !spriteProperty.isEmpty() )
2181 rasterMarker->
setPath( sprite );
2182 rasterMarker->
setSize( spriteSize.width() );
2186 if ( !spriteProperty.isEmpty() )
2192 if ( jsonLayout.contains( QStringLiteral(
"icon-size" ) ) )
2194 const QVariant jsonIconSize = jsonLayout.value( QStringLiteral(
"icon-size" ) );
2197 switch ( jsonIconSize.userType() )
2199 case QMetaType::Type::Int:
2200 case QMetaType::Type::LongLong:
2201 case QMetaType::Type::Double:
2203 size = jsonIconSize.toDouble();
2204 if ( !spriteSizeProperty.isEmpty() )
2207 QgsProperty::fromExpression( QStringLiteral(
"with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
2212 case QMetaType::Type::QVariantMap:
2216 case QMetaType::Type::QVariantList:
2217 case QMetaType::Type::QStringList:
2221 context.
pushWarning( QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) ) );
2224 rasterMarker->
setSize( size * spriteSize.width() );
2227 if ( !spriteSizeProperty.isEmpty() )
2240 double rotation = 0.0;
2241 if ( jsonLayout.contains( QStringLiteral(
"icon-rotate" ) ) )
2243 const QVariant jsonIconRotate = jsonLayout.value( QStringLiteral(
"icon-rotate" ) );
2244 switch ( jsonIconRotate.userType() )
2246 case QMetaType::Type::Int:
2247 case QMetaType::Type::LongLong:
2248 case QMetaType::Type::Double:
2249 rotation = jsonIconRotate.toDouble();
2252 case QMetaType::Type::QVariantMap:
2256 case QMetaType::Type::QVariantList:
2257 case QMetaType::Type::QStringList:
2262 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconRotate.userType() ) ) ) );
2267 double iconOpacity = -1.0;
2268 if ( jsonPaint.contains( QStringLiteral(
"icon-opacity" ) ) )
2270 const QVariant jsonIconOpacity = jsonPaint.value( QStringLiteral(
"icon-opacity" ) );
2271 switch ( jsonIconOpacity.userType() )
2273 case QMetaType::Type::Int:
2274 case QMetaType::Type::LongLong:
2275 case QMetaType::Type::Double:
2276 iconOpacity = jsonIconOpacity.toDouble();
2279 case QMetaType::Type::QVariantMap:
2283 case QMetaType::Type::QVariantList:
2284 case QMetaType::Type::QStringList:
2289 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported icon-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconOpacity.userType() ) ) ) );
2295 rasterMarker->
setAngle( rotation );
2296 if ( iconOpacity >= 0 )
2300 rendererStyle.
setSymbol( markerSymbol );
2311 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2312 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2313 if ( stops.empty() )
2316 QString caseString = QStringLiteral(
"CASE " );
2317 const QString colorComponent(
"color_part(%1,'%2')" );
2319 for (
int i = 0; i < stops.length() - 1; ++i )
2322 const QString bz = stops.at( i ).toList().value( 0 ).toString();
2324 const QString tz = stops.at( i + 1 ).toList().value( 0 ).toString();
2326 const QVariant bcVariant = stops.at( i ).toList().value( 1 );
2327 const QVariant tcVariant = stops.at( i + 1 ).toList().value( 1 );
2329 const QColor bottomColor =
parseColor( bcVariant.toString(), context );
2330 const QColor topColor =
parseColor( tcVariant.toString(), context );
2332 if ( i == 0 && bottomColor.isValid() )
2339 caseString += QStringLiteral(
"WHEN @vector_tile_zoom < %1 THEN color_hsla(%2, %3, %4, %5) " )
2340 .arg( bz ).arg( bcHue ).arg( bcSat ).arg( bcLight ).arg( bcAlpha );
2343 if ( bottomColor.isValid() && topColor.isValid() )
2355 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2356 "%3, %4, %5, %6) " ).arg( bz, tz,
2367 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2368 "%3, %4, %5, %6) " ).arg( bz, tz,
2369 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"hsl_hue" ), colorComponent.arg( topColorExpr ).arg(
"hsl_hue" ), base, 1, &context ),
2370 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"hsl_saturation" ), colorComponent.arg( topColorExpr ).arg(
"hsl_saturation" ), base, 1, &context ),
2371 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"lightness" ), colorComponent.arg( topColorExpr ).arg(
"lightness" ), base, 1, &context ),
2372 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"alpha" ), colorComponent.arg( topColorExpr ).arg(
"alpha" ), base, 1, &context ) );
2377 const QString tz = stops.last().toList().value( 0 ).toString();
2378 const QVariant tcVariant = stops.last().toList().value( 1 );
2380 if ( tcVariant.userType() == QMetaType::Type::QString )
2383 if ( topColor.isValid() )
2390 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2391 "ELSE color_hsla(%2, %3, %4, %5) END" ).arg( tz ).arg( tcHue ).arg( tcSat ).arg( tcLight ).arg( tcAlpha );
2394 else if ( tcVariant.userType() == QMetaType::QVariantList )
2398 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2399 "ELSE color_hsla(%2, %3, %4, %5) END" ).arg( tz )
2400 .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" ) );
2403 if ( !stops.empty() && defaultColor )
2404 *defaultColor =
parseColor( stops.value( 0 ).toList().value( 1 ).toString(), context );
2411 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2412 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2413 if ( stops.empty() )
2416 QString scaleExpression;
2417 if ( stops.size() <= 2 )
2420 stops.last().toList().value( 0 ).toDouble(),
2421 stops.value( 0 ).toList().value( 1 ),
2422 stops.last().toList().value( 1 ), base, multiplier, &context );
2426 scaleExpression =
parseStops( base, stops, multiplier, context );
2429 if ( !stops.empty() && defaultNumber )
2430 *defaultNumber = stops.value( 0 ).toList().value( 1 ).toDouble() * multiplier;
2440 context = *contextPtr;
2442 const double base = json.value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2443 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2444 if ( stops.empty() )
2447 QString scaleExpression;
2448 if ( stops.length() <= 2 )
2450 const QVariant bv = stops.value( 0 ).toList().value( 1 );
2451 const QVariant tv = stops.last().toList().value( 1 );
2452 double bottom = 0.0;
2454 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2455 scaleExpression = QStringLiteral(
"set_color_part(@symbol_color, 'alpha', %1)" )
2457 stops.last().toList().value( 0 ).toDouble(),
2458 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2459 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ), base, 1, &context ) );
2470 QString caseString = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN set_color_part(@symbol_color, 'alpha', %2)" )
2471 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
2472 .arg( stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity );
2474 for (
int i = 0; i < stops.size() - 1; ++i )
2476 const QVariant bv = stops.value( i ).toList().value( 1 );
2477 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2478 double bottom = 0.0;
2480 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2482 caseString += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
2483 "THEN set_color_part(@symbol_color, 'alpha', %3)" )
2484 .arg( stops.value( i ).toList().value( 0 ).toString(),
2485 stops.value( i + 1 ).toList().value( 0 ).toString(),
2487 stops.value( i + 1 ).toList().value( 0 ).toDouble(),
2488 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2489 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ),
2490 base, 1, &context ) );
2493 caseString += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
2494 "THEN set_color_part(@symbol_color, 'alpha', %2) END" )
2495 .arg( stops.last().toList().value( 0 ).toString() )
2496 .arg( stops.last().toList().value( 1 ).toDouble() * maxOpacity );
2502 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2503 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2504 if ( stops.empty() )
2507 QString scaleExpression;
2508 if ( stops.size() <= 2 )
2510 scaleExpression = QStringLiteral(
"array(%1,%2)" ).arg(
interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(),
2511 stops.last().toList().value( 0 ).toDouble(),
2512 stops.value( 0 ).toList().value( 1 ).toList().value( 0 ),
2513 stops.last().toList().value( 1 ).toList().value( 0 ), base, multiplier, &context ),
2515 stops.last().toList().value( 0 ).toDouble(),
2516 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ),
2517 stops.last().toList().value( 1 ).toList().value( 1 ), base, multiplier, &context )
2522 scaleExpression =
parsePointStops( base, stops, context, multiplier );
2525 if ( !stops.empty() && defaultPoint )
2526 *defaultPoint = QPointF( stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble() * multiplier,
2527 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble() * multiplier );
2533 const QVariantMap &conversionMap, QString *defaultString )
2535 const QVariantList stops = json.
value( QStringLiteral(
"stops" ) ).toList();
2536 if ( stops.empty() )
2539 const QString scaleExpression =
parseStringStops( stops, context, conversionMap, defaultString );
2546 QString caseString = QStringLiteral(
"CASE " );
2548 for (
int i = 0; i < stops.length() - 1; ++i )
2551 const QVariant bz = stops.value( i ).toList().value( 0 );
2552 const QVariant bv = stops.value( i ).toList().value( 1 );
2553 if ( bv.userType() != QMetaType::Type::QVariantList && bv.userType() != QMetaType::Type::QStringList )
2555 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( bz.userType() ) ) ) );
2560 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2561 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2562 if ( tv.userType() != QMetaType::Type::QVariantList && tv.userType() != QMetaType::Type::QStringList )
2564 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( tz.userType() ) ) ) );
2568 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2569 "THEN array(%3,%4)" ).arg( bz.toString(),
2571 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 0 ), tv.toList().value( 0 ), base, multiplier, &context ),
2572 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 1 ), tv.toList().value( 1 ), base, multiplier, &context ) );
2574 caseString += QLatin1String(
"END" );
2580 if ( stops.length() < 2 )
2583 QString caseString = QStringLiteral(
"CASE" );
2585 for (
int i = 0; i < stops.length(); ++i )
2587 caseString += QLatin1String(
" WHEN " );
2588 QStringList conditions;
2591 const QVariant bottomZoom = stops.value( i ).toList().value( 0 );
2592 conditions << QStringLiteral(
"@vector_tile_zoom > %1" ).arg( bottomZoom.toString() );
2594 if ( i < stops.length() - 1 )
2596 const QVariant topZoom = stops.value( i + 1 ).toList().value( 0 );
2597 conditions << QStringLiteral(
"@vector_tile_zoom <= %1" ).arg( topZoom.toString() );
2600 const QVariantList values = stops.value( i ).toList().value( 1 ).toList();
2601 QStringList valuesFixed;
2603 for (
const QVariant &value : values )
2605 const double number = value.toDouble( &ok );
2607 valuesFixed << QString::number( number * multiplier );
2611 caseString += QStringLiteral(
"%1 THEN array(%3)" ).arg(
2612 conditions.join( QLatin1String(
" AND " ) ),
2613 valuesFixed.join(
',' )
2616 caseString += QLatin1String(
" END" );
2622 QString caseString = QStringLiteral(
"CASE " );
2624 for (
int i = 0; i < stops.length() - 1; ++i )
2627 const QVariant bz = stops.value( i ).toList().value( 0 );
2628 const QVariant bv = stops.value( i ).toList().value( 1 );
2629 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2631 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2636 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2637 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2638 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2640 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2644 const QString lowerComparator = i == 0 ? QStringLiteral(
">=" ) : QStringLiteral(
">" );
2646 caseString += QStringLiteral(
"WHEN @vector_tile_zoom %1 %2 AND @vector_tile_zoom <= %3 "
2647 "THEN %4 " ).arg( lowerComparator,
2653 const QVariant z = stops.last().toList().value( 0 );
2654 const QVariant v = stops.last().toList().value( 1 );
2655 QString vStr = v.toString();
2656 if ( ( QMetaType::Type )v.userType() == QMetaType::QVariantList )
2659 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 "
2660 "THEN ( ( %2 ) * %3 ) END" ).arg( z.toString() ).arg( vStr ).arg( multiplier );
2664 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 "
2665 "THEN %2 END" ).arg( z.toString() ).arg( v.toDouble() * multiplier );
2673 QString caseString = QStringLiteral(
"CASE " );
2675 for (
int i = 0; i < stops.length() - 1; ++i )
2678 const QVariant bz = stops.value( i ).toList().value( 0 );
2679 const QString bv = stops.value( i ).toList().value( 1 ).toString();
2680 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2682 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2687 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2688 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2690 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2694 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2695 "THEN %3 " ).arg( bz.toString(),
2699 caseString += QStringLiteral(
"ELSE %1 END" ).arg(
QgsExpression::quotedValue( conversionMap.value( stops.constLast().toList().value( 1 ).toString(),
2700 stops.constLast().toList().value( 1 ) ) ) );
2701 if ( defaultString )
2702 *defaultString = stops.constLast().toList().value( 1 ).toString();
2708 QString caseString = QStringLiteral(
"CASE " );
2710 bool isExpression =
false;
2711 for (
int i = 0; i < stops.length() - 1; ++i )
2714 const QVariant bz = stops.value( i ).toList().value( 0 );
2715 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2717 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2722 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2723 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2725 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2729 QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
2730 if ( fieldPart.isEmpty() )
2731 fieldPart = QStringLiteral(
"''" );
2732 else if ( !isExpression )
2735 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom < %2 "
2736 "THEN %3 " ).arg( bz.toString(),
2742 const QVariant bz = stops.constLast().toList().value( 0 );
2743 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2745 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2749 QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
2750 if ( fieldPart.isEmpty() )
2751 fieldPart = QStringLiteral(
"''" );
2752 else if ( !isExpression )
2755 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 "
2756 "THEN %3 " ).arg( bz.toString(),
2760 QString defaultPart = processLabelField( stops.constFirst().toList().value( 1 ).toString(), isExpression );
2761 if ( defaultPart.isEmpty() )
2762 defaultPart = QStringLiteral(
"''" );
2763 else if ( !isExpression )
2765 caseString += QStringLiteral(
"ELSE %1 END" ).arg( defaultPart );
2772 const QString method = json.
value( 0 ).toString();
2773 if ( method == QLatin1String(
"interpolate" ) )
2777 else if ( method == QLatin1String(
"match" ) )
2779 return parseMatchList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
2781 else if ( method == QLatin1String(
"step" ) )
2783 return parseStepList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
2793 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
2794 if ( attribute.isEmpty() )
2796 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
2800 QString caseString = QStringLiteral(
"CASE " );
2802 for (
int i = 2; i < json.length() - 1; i += 2 )
2805 QVariant variantKeys = json.value( i );
2806 if ( variantKeys.userType() == QMetaType::Type::QVariantList || variantKeys.userType() == QMetaType::Type::QStringList )
2807 keys = variantKeys.toList();
2809 keys = {variantKeys};
2811 QStringList matchString;
2812 for (
const QVariant &key : keys )
2817 const QVariant value = json.value( i + 1 );
2819 QString valueString;
2824 if ( value.userType() == QMetaType::Type::QVariantList || value.userType() == QMetaType::Type::QStringList )
2830 const QColor color =
parseColor( value, context );
2838 const double v = value.toDouble() * multiplier;
2839 valueString = QString::number( v );
2845 const double v = value.toDouble() * maxOpacity;
2846 valueString = QString::number( v );
2852 valueString = QStringLiteral(
"array(%1,%2)" ).arg( value.toList().value( 0 ).toDouble() * multiplier,
2853 value.toList().value( 0 ).toDouble() * multiplier );
2858 if ( matchString.count() == 1 )
2860 caseString += QStringLiteral(
"WHEN %1 IS %2 THEN %3 " ).arg( attribute, matchString.at( 0 ), valueString );
2864 caseString += QStringLiteral(
"WHEN %1 IN (%2) THEN %3 " ).arg( attribute, matchString.join(
',' ), valueString );
2868 QVariant lastValue = json.constLast();
2871 switch ( lastValue.userType() )
2873 case QMetaType::Type::QVariantList:
2874 case QMetaType::Type::QStringList:
2875 elseValue =
parseValueList( lastValue.toList(), type, context, multiplier, maxOpacity, defaultColor, defaultNumber ).
asExpression();
2884 const QColor color =
parseColor( lastValue, context );
2886 *defaultColor = color;
2894 const double v = json.constLast().toDouble() * multiplier;
2895 if ( defaultNumber )
2897 elseValue = QString::number( v );
2903 const double v = json.constLast().toDouble() * maxOpacity;
2904 if ( defaultNumber )
2906 elseValue = QString::number( v );
2912 elseValue = QStringLiteral(
"array(%1,%2)" )
2913 .arg( json.constLast().toList().value( 0 ).toDouble() * multiplier )
2914 .arg( json.constLast().toList().value( 0 ).toDouble() * multiplier );
2923 caseString += QStringLiteral(
"ELSE %1 END" ).arg( elseValue );
2929 const QString expression =
parseExpression( json.value( 1 ).toList(), context );
2930 if ( expression.isEmpty() )
2932 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
2936 QString caseString = QStringLiteral(
"CASE " );
2939 for (
int i = json.length() - 2; i > 0; i -= 2 )
2941 const QVariant stepValue = json.value( i + 1 );
2943 QString valueString;
2944 if ( stepValue.canConvert<QVariantList>() && ( stepValue.toList().count() != 2 || type !=
PropertyType::Point ) )
2954 const QColor color =
parseColor( stepValue, context );
2961 const double v = stepValue.toDouble() * multiplier;
2962 valueString = QString::number( v );
2968 const double v = stepValue.toDouble() * maxOpacity;
2969 valueString = QString::number( v );
2975 valueString = QStringLiteral(
"array(%1,%2)" ).arg(
2976 stepValue.toList().value( 0 ).toDouble() * multiplier ).arg(
2977 stepValue.toList().value( 0 ).toDouble() * multiplier
2987 caseString += QStringLiteral(
" WHEN %1 >= %2 THEN (%3) " ).arg( expression, stepKey, valueString );
2991 caseString += QStringLiteral(
"ELSE (%1) END" ).arg( valueString );
2999 if ( json.value( 0 ).toString() != QLatin1String(
"interpolate" ) )
3001 context.
pushWarning( QObject::tr(
"%1: Could not interpret value list" ).arg( context.
layerId() ) );
3006 const QString technique = json.value( 1 ).toList().value( 0 ).toString();
3007 if ( technique == QLatin1String(
"linear" ) )
3009 else if ( technique == QLatin1String(
"exponential" ) )
3010 base = json.value( 1 ).toList(). value( 1 ).toDouble();
3011 else if ( technique == QLatin1String(
"cubic-bezier" ) )
3013 context.
pushWarning( QObject::tr(
"%1: Cubic-bezier interpolation is not supported, linear used instead." ).arg( context.
layerId() ) );
3018 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation method %2" ).arg( context.
layerId(), technique ) );
3022 if ( json.value( 2 ).toList().value( 0 ).toString() != QLatin1String(
"zoom" ) )
3024 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation input %2" ).arg( context.
layerId(), json.value( 2 ).toString() ) );
3030 for (
int i = 3; i < json.length(); i += 2 )
3032 stops.push_back( QVariantList() << json.value( i ).toString() << json.value( i + 1 ) );
3036 props.insert( QStringLiteral(
"stops" ), stops );
3037 props.insert( QStringLiteral(
"base" ), base );
3057 if ( ( QMetaType::Type )colorExpression.userType() == QMetaType::QVariantList )
3061 return parseValue( colorExpression, context,
true );
3066 if ( color.userType() != QMetaType::Type::QString )
3068 context.
pushWarning( QObject::tr(
"%1: Could not parse non-string color %2, skipping" ).arg( context.
layerId(), color.toString() ) );
3077 hue = std::max( 0, color.hslHue() );
3078 saturation = color.hslSaturation() / 255.0 * 100;
3079 lightness = color.lightness() / 255.0 * 100;
3080 alpha = color.alpha();
3088 context = *contextPtr;
3092 if ( valueMin.canConvert( QMetaType::Double ) && valueMax.canConvert( QMetaType::Double ) )
3094 bool minDoubleOk =
true;
3095 const double min = valueMin.toDouble( &minDoubleOk );
3096 bool maxDoubleOk =
true;
3097 const double max = valueMax.toDouble( &maxDoubleOk );
3098 if ( minDoubleOk && maxDoubleOk &&
qgsDoubleNear( min, max ) )
3100 return QString::number( min * multiplier );
3104 QString minValueExpr = valueMin.toString();
3105 QString maxValueExpr = valueMax.toString();
3106 if ( valueMin.userType() == QMetaType::Type::QVariantList )
3110 if ( valueMax.userType() == QMetaType::Type::QVariantList )
3116 if ( minValueExpr == maxValueExpr )
3118 expression = minValueExpr;
3124 expression = QStringLiteral(
"scale_linear(@vector_tile_zoom,%1,%2,%3,%4)" ).arg( zoomMin )
3126 .arg( minValueExpr )
3127 .arg( maxValueExpr );
3133 QString ratioExpr = QStringLiteral(
"(%1^(@vector_tile_zoom - %2) - 1) / (%1^(%3 - %2) - 1)" ).arg( base ).arg( zoomMin ).arg( zoomMax );
3134 expression = QStringLiteral(
"(%1) + (%2) * ((%3) - (%1))" ).arg( minValueExpr ).arg( ratioExpr ).arg( maxValueExpr );
3144 if ( multiplier != 1 )
3145 return QStringLiteral(
"(%1) * %2" ).arg( expression ).arg( multiplier );
3152 if ( style == QLatin1String(
"round" ) )
3153 return Qt::RoundCap;
3154 else if ( style == QLatin1String(
"square" ) )
3155 return Qt::SquareCap;
3162 if ( style == QLatin1String(
"bevel" ) )
3163 return Qt::BevelJoin;
3164 else if ( style == QLatin1String(
"round" ) )
3165 return Qt::RoundJoin;
3167 return Qt::MiterJoin;
3172 QString op = expression.value( 0 ).toString();
3173 if ( op == QLatin1String(
"%" ) && expression.size() >= 3 )
3175 return QStringLiteral(
"%1 %2 %3" ).arg( parseValue( expression.value( 1 ), context ),
3177 parseValue( expression.value( 2 ), context ) );
3179 else if ( op == QLatin1String(
"to-number" ) )
3181 return QStringLiteral(
"to_real(%1)" ).arg( parseValue( expression.value( 1 ), context ) );
3183 if ( op == QLatin1String(
"literal" ) )
3185 return expression.value( 1 ).toString();
3187 else if ( op == QLatin1String(
"all" )
3188 || op == QLatin1String(
"any" )
3189 || op == QLatin1String(
"none" ) )
3192 for (
int i = 1; i < expression.size(); ++i )
3194 const QString part = parseValue( expression.at( i ), context );
3195 if ( part.isEmpty() )
3197 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3203 if ( op == QLatin1String(
"none" ) )
3204 return QStringLiteral(
"NOT (%1)" ).arg( parts.join( QLatin1String(
") AND NOT (" ) ) );
3206 QString operatorString;
3207 if ( op == QLatin1String(
"all" ) )
3208 operatorString = QStringLiteral(
") AND (" );
3209 else if ( op == QLatin1String(
"any" ) )
3210 operatorString = QStringLiteral(
") OR (" );
3212 return QStringLiteral(
"(%1)" ).arg( parts.join( operatorString ) );
3214 else if ( op ==
'!' )
3217 QVariantList contraJsonExpr = expression.value( 1 ).toList();
3218 contraJsonExpr[0] = QString( op + contraJsonExpr[0].toString() );
3220 return parseKey( contraJsonExpr, context );
3222 else if ( op == QLatin1String(
"==" )
3223 || op == QLatin1String(
"!=" )
3224 || op == QLatin1String(
">=" )
3226 || op == QLatin1String(
"<=" )
3230 if ( op == QLatin1String(
"==" ) )
3231 op = QStringLiteral(
"IS" );
3232 else if ( op == QLatin1String(
"!=" ) )
3233 op = QStringLiteral(
"IS NOT" );
3234 return QStringLiteral(
"%1 %2 %3" ).arg( parseKey( expression.value( 1 ), context ),
3235 op, parseValue( expression.value( 2 ), context ) );
3237 else if ( op == QLatin1String(
"has" ) )
3239 return parseKey( expression.value( 1 ), context ) + QStringLiteral(
" IS NOT NULL" );
3241 else if ( op == QLatin1String(
"!has" ) )
3243 return parseKey( expression.value( 1 ), context ) + QStringLiteral(
" IS NULL" );
3245 else if ( op == QLatin1String(
"in" ) || op == QLatin1String(
"!in" ) )
3247 const QString key = parseKey( expression.value( 1 ), context );
3250 QVariantList values = expression.mid( 2 );
3251 if ( expression.size() == 3
3252 && expression.at( 2 ).userType() == QMetaType::Type::QVariantList && expression.at( 2 ).toList().count() > 1
3253 && expression.at( 2 ).toList().at( 0 ).toString() == QStringLiteral(
"literal" ) )
3255 values = expression.at( 2 ).toList().at( 1 ).toList();
3258 for (
const QVariant &value : std::as_const( values ) )
3260 const QString part = parseValue( value, context );
3261 if ( part.isEmpty() )
3263 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3268 if ( op == QLatin1String(
"in" ) )
3269 return QStringLiteral(
"%1 IN (%2)" ).arg( key, parts.join( QLatin1String(
", " ) ) );
3271 return QStringLiteral(
"(%1 IS NULL OR %1 NOT IN (%2))" ).arg( key, parts.join( QLatin1String(
", " ) ) );
3273 else if ( op == QLatin1String(
"get" ) )
3275 return parseKey( expression.value( 1 ), context );
3277 else if ( op == QLatin1String(
"match" ) )
3279 const QString attribute = expression.value( 1 ).toList().value( 1 ).toString();
3281 if ( expression.size() == 5
3282 && expression.at( 3 ).userType() == QMetaType::Type::Bool && expression.at( 3 ).toBool() ==
true
3283 && expression.at( 4 ).userType() == QMetaType::Type::Bool && expression.at( 4 ).toBool() ==
false )
3286 if ( expression.at( 2 ).userType() == QMetaType::Type::QVariantList || expression.at( 2 ).userType() == QMetaType::Type::QStringList )
3289 for (
const QVariant &p : expression.at( 2 ).toList() )
3291 parts << parseValue( p, context );
3294 if ( parts.size() > 1 )
3299 else if ( expression.at( 2 ).userType() == QMetaType::Type::QString || expression.at( 2 ).userType() == QMetaType::Type::Int
3300 || expression.at( 2 ).userType() == QMetaType::Type::Double || expression.at( 2 ).userType() == QMetaType::Type::LongLong )
3306 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3312 QString caseString = QStringLiteral(
"CASE " );
3313 for (
int i = 2; i < expression.size() - 2; i += 2 )
3315 if ( expression.at( i ).userType() == QMetaType::Type::QVariantList || expression.at( i ).userType() == QMetaType::Type::QStringList )
3318 for (
const QVariant &p : expression.at( i ).toList() )
3323 if ( parts.size() > 1 )
3328 else if ( expression.at( i ).userType() == QMetaType::Type::QString || expression.at( i ).userType() == QMetaType::Type::Int
3329 || expression.at( i ).userType() == QMetaType::Type::Double || expression.at( i ).userType() == QMetaType::Type::LongLong )
3334 caseString += QStringLiteral(
"THEN %1 " ).arg( parseValue( expression.at( i + 1 ), context, colorExpected ) );
3336 caseString += QStringLiteral(
"ELSE %1 END" ).arg( parseValue( expression.last(), context, colorExpected ) );
3340 else if ( op == QLatin1String(
"to-string" ) )
3342 return QStringLiteral(
"to_string(%1)" ).arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3344 else if ( op == QLatin1String(
"case" ) )
3346 QString caseString = QStringLiteral(
"CASE" );
3347 for (
int i = 1; i < expression.size() - 2; i += 2 )
3349 const QString condition =
parseExpression( expression.value( i ).toList(), context );
3350 const QString value = parseValue( expression.value( i + 1 ), context );
3351 caseString += QStringLiteral(
" WHEN (%1) THEN %2" ).arg( condition, value );
3353 const QString value = parseValue( expression.constLast(), context );
3354 caseString += QStringLiteral(
" ELSE %1 END" ).arg( value );
3357 else if ( op == QLatin1String(
"zoom" ) && expression.count() == 1 )
3359 return QStringLiteral(
"@vector_tile_zoom" );
3361 else if ( op == QLatin1String(
"concat" ) )
3363 QString concatString = QStringLiteral(
"concat(" );
3364 for (
int i = 1; i < expression.size(); i++ )
3367 concatString += QLatin1String(
", " );
3368 concatString += parseValue( expression.value( i ), context );
3370 concatString += QLatin1Char(
')' );
3371 return concatString;
3373 else if ( op == QLatin1String(
"length" ) )
3375 return QStringLiteral(
"length(%1)" ).arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3377 else if ( op == QLatin1String(
"step" ) )
3379 const QString stepExpression =
parseExpression( expression.value( 1 ).toList(), context );
3380 if ( stepExpression.isEmpty() )
3382 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3386 QString caseString = QStringLiteral(
"CASE " );
3388 for (
int i = expression.length() - 2; i > 0; i -= 2 )
3390 const QString stepValue = parseValue( expression.value( i + 1 ), context, colorExpected );
3394 caseString += QStringLiteral(
" WHEN %1 >= %2 THEN (%3) " ).arg( stepExpression, stepKey, stepValue );
3398 caseString += QStringLiteral(
"ELSE (%1) END" ).arg( stepValue );
3405 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression \"%2\"" ).arg( context.
layerId(), op ) );
3414 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3418 const QVariantMap spriteDefinition = context.
spriteDefinitions().value( name ).toMap();
3419 if ( spriteDefinition.size() == 0 )
3421 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3425 const QImage sprite = context.
spriteImage().copy( spriteDefinition.value( QStringLiteral(
"x" ) ).toInt(),
3426 spriteDefinition.value( QStringLiteral(
"y" ) ).toInt(),
3427 spriteDefinition.value( QStringLiteral(
"width" ) ).toInt(),
3428 spriteDefinition.value( QStringLiteral(
"height" ) ).toInt() );
3429 if ( sprite.isNull() )
3431 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3435 spriteSize = sprite.size() / spriteDefinition.value( QStringLiteral(
"pixelRatio" ) ).toDouble() * context.
pixelSizeConversionFactor();
3443 auto prepareBase64 = [](
const QImage & sprite )
3446 if ( !sprite.isNull() )
3449 QBuffer buffer( &blob );
3450 buffer.open( QIODevice::WriteOnly );
3451 sprite.save( &buffer,
"PNG" );
3453 const QByteArray encoded = blob.toBase64();
3454 path = QString( encoded );
3455 path.prepend( QLatin1String(
"base64:" ) );
3460 switch ( value.userType() )
3462 case QMetaType::Type::QString:
3464 QString spriteName = value.toString();
3465 const thread_local QRegularExpression fieldNameMatch( QStringLiteral(
"{([^}]+)}" ) );
3466 QRegularExpressionMatch match = fieldNameMatch.match( spriteName );
3467 if ( match.hasMatch() )
3469 const QString fieldName = match.captured( 1 );
3470 spriteProperty = QStringLiteral(
"CASE" );
3471 spriteSizeProperty = QStringLiteral(
"CASE" );
3473 spriteName.replace(
"(", QLatin1String(
"\\(" ) );
3474 spriteName.replace(
")", QLatin1String(
"\\)" ) );
3475 spriteName.replace( fieldNameMatch, QStringLiteral(
"([^\\/\\\\]+)" ) );
3476 const QRegularExpression fieldValueMatch( spriteName );
3478 for (
const QString &name : spriteNames )
3480 match = fieldValueMatch.match( name );
3481 if ( match.hasMatch() )
3485 const QString fieldValue = match.captured( 1 );
3487 path = prepareBase64( sprite );
3488 if ( spritePath.isEmpty() && !path.isEmpty() )
3494 spriteProperty += QStringLiteral(
" WHEN \"%1\" = '%2' THEN '%3'" )
3495 .arg( fieldName, fieldValue, path );
3496 spriteSizeProperty += QStringLiteral(
" WHEN \"%1\" = '%2' THEN %3" )
3497 .arg( fieldName ).arg( fieldValue ).arg( size.width() );
3501 spriteProperty += QLatin1String(
" END" );
3502 spriteSizeProperty += QLatin1String(
" END" );
3506 spriteProperty.clear();
3507 spriteSizeProperty.clear();
3508 const QImage sprite =
retrieveSprite( spriteName, context, spriteSize );
3509 spritePath = prepareBase64( sprite );
3514 case QMetaType::Type::QVariantMap:
3516 const QVariantList stops = value.toMap().value( QStringLiteral(
"stops" ) ).toList();
3517 if ( stops.size() == 0 )
3524 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, spriteSize );
3525 spritePath = prepareBase64( sprite );
3527 spriteProperty = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN '%2'" )
3528 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
3530 spriteSizeProperty = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN %2" )
3531 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
3532 .arg( spriteSize.width() );
3534 for (
int i = 0; i < stops.size() - 1; ++i )
3537 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, size );
3538 path = prepareBase64( sprite );
3540 spriteProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
3542 .arg( stops.value( i ).toList().value( 0 ).toString(),
3543 stops.value( i + 1 ).toList().value( 0 ).toString(),
3545 spriteSizeProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
3547 .arg( stops.value( i ).toList().value( 0 ).toString(),
3548 stops.value( i + 1 ).toList().value( 0 ).toString() )
3549 .arg( size.width() );
3551 sprite =
retrieveSprite( stops.last().toList().value( 1 ).toString(), context, size );
3552 path = prepareBase64( sprite );
3554 spriteProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
3556 .arg( stops.last().toList().value( 0 ).toString() )
3558 spriteSizeProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
3560 .arg( stops.last().toList().value( 0 ).toString() )
3561 .arg( size.width() );
3565 case QMetaType::Type::QVariantList:
3567 const QVariantList json = value.toList();
3568 const QString method = json.value( 0 ).toString();
3570 if ( method == QLatin1String(
"match" ) )
3572 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
3573 if ( attribute.isEmpty() )
3575 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
3579 spriteProperty = QStringLiteral(
"CASE" );
3580 spriteSizeProperty = QStringLiteral(
"CASE" );
3582 for (
int i = 2; i < json.length() - 1; i += 2 )
3584 const QVariant matchKey = json.value( i );
3585 const QVariant matchValue = json.value( i + 1 );
3586 QString matchString;
3587 switch ( matchKey.userType() )
3589 case QMetaType::Type::QVariantList:
3590 case QMetaType::Type::QStringList:
3592 const QVariantList keys = matchKey.toList();
3593 QStringList matchStringList;
3594 for (
const QVariant &key : keys )
3598 matchString = matchStringList.join(
',' );
3602 case QMetaType::Type::Bool:
3603 case QMetaType::Type::QString:
3604 case QMetaType::Type::Int:
3605 case QMetaType::Type::LongLong:
3606 case QMetaType::Type::Double:
3613 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported sprite type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( value.userType() ) ) ) );
3618 const QImage sprite =
retrieveSprite( matchValue.toString(), context, spriteSize );
3619 spritePath = prepareBase64( sprite );
3621 spriteProperty += QStringLiteral(
" WHEN %1 IN (%2) "
3622 "THEN '%3'" ).arg( attribute,
3626 spriteSizeProperty += QStringLiteral(
" WHEN %1 IN (%2) "
3627 "THEN %3" ).arg( attribute,
3628 matchString ).arg( spriteSize.width() );
3631 if ( !json.constLast().toString().isEmpty() )
3633 const QImage sprite =
retrieveSprite( json.constLast().toString(), context, spriteSize );
3634 spritePath = prepareBase64( sprite );
3638 spritePath = QString();
3641 spriteProperty += QStringLiteral(
" ELSE '%1' END" ).arg( spritePath );
3642 spriteSizeProperty += QStringLiteral(
" ELSE %3 END" ).arg( spriteSize.width() );
3645 else if ( method == QLatin1String(
"step" ) )
3647 const QString expression =
parseExpression( json.value( 1 ).toList(), context );
3648 if ( expression.isEmpty() )
3650 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3654 spriteProperty = QStringLiteral(
"CASE" );
3655 spriteSizeProperty = QStringLiteral(
"CASE" );
3656 for (
int i = json.length() - 2; i > 2; i -= 2 )
3659 const QString stepValue = json.value( i + 1 ).toString();
3661 const QImage sprite =
retrieveSprite( stepValue, context, spriteSize );
3662 spritePath = prepareBase64( sprite );
3664 spriteProperty += QStringLiteral(
" WHEN %1 >= %2 THEN '%3' " ).arg( expression, stepKey, spritePath );
3665 spriteSizeProperty += QStringLiteral(
" WHEN %1 >= %2 THEN %3 " ).arg( expression ).arg( stepKey ).arg( spriteSize.width() );
3668 const QImage sprite =
retrieveSprite( json.at( 2 ).toString(), context, spriteSize );
3669 spritePath = prepareBase64( sprite );
3671 spriteProperty += QStringLiteral(
"ELSE '%1' END" ).arg( spritePath );
3672 spriteSizeProperty += QStringLiteral(
"ELSE %3 END" ).arg( spriteSize.width() );
3675 else if ( method == QLatin1String(
"case" ) )
3677 spriteProperty = QStringLiteral(
"CASE" );
3678 spriteSizeProperty = QStringLiteral(
"CASE" );
3679 for (
int i = 1; i < json.length() - 2; i += 2 )
3681 const QString caseExpression =
parseExpression( json.value( i ).toList(), context );
3682 const QString caseValue = json.value( i + 1 ).toString();
3684 const QImage sprite =
retrieveSprite( caseValue, context, spriteSize );
3685 spritePath = prepareBase64( sprite );
3687 spriteProperty += QStringLiteral(
" WHEN %1 THEN '%2' " ).arg( caseExpression, spritePath );
3688 spriteSizeProperty += QStringLiteral(
" WHEN %1 THEN %2 " ).arg( caseExpression ).arg( spriteSize.width() );
3690 const QImage sprite =
retrieveSprite( json.last().toString(), context, spriteSize );
3691 spritePath = prepareBase64( sprite );
3693 spriteProperty += QStringLiteral(
"ELSE '%1' END" ).arg( spritePath );
3694 spriteSizeProperty += QStringLiteral(
"ELSE %3 END" ).arg( spriteSize.width() );
3699 context.
pushWarning( QObject::tr(
"%1: Could not interpret sprite value list with method %2" ).arg( context.
layerId(), method ) );
3705 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported sprite type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( value.userType() ) ) ) );
3715 switch ( value.userType() )
3717 case QMetaType::Type::QVariantList:
3718 case QMetaType::Type::QStringList:
3721 case QMetaType::Type::Bool:
3722 case QMetaType::Type::QString:
3723 if ( colorExpected )
3728 return parseValue(
c, context );
3733 case QMetaType::Type::Int:
3734 case QMetaType::Type::LongLong:
3735 case QMetaType::Type::Double:
3736 return value.toString();
3738 case QMetaType::Type::QColor:
3739 c = value.value<QColor>();
3740 return QString(
"color_rgba(%1,%2,%3,%4)" ).arg(
c.red() ).arg(
c.green() ).arg(
c.blue() ).arg(
c.alpha() );
3743 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression part" ).arg( context.
layerId() ) );
3751 if ( value.toString() == QLatin1String(
"$type" ) )
3753 return QStringLiteral(
"_geom_type" );
3755 if ( value.toString() == QLatin1String(
"level" ) )
3757 return QStringLiteral(
"level" );
3759 else if ( ( value.userType() == QMetaType::Type::QVariantList && value.toList().size() == 1 ) || value.userType() == QMetaType::Type::QStringList )
3761 if ( value.toList().size() > 1 )
3762 return value.toList().at( 1 ).toString();
3765 QString valueString = value.toList().value( 0 ).toString();
3766 if ( valueString == QLatin1String(
"geometry-type" ) )
3768 return QStringLiteral(
"_geom_type" );
3773 else if ( value.userType() == QMetaType::Type::QVariantList && value.toList().size() > 1 )
3780QString QgsMapBoxGlStyleConverter::processLabelField(
const QString &
string,
bool &isExpression )
3784 const thread_local QRegularExpression singleFieldRx( QStringLiteral(
"^{([^}]+)}$" ) );
3785 const QRegularExpressionMatch match = singleFieldRx.match(
string );
3786 if ( match.hasMatch() )
3788 isExpression =
false;
3789 return match.captured( 1 );
3792 const thread_local QRegularExpression multiFieldRx( QStringLiteral(
"(?={[^}]+})" ) );
3793 const QStringList parts =
string.split( multiFieldRx );
3794 if ( parts.size() > 1 )
3796 isExpression =
true;
3799 for (
const QString &part : parts )
3801 if ( part.isEmpty() )
3804 if ( !part.contains(
'{' ) )
3811 const QStringList split = part.split(
'}' );
3813 if ( !split.at( 1 ).isEmpty() )
3816 return QStringLiteral(
"concat(%1)" ).arg( res.join(
',' ) );
3820 isExpression =
false;
3827 return mRenderer ? mRenderer->clone() :
nullptr;
3832 return mLabeling ? mLabeling->clone() :
nullptr;
3842 return mRasterSubLayers;
3847 QList<QgsMapLayer *> subLayers;
3850 const QString sourceName = subLayer.source();
3851 std::unique_ptr< QgsRasterLayer > rl;
3858 rl->pipe()->setDataDefinedProperties( subLayer.dataDefinedProperties() );
3865 subLayers.append( rl.release() );
3874 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
3877 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
3878 context = tmpContext.get();
3883 if (
string.compare( QLatin1String(
"vector" ), Qt::CaseInsensitive ) == 0 )
3885 else if (
string.compare( QLatin1String(
"raster" ), Qt::CaseInsensitive ) == 0 )
3887 else if (
string.compare( QLatin1String(
"raster-dem" ), Qt::CaseInsensitive ) == 0 )
3889 else if (
string.compare( QLatin1String(
"geojson" ), Qt::CaseInsensitive ) == 0 )
3891 else if (
string.compare( QLatin1String(
"image" ), Qt::CaseInsensitive ) == 0 )
3893 else if (
string.compare( QLatin1String(
"video" ), Qt::CaseInsensitive ) == 0 )
3895 context->
pushWarning( QObject::tr(
"Invalid source type \"%1\" for source \"%2\"" ).arg(
string, name ) );
3901 const QString name = it.key();
3902 const QVariantMap jsonSource = it.value().toMap();
3903 const QString typeString = jsonSource.value( QStringLiteral(
"type" ) ).toString();
3926 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
3929 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
3930 context = tmpContext.get();
3933 std::unique_ptr< QgsMapBoxGlStyleRasterSource > raster = std::make_unique< QgsMapBoxGlStyleRasterSource >( name );
3934 if ( raster->setFromJson( source, context ) )
3935 mSources.append( raster.release() );
3938bool QgsMapBoxGlStyleConverter::numericArgumentsOnly(
const QVariant &bottomVariant,
const QVariant &topVariant,
double &bottom,
double &top )
3940 if ( bottomVariant.canConvert( QMetaType::Double ) && topVariant.canConvert( QMetaType::Double ) )
3942 bool bDoubleOk, tDoubleOk;
3943 bottom = bottomVariant.toDouble( &bDoubleOk );
3944 top = topVariant.toDouble( &tDoubleOk );
3945 return ( bDoubleOk && tDoubleOk );
3956 mWarnings << warning;
3971 return mSizeConversionFactor;
3976 mSizeConversionFactor = sizeConversionFactor;
3981 return mSpriteImage;
3986 return mSpriteDefinitions;
3991 mSpriteImage = image;
3992 mSpriteDefinitions = definitions;
4042 mAttribution = json.value( QStringLiteral(
"attribution" ) ).toString();
4044 const QString scheme = json.value( QStringLiteral(
"scheme" ), QStringLiteral(
"xyz" ) ).toString();
4045 if ( scheme.compare( QLatin1String(
"xyz" ) ) == 0 )
4051 context->
pushWarning( QObject::tr(
"%1 scheme is not supported for raster source %2" ).arg( scheme,
name() ) );
4055 mMinZoom = json.value( QStringLiteral(
"minzoom" ), QStringLiteral(
"0" ) ).toInt();
4056 mMaxZoom = json.value( QStringLiteral(
"maxzoom" ), QStringLiteral(
"22" ) ).toInt();
4057 mTileSize = json.value( QStringLiteral(
"tileSize" ), QStringLiteral(
"512" ) ).toInt();
4059 const QVariantList
tiles = json.value( QStringLiteral(
"tiles" ) ).toList();
4060 for (
const QVariant &tile :
tiles )
4062 mTiles.append( tile.toString() );
4071 parts.insert( QStringLiteral(
"type" ), QStringLiteral(
"xyz" ) );
4072 parts.insert( QStringLiteral(
"url" ), mTiles.value( 0 ) );
4074 if ( mTileSize == 256 )
4075 parts.insert( QStringLiteral(
"tilePixelRation" ), QStringLiteral(
"1" ) );
4076 else if ( mTileSize == 512 )
4077 parts.insert( QStringLiteral(
"tilePixelRation" ), QStringLiteral(
"2" ) );
4079 parts.insert( QStringLiteral(
"zmax" ), QString::number( mMaxZoom ) );
4080 parts.insert( QStringLiteral(
"zmin" ), QString::number( mMinZoom ) );
4082 std::unique_ptr< QgsRasterLayer > rl = std::make_unique< QgsRasterLayer >(
QgsProviderRegistry::instance()->encodeUri( QStringLiteral(
"wms" ), parts ),
name(), QStringLiteral(
"wms" ) );
4083 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.
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,...
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.
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 setPixelSizeConversionFactor(double sizeConversionFactor)
Sets the pixel size conversion factor, used to scale the original pixel sizes when converting styles.
Qgis::RenderUnit targetUnit() const
Returns the target unit type.
void setSprites(const QImage &image, const QVariantMap &definitions)
Sets the sprite image and definitions JSON to use during conversion.
QString layerId() const
Returns the layer ID of the layer currently being converted.
QImage spriteImage() const
Returns the sprite image to use during conversion, or an invalid image if this is not set.
void clearWarnings()
Clears the list of warning messages.
QVariantMap spriteDefinitions() const
Returns the sprite definitions to use during conversion.
static QString parseOpacityStops(double base, const QVariantList &stops, int maxOpacity, QgsMapBoxGlStyleConversionContext &context)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate alpha ...
static QString parseColorExpression(const QVariant &colorExpression, QgsMapBoxGlStyleConversionContext &context)
Converts an expression representing a color to a string (can be color string or an expression where a...
static QString parseStops(double base, const QVariantList &stops, double multiplier, QgsMapBoxGlStyleConversionContext &context)
Parses a list of interpolation stops.
QgsVectorTileRenderer * renderer() const
Returns a new instance of a vector tile renderer representing the converted style,...
static QString parseExpression(const QVariantList &expression, QgsMapBoxGlStyleConversionContext &context, bool colorExpected=false)
Converts a MapBox GL expression to a QGIS expression.
PropertyType
Property types, for interpolated value conversion.
@ Point
Point/offset property.
@ Numeric
Numeric property (e.g. line width, text size)
@ Opacity
Opacity property.
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 QString interpolateExpression(double zoomMin, double zoomMax, QVariant valueMin, QVariant valueMax, double base, double multiplier=1, QgsMapBoxGlStyleConversionContext *contextPtr=0)
Generates an interpolation for values between valueMin and valueMax, scaled between the ranges zoomMi...
static QgsProperty 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...
static QgsProperty parseInterpolateOpacityByZoom(const QVariantMap &json, int maxOpacity, QgsMapBoxGlStyleConversionContext *contextPtr=0)
Interpolates opacity with either scale_linear() or scale_exp() (depending on base value).
void parseSources(const QVariantMap &sources, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse list of sources from JSON.
static QColor parseColor(const QVariant &color, QgsMapBoxGlStyleConversionContext &context)
Parses a color in one of these supported formats:
static bool parseSymbolLayerAsRenderer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as a renderer.
static bool parseFillLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context, bool isBackgroundStyle=false)
Parses a fill layer.
static void parseSymbolLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, bool &hasRenderer, QgsVectorTileBasicLabelingStyle &labelingStyle, bool &hasLabeling, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as renderer or labeling.
static 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.
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.
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.
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'
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.
@ 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 a 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 class for filling symbols 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 an 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)
Basic labeling configuration for vector tile layers.
Definition of map rendering of a subset of vector tile data.
void setEnabled(bool enabled)
Sets whether this style is enabled (used for rendering)
void setMinZoomLevel(int minZoom)
Sets minimum zoom level index (negative number means no limit).
void setLayerName(const QString &name)
Sets name of the sub-layer to render (empty layer means that all layers match)
void 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)
The default vector tile renderer implementation.
Base class for labeling configuration classes for vector tile layers.
Abstract base class for all vector tile renderer implementations.
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