48#include <QRegularExpression>
50#include "moc_qgsmapboxglstyleconverter.cpp"
61 if ( style.contains( QStringLiteral(
"sources" ) ) )
63 parseSources( style.value( QStringLiteral(
"sources" ) ).toMap(), context );
66 if ( style.contains( QStringLiteral(
"layers" ) ) )
68 parseLayers( style.value( QStringLiteral(
"layers" ) ).toList(), context );
72 mError = QObject::tr(
"Could not find layers list in JSON" );
85 qDeleteAll( mSources );
90 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
93 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
94 context = tmpContext.get();
97 QList<QgsVectorTileBasicRendererStyle> rendererStyles;
98 QList<QgsVectorTileBasicLabelingStyle> labelingStyles;
101 bool hasRendererBackgroundStyle =
false;
103 for (
const QVariant &layer : layers )
105 const QVariantMap jsonLayer = layer.toMap();
107 const QString layerType = jsonLayer.value( QStringLiteral(
"type" ) ).toString();
108 if ( layerType == QLatin1String(
"background" ) )
110 hasRendererBackgroundStyle =
parseFillLayer( jsonLayer, rendererBackgroundStyle, *context,
true );
111 if ( hasRendererBackgroundStyle )
121 const QString styleId = jsonLayer.value( QStringLiteral(
"id" ) ).toString();
124 if ( layerType.compare( QLatin1String(
"raster" ), Qt::CaseInsensitive ) == 0 )
127 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
128 if ( jsonPaint.contains( QStringLiteral(
"raster-opacity" ) ) )
130 const QVariant jsonRasterOpacity = jsonPaint.value( QStringLiteral(
"raster-opacity" ) );
131 double defaultOpacity = 1;
135 mRasterSubLayers.append( raster );
139 const QString layerName = jsonLayer.value( QStringLiteral(
"source-layer" ) ).toString();
141 const int minZoom = jsonLayer.value( QStringLiteral(
"minzoom" ), QStringLiteral(
"-1" ) ).toInt();
150 int maxZoom = jsonLayer.value( QStringLiteral(
"maxzoom" ), QStringLiteral(
"-1" ) ).toInt();
155 if ( jsonLayer.contains( QStringLiteral(
"visibility" ) ) )
157 visibilyStr = jsonLayer.value( QStringLiteral(
"visibility" ) ).toString();
159 else if ( jsonLayer.contains( QStringLiteral(
"layout" ) ) && jsonLayer.value( QStringLiteral(
"layout" ) ).userType() == QMetaType::Type::QVariantMap )
161 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
162 visibilyStr = jsonLayout.value( QStringLiteral(
"visibility" ) ).toString();
165 const bool enabled = visibilyStr != QLatin1String(
"none" );
167 QString filterExpression;
168 if ( jsonLayer.contains( QStringLiteral(
"filter" ) ) )
170 filterExpression =
parseExpression( jsonLayer.value( QStringLiteral(
"filter" ) ).toList(), *context );
176 bool hasRendererStyle =
false;
177 bool hasLabelingStyle =
false;
178 if ( layerType == QLatin1String(
"fill" ) )
180 hasRendererStyle =
parseFillLayer( jsonLayer, rendererStyle, *context );
182 else if ( layerType == QLatin1String(
"line" ) )
184 hasRendererStyle =
parseLineLayer( jsonLayer, rendererStyle, *context );
186 else if ( layerType == QLatin1String(
"circle" ) )
190 else if ( layerType == QLatin1String(
"symbol" ) )
192 parseSymbolLayer( jsonLayer, rendererStyle, hasRendererStyle, labelingStyle, hasLabelingStyle, *context );
196 mWarnings << QObject::tr(
"%1: Skipping unknown layer type %2" ).arg( context->
layerId(), layerType );
201 if ( hasRendererStyle )
209 rendererStyles.append( rendererStyle );
212 if ( hasLabelingStyle )
220 labelingStyles.append( labelingStyle );
223 mWarnings.append( context->
warnings() );
227 if ( hasRendererBackgroundStyle )
228 rendererStyles.prepend( rendererBackgroundStyle );
230 auto renderer = std::make_unique< QgsVectorTileBasicRenderer >();
231 renderer->setStyles( rendererStyles );
234 auto labeling = std::make_unique< QgsVectorTileBasicLabeling >();
235 labeling->setStyles( labelingStyles );
241 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
246 bool colorIsDataDefined =
false;
248 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsFillSymbol >() );
252 if ( jsonPaint.contains( isBackgroundStyle ? QStringLiteral(
"background-color" ) : QStringLiteral(
"fill-color" ) ) )
254 const QVariant jsonFillColor = jsonPaint.value( isBackgroundStyle ? QStringLiteral(
"background-color" ) : QStringLiteral(
"fill-color" ) );
255 switch ( jsonFillColor.userType() )
257 case QMetaType::Type::QVariantMap:
261 case QMetaType::Type::QVariantList:
262 case QMetaType::Type::QStringList:
263 colorIsDataDefined =
true;
267 case QMetaType::Type::QString:
268 fillColor =
parseColor( jsonFillColor.toString(), context );
273 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillColor.userType() ) ) ) );
281 fillColor = QColor( 0, 0, 0 );
284 QColor fillOutlineColor;
285 if ( !isBackgroundStyle )
287 if ( !jsonPaint.contains( QStringLiteral(
"fill-outline-color" ) ) )
289 if ( fillColor.isValid() )
290 fillOutlineColor = fillColor;
298 const QVariant jsonFillOutlineColor = jsonPaint.value( QStringLiteral(
"fill-outline-color" ) );
299 switch ( jsonFillOutlineColor.userType() )
301 case QMetaType::Type::QVariantMap:
305 case QMetaType::Type::QVariantList:
306 case QMetaType::Type::QStringList:
310 case QMetaType::Type::QString:
311 fillOutlineColor =
parseColor( jsonFillOutlineColor.toString(), context );
315 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-outline-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillOutlineColor.userType() ) ) ) );
321 double fillOpacity = -1.0;
322 double rasterOpacity = -1.0;
323 if ( jsonPaint.contains( isBackgroundStyle ? QStringLiteral(
"background-opacity" ) : QStringLiteral(
"fill-opacity" ) ) )
325 const QVariant jsonFillOpacity = jsonPaint.value( isBackgroundStyle ? QStringLiteral(
"background-opacity" ) : QStringLiteral(
"fill-opacity" ) );
326 switch ( jsonFillOpacity.userType() )
328 case QMetaType::Type::Int:
329 case QMetaType::Type::LongLong:
330 case QMetaType::Type::Double:
331 fillOpacity = jsonFillOpacity.toDouble();
332 rasterOpacity = fillOpacity;
335 case QMetaType::Type::QVariantMap:
348 case QMetaType::Type::QVariantList:
349 case QMetaType::Type::QStringList:
363 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillOpacity.userType() ) ) ) );
369 QPointF fillTranslate;
370 if ( jsonPaint.contains( QStringLiteral(
"fill-translate" ) ) )
372 const QVariant jsonFillTranslate = jsonPaint.value( QStringLiteral(
"fill-translate" ) );
373 switch ( jsonFillTranslate.userType() )
376 case QMetaType::Type::QVariantMap:
380 case QMetaType::Type::QVariantList:
381 case QMetaType::Type::QStringList:
387 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-translate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillTranslate.userType() ) ) ) );
393 Q_ASSERT( fillSymbol );
396 symbol->setOutputUnit( context.
targetUnit() );
399 if ( !fillTranslate.isNull() )
405 if ( jsonPaint.contains( isBackgroundStyle ? QStringLiteral(
"background-pattern" ) : QStringLiteral(
"fill-pattern" ) ) )
409 const QVariant fillPatternJson = jsonPaint.value( isBackgroundStyle ? QStringLiteral(
"background-pattern" ) : QStringLiteral(
"fill-pattern" ) );
412 fillColor = QColor();
413 fillOutlineColor = QColor();
420 QString spriteProperty, spriteSizeProperty;
422 if ( !sprite.isEmpty() )
427 rasterFill->
setWidth( spriteSize.width() );
431 if ( rasterOpacity >= 0 )
436 if ( !spriteProperty.isEmpty() )
443 symbol->appendSymbolLayer( rasterFill );
449 if ( fillOpacity != -1 )
451 symbol->setOpacity( fillOpacity );
462 if ( fillOutlineColor.isValid() && ( fillOutlineColor.alpha() == 255 || fillOutlineColor != fillColor ) )
473 if ( fillColor.isValid() )
477 else if ( colorIsDataDefined )
493 if ( !jsonLayer.contains( QStringLiteral(
"paint" ) ) )
495 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
500 QString rasterLineSprite;
502 const QVariantMap jsonPaint = jsonLayer.
value( QStringLiteral(
"paint" ) ).toMap();
503 if ( jsonPaint.contains( QStringLiteral(
"line-pattern" ) ) )
505 const QVariant jsonLinePattern = jsonPaint.value( QStringLiteral(
"line-pattern" ) );
506 switch ( jsonLinePattern.userType() )
508 case QMetaType::Type::QVariantMap:
509 case QMetaType::Type::QString:
512 QString spriteProperty, spriteSizeProperty;
518 case QMetaType::Type::QVariantList:
519 case QMetaType::Type::QStringList:
524 if ( rasterLineSprite.isEmpty() )
527 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-pattern property" ).arg( context.
layerId() ) );
534 if ( jsonPaint.contains( QStringLiteral(
"line-color" ) ) )
536 const QVariant jsonLineColor = jsonPaint.value( QStringLiteral(
"line-color" ) );
537 switch ( jsonLineColor.userType() )
539 case QMetaType::Type::QVariantMap:
544 case QMetaType::Type::QVariantList:
545 case QMetaType::Type::QStringList:
550 case QMetaType::Type::QString:
551 lineColor =
parseColor( jsonLineColor.toString(), context );
555 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineColor.userType() ) ) ) );
562 lineColor = QColor( 0, 0, 0 );
568 if ( jsonPaint.contains( QStringLiteral(
"line-width" ) ) )
570 const QVariant jsonLineWidth = jsonPaint.value( QStringLiteral(
"line-width" ) );
571 switch ( jsonLineWidth.userType() )
573 case QMetaType::Type::Int:
574 case QMetaType::Type::LongLong:
575 case QMetaType::Type::Double:
579 case QMetaType::Type::QVariantMap:
591 case QMetaType::Type::QVariantList:
592 case QMetaType::Type::QStringList:
604 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineWidth.userType() ) ) ) );
609 double lineOffset = 0.0;
610 if ( jsonPaint.contains( QStringLiteral(
"line-offset" ) ) )
612 const QVariant jsonLineOffset = jsonPaint.value( QStringLiteral(
"line-offset" ) );
613 switch ( jsonLineOffset.userType() )
615 case QMetaType::Type::Int:
616 case QMetaType::Type::LongLong:
617 case QMetaType::Type::Double:
621 case QMetaType::Type::QVariantMap:
626 case QMetaType::Type::QVariantList:
627 case QMetaType::Type::QStringList:
632 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineOffset.userType() ) ) ) );
637 double lineOpacity = -1.0;
639 if ( jsonPaint.contains( QStringLiteral(
"line-opacity" ) ) )
641 const QVariant jsonLineOpacity = jsonPaint.value( QStringLiteral(
"line-opacity" ) );
642 switch ( jsonLineOpacity.userType() )
644 case QMetaType::Type::Int:
645 case QMetaType::Type::LongLong:
646 case QMetaType::Type::Double:
647 lineOpacity = jsonLineOpacity.toDouble();
650 case QMetaType::Type::QVariantMap:
653 double defaultValue = 1.0;
662 case QMetaType::Type::QVariantList:
663 case QMetaType::Type::QStringList:
666 double defaultValue = 1.0;
677 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineOpacity.userType() ) ) ) );
682 QVector< double > dashVector;
683 if ( jsonPaint.contains( QStringLiteral(
"line-dasharray" ) ) )
685 const QVariant jsonLineDashArray = jsonPaint.value( QStringLiteral(
"line-dasharray" ) );
686 switch ( jsonLineDashArray.userType() )
688 case QMetaType::Type::QVariantMap:
690 QString arrayExpression;
693 arrayExpression = QStringLiteral(
"array_to_string(array_foreach(%1,@element * (%2)), ';')" )
694 .arg(
parseArrayStops( jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList(), context, 1 ),
699 arrayExpression = QStringLiteral(
"array_to_string(%1, ';')" ).arg(
parseArrayStops( jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList(), context, lineWidth ) );
703 const QVariantList dashSource = jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList().first().toList().value( 1 ).toList();
704 for (
const QVariant &v : dashSource )
706 dashVector << v.toDouble() * lineWidth;
711 case QMetaType::Type::QVariantList:
712 case QMetaType::Type::QStringList:
714 const QVariantList dashSource = jsonLineDashArray.toList();
716 if ( dashSource.at( 0 ).userType() == QMetaType::Type::QString )
722 .arg( property.asExpression(), lineWidthProperty.
asExpression() ) );
732 QVector< double > rawDashVectorSizes;
733 rawDashVectorSizes.reserve( dashSource.size() );
734 for (
const QVariant &v : dashSource )
736 rawDashVectorSizes << v.toDouble();
740 if ( rawDashVectorSizes.size() == 1 )
743 rawDashVectorSizes.clear();
745 else if ( rawDashVectorSizes.size() % 2 == 1 )
749 rawDashVectorSizes[0] = rawDashVectorSizes[0] + rawDashVectorSizes[rawDashVectorSizes.size() - 1];
750 rawDashVectorSizes.resize( rawDashVectorSizes.size() - 1 );
753 if ( !rawDashVectorSizes.isEmpty() && ( !lineWidthProperty.
asExpression().isEmpty() ) )
755 QStringList dashArrayStringParts;
756 dashArrayStringParts.reserve( rawDashVectorSizes.size() );
757 for (
double v : std::as_const( rawDashVectorSizes ) )
762 QString arrayExpression = QStringLiteral(
"array_to_string(array_foreach(array(%1),@element * (%2)), ';')" )
763 .arg( dashArrayStringParts.join(
',' ),
769 for (
double v : std::as_const( rawDashVectorSizes ) )
771 dashVector << v *lineWidth;
778 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-dasharray type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineDashArray.userType() ) ) ) );
783 Qt::PenCapStyle penCapStyle = Qt::FlatCap;
784 Qt::PenJoinStyle penJoinStyle = Qt::MiterJoin;
785 if ( jsonLayer.contains( QStringLiteral(
"layout" ) ) )
787 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
788 if ( jsonLayout.contains( QStringLiteral(
"line-cap" ) ) )
790 penCapStyle =
parseCapStyle( jsonLayout.value( QStringLiteral(
"line-cap" ) ).toString() );
792 if ( jsonLayout.contains( QStringLiteral(
"line-join" ) ) )
794 penJoinStyle =
parseJoinStyle( jsonLayout.value( QStringLiteral(
"line-join" ) ).toString() );
798 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsLineSymbol >() );
799 symbol->setOutputUnit( context.
targetUnit() );
801 if ( !rasterLineSprite.isEmpty() )
811 if ( lineOpacity != -1 )
813 symbol->setOpacity( lineOpacity );
819 symbol->setDataDefinedProperties( ddProperties );
821 if ( lineWidth != -1 )
825 symbol->changeSymbolLayer( 0, lineSymbol );
830 Q_ASSERT( lineSymbol );
840 if ( lineOpacity != -1 )
842 symbol->setOpacity( lineOpacity );
848 symbol->setDataDefinedProperties( ddProperties );
850 if ( lineColor.isValid() )
854 if ( lineWidth != -1 )
858 if ( !dashVector.empty() )
872 if ( !jsonLayer.contains( QStringLiteral(
"paint" ) ) )
874 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
878 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
882 QColor circleFillColor;
883 if ( jsonPaint.contains( QStringLiteral(
"circle-color" ) ) )
885 const QVariant jsonCircleColor = jsonPaint.value( QStringLiteral(
"circle-color" ) );
886 switch ( jsonCircleColor.userType() )
888 case QMetaType::Type::QVariantMap:
892 case QMetaType::Type::QVariantList:
893 case QMetaType::Type::QStringList:
897 case QMetaType::Type::QString:
898 circleFillColor =
parseColor( jsonCircleColor.toString(), context );
902 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleColor.userType() ) ) ) );
909 circleFillColor = QColor( 0, 0, 0 );
913 double circleDiameter = 10.0;
914 if ( jsonPaint.contains( QStringLiteral(
"circle-radius" ) ) )
916 const QVariant jsonCircleRadius = jsonPaint.value( QStringLiteral(
"circle-radius" ) );
917 switch ( jsonCircleRadius.userType() )
919 case QMetaType::Type::Int:
920 case QMetaType::Type::LongLong:
921 case QMetaType::Type::Double:
925 case QMetaType::Type::QVariantMap:
930 case QMetaType::Type::QVariantList:
931 case QMetaType::Type::QStringList:
936 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-radius type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleRadius.userType() ) ) ) );
941 double circleOpacity = -1.0;
942 if ( jsonPaint.contains( QStringLiteral(
"circle-opacity" ) ) )
944 const QVariant jsonCircleOpacity = jsonPaint.value( QStringLiteral(
"circle-opacity" ) );
945 switch ( jsonCircleOpacity.userType() )
947 case QMetaType::Type::Int:
948 case QMetaType::Type::LongLong:
949 case QMetaType::Type::Double:
950 circleOpacity = jsonCircleOpacity.toDouble();
953 case QMetaType::Type::QVariantMap:
957 case QMetaType::Type::QVariantList:
958 case QMetaType::Type::QStringList:
963 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleOpacity.userType() ) ) ) );
967 if ( ( circleOpacity != -1 ) && circleFillColor.isValid() )
969 circleFillColor.setAlphaF( circleOpacity );
973 QColor circleStrokeColor;
974 if ( jsonPaint.contains( QStringLiteral(
"circle-stroke-color" ) ) )
976 const QVariant jsonCircleStrokeColor = jsonPaint.value( QStringLiteral(
"circle-stroke-color" ) );
977 switch ( jsonCircleStrokeColor.userType() )
979 case QMetaType::Type::QVariantMap:
983 case QMetaType::Type::QVariantList:
984 case QMetaType::Type::QStringList:
988 case QMetaType::Type::QString:
989 circleStrokeColor =
parseColor( jsonCircleStrokeColor.toString(), context );
993 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-stroke-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleStrokeColor.userType() ) ) ) );
999 double circleStrokeWidth = -1.0;
1000 if ( jsonPaint.contains( QStringLiteral(
"circle-stroke-width" ) ) )
1002 const QVariant circleStrokeWidthJson = jsonPaint.value( QStringLiteral(
"circle-stroke-width" ) );
1003 switch ( circleStrokeWidthJson.userType() )
1005 case QMetaType::Type::Int:
1006 case QMetaType::Type::LongLong:
1007 case QMetaType::Type::Double:
1011 case QMetaType::Type::QVariantMap:
1012 circleStrokeWidth = -1.0;
1016 case QMetaType::Type::QVariantList:
1017 case QMetaType::Type::QStringList:
1022 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-stroke-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( circleStrokeWidthJson.userType() ) ) ) );
1027 double circleStrokeOpacity = -1.0;
1028 if ( jsonPaint.contains( QStringLiteral(
"circle-stroke-opacity" ) ) )
1030 const QVariant jsonCircleStrokeOpacity = jsonPaint.value( QStringLiteral(
"circle-stroke-opacity" ) );
1031 switch ( jsonCircleStrokeOpacity.userType() )
1033 case QMetaType::Type::Int:
1034 case QMetaType::Type::LongLong:
1035 case QMetaType::Type::Double:
1036 circleStrokeOpacity = jsonCircleStrokeOpacity.toDouble();
1039 case QMetaType::Type::QVariantMap:
1043 case QMetaType::Type::QVariantList:
1044 case QMetaType::Type::QStringList:
1049 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-stroke-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleStrokeOpacity.userType() ) ) ) );
1053 if ( ( circleStrokeOpacity != -1 ) && circleStrokeColor.isValid() )
1055 circleStrokeColor.setAlphaF( circleStrokeOpacity );
1059 QPointF circleTranslate;
1060 if ( jsonPaint.contains( QStringLiteral(
"circle-translate" ) ) )
1062 const QVariant jsonCircleTranslate = jsonPaint.value( QStringLiteral(
"circle-translate" ) );
1063 switch ( jsonCircleTranslate.userType() )
1066 case QMetaType::Type::QVariantMap:
1070 case QMetaType::Type::QVariantList:
1071 case QMetaType::Type::QStringList:
1077 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-translate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleTranslate.userType() ) ) ) );
1082 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsMarkerSymbol >() );
1084 Q_ASSERT( markerSymbolLayer );
1087 symbol->setOutputUnit( context.
targetUnit() );
1088 symbol->setDataDefinedProperties( ddProperties );
1090 if ( !circleTranslate.isNull() )
1092 markerSymbolLayer->
setOffset( circleTranslate );
1096 if ( circleFillColor.isValid() )
1100 if ( circleDiameter != -1 )
1102 markerSymbolLayer->
setSize( circleDiameter );
1105 if ( circleStrokeColor.isValid() )
1109 if ( circleStrokeWidth != -1 )
1122 hasLabeling =
false;
1123 hasRenderer =
false;
1125 if ( !jsonLayer.contains( QStringLiteral(
"layout" ) ) )
1127 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
1130 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
1131 if ( !jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
1137 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
1143 if ( jsonLayout.contains( QStringLiteral(
"text-size" ) ) )
1145 const QVariant jsonTextSize = jsonLayout.value( QStringLiteral(
"text-size" ) );
1146 switch ( jsonTextSize.userType() )
1148 case QMetaType::Type::Int:
1149 case QMetaType::Type::LongLong:
1150 case QMetaType::Type::Double:
1154 case QMetaType::Type::QVariantMap:
1160 case QMetaType::Type::QVariantList:
1161 case QMetaType::Type::QStringList:
1167 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextSize.userType() ) ) ) );
1171 if ( textSizeProperty )
1178 constexpr double EM_TO_CHARS = 2.0;
1180 double textMaxWidth = -1;
1181 if ( jsonLayout.contains( QStringLiteral(
"text-max-width" ) ) )
1183 const QVariant jsonTextMaxWidth = jsonLayout.value( QStringLiteral(
"text-max-width" ) );
1184 switch ( jsonTextMaxWidth.userType() )
1186 case QMetaType::Type::Int:
1187 case QMetaType::Type::LongLong:
1188 case QMetaType::Type::Double:
1189 textMaxWidth = jsonTextMaxWidth.toDouble() * EM_TO_CHARS;
1192 case QMetaType::Type::QVariantMap:
1196 case QMetaType::Type::QVariantList:
1197 case QMetaType::Type::QStringList:
1202 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-max-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextMaxWidth.userType() ) ) ) );
1209 textMaxWidth = 10 * EM_TO_CHARS;
1212 double textLetterSpacing = -1;
1213 if ( jsonLayout.contains( QStringLiteral(
"text-letter-spacing" ) ) )
1215 const QVariant jsonTextLetterSpacing = jsonLayout.value( QStringLiteral(
"text-letter-spacing" ) );
1216 switch ( jsonTextLetterSpacing.userType() )
1218 case QMetaType::Type::Int:
1219 case QMetaType::Type::LongLong:
1220 case QMetaType::Type::Double:
1221 textLetterSpacing = jsonTextLetterSpacing.toDouble();
1224 case QMetaType::Type::QVariantMap:
1228 case QMetaType::Type::QVariantList:
1229 case QMetaType::Type::QStringList:
1234 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-letter-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextLetterSpacing.userType() ) ) ) );
1240 bool foundFont =
false;
1242 QString fontStyleName;
1244 if ( jsonLayout.contains( QStringLiteral(
"text-font" ) ) )
1246 auto splitFontFamily = [](
const QString & fontName, QString & family, QString & style ) ->
bool
1248 QString matchedFamily;
1249 const QStringList textFontParts = fontName.split(
' ' );
1250 for (
int i = textFontParts.size() - 1; i >= 1; --i )
1252 const QString candidateFontFamily = textFontParts.mid( 0, i ).join(
' ' );
1253 const QString candidateFontStyle = textFontParts.mid( i ).join(
' ' );
1258 family = processedFontFamily;
1259 style = candidateFontStyle;
1264 if ( processedFontFamily == matchedFamily )
1266 family = processedFontFamily;
1267 style = candidateFontStyle;
1271 family = matchedFamily;
1272 style = processedFontFamily;
1273 style.replace( matchedFamily, QString() );
1274 style = style.trimmed();
1275 if ( !style.isEmpty() && !candidateFontStyle.isEmpty() )
1277 style += QStringLiteral(
" %1" ).arg( candidateFontStyle );
1285 if ( QFontDatabase().hasFamily( processedFontFamily ) )
1288 family = processedFontFamily;
1294 family = matchedFamily;
1301 const QVariant jsonTextFont = jsonLayout.value( QStringLiteral(
"text-font" ) );
1302 if ( jsonTextFont.userType() != QMetaType::Type::QVariantList && jsonTextFont.userType() != QMetaType::Type::QStringList && jsonTextFont.userType() != QMetaType::Type::QString
1303 && jsonTextFont.userType() != QMetaType::Type::QVariantMap )
1305 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-font type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextFont.userType() ) ) ) );
1309 switch ( jsonTextFont.userType() )
1311 case QMetaType::Type::QVariantList:
1312 case QMetaType::Type::QStringList:
1313 fontName = jsonTextFont.toList().value( 0 ).toString();
1316 case QMetaType::Type::QString:
1317 fontName = jsonTextFont.toString();
1320 case QMetaType::Type::QVariantMap:
1322 QString familyCaseString = QStringLiteral(
"CASE " );
1323 QString styleCaseString = QStringLiteral(
"CASE " );
1325 const QVariantList stops = jsonTextFont.toMap().value( QStringLiteral(
"stops" ) ).toList();
1328 for (
int i = 0; i < stops.length() - 1; ++i )
1331 const QVariant bz = stops.value( i ).toList().value( 0 );
1332 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();
1333 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
1335 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1341 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
1342 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
1344 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1349 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1351 familyCaseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1352 "THEN %3 " ).arg( bz.toString(),
1355 styleCaseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1356 "THEN %3 " ).arg( bz.toString(),
1362 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1368 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();
1369 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1376 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1383 fontName = fontFamily;
1393 if ( splitFontFamily( fontName, fontFamily, fontStyleName ) )
1396 if ( !fontStyleName.isEmpty() )
1397 textFont.setStyleName( fontStyleName );
1407 fontName = QStringLiteral(
"Open Sans" );
1409 textFont.setStyleName( QStringLiteral(
"Regular" ) );
1410 fontStyleName = QStringLiteral(
"Regular" );
1415 fontName = QStringLiteral(
"Arial Unicode MS" );
1417 textFont.setStyleName( QStringLiteral(
"Regular" ) );
1418 fontStyleName = QStringLiteral(
"Regular" );
1423 fontName = QStringLiteral(
"Open Sans, Arial Unicode MS" );
1426 if ( !foundFont && !fontName.isEmpty() )
1428 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), fontName ) );
1433 if ( jsonPaint.contains( QStringLiteral(
"text-color" ) ) )
1435 const QVariant jsonTextColor = jsonPaint.value( QStringLiteral(
"text-color" ) );
1436 switch ( jsonTextColor.userType() )
1438 case QMetaType::Type::QVariantMap:
1442 case QMetaType::Type::QVariantList:
1443 case QMetaType::Type::QStringList:
1447 case QMetaType::Type::QString:
1448 textColor =
parseColor( jsonTextColor.toString(), context );
1452 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextColor.userType() ) ) ) );
1459 textColor = QColor( 0, 0, 0 );
1463 QColor bufferColor( 0, 0, 0, 0 );
1464 if ( jsonPaint.contains( QStringLiteral(
"text-halo-color" ) ) )
1466 const QVariant jsonBufferColor = jsonPaint.value( QStringLiteral(
"text-halo-color" ) );
1467 switch ( jsonBufferColor.userType() )
1469 case QMetaType::Type::QVariantMap:
1473 case QMetaType::Type::QVariantList:
1474 case QMetaType::Type::QStringList:
1478 case QMetaType::Type::QString:
1479 bufferColor =
parseColor( jsonBufferColor.toString(), context );
1483 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-halo-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonBufferColor.userType() ) ) ) );
1488 double bufferSize = 0.0;
1492 constexpr double BUFFER_SIZE_SCALE = 2.0;
1493 if ( jsonPaint.contains( QStringLiteral(
"text-halo-width" ) ) )
1495 const QVariant jsonHaloWidth = jsonPaint.value( QStringLiteral(
"text-halo-width" ) );
1496 QString bufferSizeDataDefined;
1497 switch ( jsonHaloWidth.userType() )
1499 case QMetaType::Type::Int:
1500 case QMetaType::Type::LongLong:
1501 case QMetaType::Type::Double:
1505 case QMetaType::Type::QVariantMap:
1510 case QMetaType::Type::QVariantList:
1511 case QMetaType::Type::QStringList:
1517 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-halo-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonHaloWidth.userType() ) ) ) );
1523 if ( bufferSize > 0 )
1525 if ( textSize > 0 && bufferSizeDataDefined.isEmpty() )
1527 bufferSize = std::min( bufferSize, textSize * BUFFER_SIZE_SCALE / 4 );
1529 else if ( textSize > 0 && !bufferSizeDataDefined.isEmpty() )
1531 bufferSizeDataDefined = QStringLiteral(
"min(%1/4, %2)" ).arg( textSize * BUFFER_SIZE_SCALE ).arg( bufferSizeDataDefined );
1534 else if ( !bufferSizeDataDefined.isEmpty() )
1536 bufferSizeDataDefined = QStringLiteral(
"min(%1*%2/4, %3)" )
1538 .arg( BUFFER_SIZE_SCALE )
1539 .arg( bufferSizeDataDefined );
1542 else if ( bufferSizeDataDefined.isEmpty() )
1544 bufferSizeDataDefined = QStringLiteral(
"min(%1*%2/4, %3)" )
1546 .arg( BUFFER_SIZE_SCALE )
1553 double haloBlurSize = 0;
1554 if ( jsonPaint.contains( QStringLiteral(
"text-halo-blur" ) ) )
1556 const QVariant jsonTextHaloBlur = jsonPaint.value( QStringLiteral(
"text-halo-blur" ) );
1557 switch ( jsonTextHaloBlur.userType() )
1559 case QMetaType::Type::Int:
1560 case QMetaType::Type::LongLong:
1561 case QMetaType::Type::Double:
1568 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-halo-blur type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextHaloBlur.userType() ) ) ) );
1575 if ( textColor.isValid() )
1577 if ( textSize >= 0 )
1582 if ( !fontStyleName.isEmpty() )
1585 if ( textLetterSpacing > 0 )
1587 QFont f = format.
font();
1588 f.setLetterSpacing( QFont::AbsoluteSpacing, textLetterSpacing );
1592 if ( bufferSize > 0 )
1595 const double opacity = bufferColor.alphaF();
1596 bufferColor.setAlphaF( 1.0 );
1604 if ( haloBlurSize > 0 )
1620 if ( textMaxWidth > 0 )
1626 if ( jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
1628 const QVariant jsonTextField = jsonLayout.value( QStringLiteral(
"text-field" ) );
1629 switch ( jsonTextField.userType() )
1631 case QMetaType::Type::QString:
1633 labelSettings.
fieldName = processLabelField( jsonTextField.toString(), labelSettings.
isExpression );
1637 case QMetaType::Type::QVariantList:
1638 case QMetaType::Type::QStringList:
1640 const QVariantList textFieldList = jsonTextField.toList();
1648 if ( textFieldList.size() > 2 && textFieldList.at( 0 ).toString() == QLatin1String(
"format" ) )
1651 for (
int i = 1; i < textFieldList.size(); ++i )
1653 bool isExpression =
false;
1654 const QString part = processLabelField( textFieldList.at( i ).toString(), isExpression );
1655 if ( !isExpression )
1662 labelSettings.
fieldName = QStringLiteral(
"concat(%1)" ).arg( parts.join(
',' ) );
1677 case QMetaType::Type::QVariantMap:
1679 const QVariantList stops = jsonTextField.toMap().value( QStringLiteral(
"stops" ) ).toList();
1680 if ( !stops.empty() )
1687 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-field dictionary" ).arg( context.
layerId() ) );
1693 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-field type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextField.userType() ) ) ) );
1698 if ( jsonLayout.contains( QStringLiteral(
"text-rotate" ) ) )
1700 const QVariant jsonTextRotate = jsonLayout.value( QStringLiteral(
"text-rotate" ) );
1701 switch ( jsonTextRotate.userType() )
1703 case QMetaType::Type::Double:
1704 case QMetaType::Type::Int:
1706 labelSettings.
angleOffset = jsonTextRotate.toDouble();
1710 case QMetaType::Type::QVariantList:
1711 case QMetaType::Type::QStringList:
1718 case QMetaType::Type::QVariantMap:
1720 QVariantMap rotateMap = jsonTextRotate.toMap();
1721 if ( rotateMap.contains( QStringLiteral(
"property" ) ) && rotateMap[QStringLiteral(
"type" )].toString() == QLatin1String(
"identity" ) )
1727 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-rotate map content (%2)" ).arg( context.
layerId(), QString( QJsonDocument::fromVariant( rotateMap ).toJson() ) ) );
1732 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextRotate.userType() ) ) ) );
1737 if ( jsonLayout.contains( QStringLiteral(
"text-transform" ) ) )
1739 const QString textTransform = jsonLayout.value( QStringLiteral(
"text-transform" ) ).toString();
1740 if ( textTransform == QLatin1String(
"uppercase" ) )
1744 else if ( textTransform == QLatin1String(
"lowercase" ) )
1753 if ( jsonLayout.contains( QStringLiteral(
"symbol-placement" ) ) )
1755 const QString symbolPlacement = jsonLayout.value( QStringLiteral(
"symbol-placement" ) ).toString();
1756 if ( symbolPlacement == QLatin1String(
"line" ) )
1762 if ( jsonLayout.contains( QStringLiteral(
"text-rotation-alignment" ) ) )
1764 const QString textRotationAlignment = jsonLayout.value( QStringLiteral(
"text-rotation-alignment" ) ).toString();
1765 if ( textRotationAlignment == QLatin1String(
"viewport" ) )
1775 if ( jsonLayout.contains( QStringLiteral(
"text-offset" ) ) )
1777 const QVariant jsonTextOffset = jsonLayout.value( QStringLiteral(
"text-offset" ) );
1780 switch ( jsonTextOffset.userType() )
1782 case QMetaType::Type::QVariantMap:
1783 textOffsetProperty =
parseInterpolatePointByZoom( jsonTextOffset.toMap(), context, !textSizeProperty ? textSize : 1.0, &textOffset );
1784 if ( !textSizeProperty )
1795 case QMetaType::Type::QVariantList:
1796 case QMetaType::Type::QStringList:
1797 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1798 jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1802 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextOffset.userType() ) ) ) );
1806 if ( !textOffset.isNull() )
1809 labelSettings.
dist = std::abs( textOffset.y() ) - textSize;
1811 if ( textSizeProperty && !textOffsetProperty )
1818 if ( textOffset.isNull() )
1826 if ( jsonLayout.contains( QStringLiteral(
"text-justify" ) ) )
1828 const QVariant jsonTextJustify = jsonLayout.value( QStringLiteral(
"text-justify" ) );
1831 QString textAlign = QStringLiteral(
"center" );
1833 const QVariantMap conversionMap
1835 { QStringLiteral(
"left" ), QStringLiteral(
"left" ) },
1836 { QStringLiteral(
"center" ), QStringLiteral(
"center" ) },
1837 { QStringLiteral(
"right" ), QStringLiteral(
"right" ) },
1838 { QStringLiteral(
"auto" ), QStringLiteral(
"follow" ) }
1841 switch ( jsonTextJustify.userType() )
1843 case QMetaType::Type::QString:
1844 textAlign = jsonTextJustify.toString();
1847 case QMetaType::Type::QVariantList:
1851 case QMetaType::Type::QVariantMap:
1856 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-justify type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextJustify.userType() ) ) ) );
1860 if ( textAlign == QLatin1String(
"left" ) )
1862 else if ( textAlign == QLatin1String(
"right" ) )
1864 else if ( textAlign == QLatin1String(
"center" ) )
1866 else if ( textAlign == QLatin1String(
"follow" ) )
1876 if ( jsonLayout.contains( QStringLiteral(
"text-anchor" ) ) )
1878 const QVariant jsonTextAnchor = jsonLayout.value( QStringLiteral(
"text-anchor" ) );
1881 const QVariantMap conversionMap
1883 { QStringLiteral(
"center" ), 4 },
1884 { QStringLiteral(
"left" ), 5 },
1885 { QStringLiteral(
"right" ), 3 },
1886 { QStringLiteral(
"top" ), 7 },
1887 { QStringLiteral(
"bottom" ), 1 },
1888 { QStringLiteral(
"top-left" ), 8 },
1889 { QStringLiteral(
"top-right" ), 6 },
1890 { QStringLiteral(
"bottom-left" ), 2 },
1891 { QStringLiteral(
"bottom-right" ), 0 },
1894 switch ( jsonTextAnchor.userType() )
1896 case QMetaType::Type::QString:
1897 textAnchor = jsonTextAnchor.toString();
1900 case QMetaType::Type::QVariantList:
1904 case QMetaType::Type::QVariantMap:
1909 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-anchor type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextAnchor.userType() ) ) ) );
1913 if ( textAnchor == QLatin1String(
"center" ) )
1915 else if ( textAnchor == QLatin1String(
"left" ) )
1917 else if ( textAnchor == QLatin1String(
"right" ) )
1919 else if ( textAnchor == QLatin1String(
"top" ) )
1921 else if ( textAnchor == QLatin1String(
"bottom" ) )
1923 else if ( textAnchor == QLatin1String(
"top-left" ) )
1925 else if ( textAnchor == QLatin1String(
"top-right" ) )
1927 else if ( textAnchor == QLatin1String(
"bottom-left" ) )
1929 else if ( textAnchor == QLatin1String(
"bottom-right" ) )
1934 if ( jsonLayout.contains( QStringLiteral(
"text-offset" ) ) )
1936 const QVariant jsonTextOffset = jsonLayout.value( QStringLiteral(
"text-offset" ) );
1939 switch ( jsonTextOffset.userType() )
1941 case QMetaType::Type::QVariantMap:
1945 case QMetaType::Type::QVariantList:
1946 case QMetaType::Type::QStringList:
1947 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1948 jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1952 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextOffset.userType() ) ) ) );
1956 if ( !textOffset.isNull() )
1959 labelSettings.
xOffset = textOffset.x();
1960 labelSettings.
yOffset = textOffset.y();
1965 if ( jsonLayout.contains( QStringLiteral(
"icon-image" ) ) &&
1969 QString spriteProperty, spriteSizeProperty;
1971 if ( !sprite.isEmpty() )
1974 if ( jsonLayout.contains( QStringLiteral(
"icon-size" ) ) )
1977 const QVariant jsonIconSize = jsonLayout.
value( QStringLiteral(
"icon-size" ) );
1978 switch ( jsonIconSize.userType() )
1980 case QMetaType::Type::Int:
1981 case QMetaType::Type::LongLong:
1982 case QMetaType::Type::Double:
1984 size = jsonIconSize.toDouble();
1985 if ( !spriteSizeProperty.isEmpty() )
1988 QgsProperty::fromExpression( QStringLiteral(
"with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
1993 case QMetaType::Type::QVariantMap:
1997 case QMetaType::Type::QVariantList:
1998 case QMetaType::Type::QStringList:
2002 context.
pushWarning( QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) ) );
2008 if ( !spriteSizeProperty.isEmpty() )
2022 markerLayer->
setPath( sprite );
2023 markerLayer->
setSize( spriteSize.width() );
2026 if ( !spriteProperty.isEmpty() )
2036 backgroundSettings.
setSize( spriteSize * size );
2044 if ( textSize >= 0 )
2067 if ( !jsonLayer.contains( QStringLiteral(
"layout" ) ) )
2069 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
2072 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
2074 if ( jsonLayout.value( QStringLiteral(
"symbol-placement" ) ).toString() == QLatin1String(
"line" ) && !jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
2078 double spacing = -1.0;
2079 if ( jsonLayout.contains( QStringLiteral(
"symbol-spacing" ) ) )
2081 const QVariant jsonSpacing = jsonLayout.value( QStringLiteral(
"symbol-spacing" ) );
2082 switch ( jsonSpacing.userType() )
2084 case QMetaType::Type::Int:
2085 case QMetaType::Type::LongLong:
2086 case QMetaType::Type::Double:
2090 case QMetaType::Type::QVariantMap:
2094 case QMetaType::Type::QVariantList:
2095 case QMetaType::Type::QStringList:
2100 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported symbol-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonSpacing.userType() ) ) ) );
2110 bool rotateMarkers =
true;
2111 if ( jsonLayout.contains( QStringLiteral(
"icon-rotation-alignment" ) ) )
2113 const QString alignment = jsonLayout.value( QStringLiteral(
"icon-rotation-alignment" ) ).toString();
2114 if ( alignment == QLatin1String(
"map" ) || alignment == QLatin1String(
"auto" ) )
2116 rotateMarkers =
true;
2118 else if ( alignment == QLatin1String(
"viewport" ) )
2120 rotateMarkers =
false;
2125 double rotation = 0.0;
2126 if ( jsonLayout.contains( QStringLiteral(
"icon-rotate" ) ) )
2128 const QVariant jsonIconRotate = jsonLayout.value( QStringLiteral(
"icon-rotate" ) );
2129 switch ( jsonIconRotate.userType() )
2131 case QMetaType::Type::Int:
2132 case QMetaType::Type::LongLong:
2133 case QMetaType::Type::Double:
2134 rotation = jsonIconRotate.toDouble();
2137 case QMetaType::Type::QVariantMap:
2141 case QMetaType::Type::QVariantList:
2142 case QMetaType::Type::QStringList:
2147 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconRotate.userType() ) ) ) );
2163 QString spriteProperty, spriteSizeProperty;
2165 if ( !sprite.isNull() )
2167 markerLayer->
setPath( sprite );
2168 markerLayer->
setSize( spriteSize.width() );
2171 if ( !spriteProperty.isEmpty() )
2178 if ( jsonLayout.contains( QStringLiteral(
"icon-size" ) ) )
2180 const QVariant jsonIconSize = jsonLayout.value( QStringLiteral(
"icon-size" ) );
2183 switch ( jsonIconSize.userType() )
2185 case QMetaType::Type::Int:
2186 case QMetaType::Type::LongLong:
2187 case QMetaType::Type::Double:
2189 size = jsonIconSize.toDouble();
2190 if ( !spriteSizeProperty.isEmpty() )
2193 QgsProperty::fromExpression( QStringLiteral(
"with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
2198 case QMetaType::Type::QVariantMap:
2202 case QMetaType::Type::QVariantList:
2203 case QMetaType::Type::QStringList:
2207 context.
pushWarning( QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) ) );
2210 markerLayer->
setSize( size * spriteSize.width() );
2213 if ( !spriteSizeProperty.isEmpty() )
2230 std::unique_ptr< QgsSymbol > symbol = std::make_unique< QgsLineSymbol >(
QgsSymbolLayerList() << lineSymbol );
2233 symbol->setOutputUnit( context.
targetUnit() );
2237 rendererStyle.
setSymbol( symbol.release() );
2240 else if ( jsonLayout.contains( QStringLiteral(
"icon-image" ) ) )
2242 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
2245 QString spriteProperty, spriteSizeProperty;
2247 if ( !sprite.isEmpty() || !spriteProperty.isEmpty() )
2250 rasterMarker->
setPath( sprite );
2251 rasterMarker->
setSize( spriteSize.width() );
2255 if ( !spriteProperty.isEmpty() )
2261 if ( jsonLayout.contains( QStringLiteral(
"icon-size" ) ) )
2263 const QVariant jsonIconSize = jsonLayout.value( QStringLiteral(
"icon-size" ) );
2266 switch ( jsonIconSize.userType() )
2268 case QMetaType::Type::Int:
2269 case QMetaType::Type::LongLong:
2270 case QMetaType::Type::Double:
2272 size = jsonIconSize.toDouble();
2273 if ( !spriteSizeProperty.isEmpty() )
2276 QgsProperty::fromExpression( QStringLiteral(
"with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
2281 case QMetaType::Type::QVariantMap:
2285 case QMetaType::Type::QVariantList:
2286 case QMetaType::Type::QStringList:
2290 context.
pushWarning( QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) ) );
2293 rasterMarker->
setSize( size * spriteSize.width() );
2296 if ( !spriteSizeProperty.isEmpty() )
2309 double rotation = 0.0;
2310 if ( jsonLayout.contains( QStringLiteral(
"icon-rotate" ) ) )
2312 const QVariant jsonIconRotate = jsonLayout.value( QStringLiteral(
"icon-rotate" ) );
2313 switch ( jsonIconRotate.userType() )
2315 case QMetaType::Type::Int:
2316 case QMetaType::Type::LongLong:
2317 case QMetaType::Type::Double:
2318 rotation = jsonIconRotate.toDouble();
2321 case QMetaType::Type::QVariantMap:
2325 case QMetaType::Type::QVariantList:
2326 case QMetaType::Type::QStringList:
2331 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconRotate.userType() ) ) ) );
2336 double iconOpacity = -1.0;
2337 if ( jsonPaint.contains( QStringLiteral(
"icon-opacity" ) ) )
2339 const QVariant jsonIconOpacity = jsonPaint.value( QStringLiteral(
"icon-opacity" ) );
2340 switch ( jsonIconOpacity.userType() )
2342 case QMetaType::Type::Int:
2343 case QMetaType::Type::LongLong:
2344 case QMetaType::Type::Double:
2345 iconOpacity = jsonIconOpacity.toDouble();
2348 case QMetaType::Type::QVariantMap:
2352 case QMetaType::Type::QVariantList:
2353 case QMetaType::Type::QStringList:
2358 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported icon-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconOpacity.userType() ) ) ) );
2364 rasterMarker->
setAngle( rotation );
2365 if ( iconOpacity >= 0 )
2369 rendererStyle.
setSymbol( markerSymbol );
2380 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2381 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2382 if ( stops.empty() )
2385 QString caseString = QStringLiteral(
"CASE " );
2386 const QString colorComponent(
"color_part(%1,'%2')" );
2388 for (
int i = 0; i < stops.length() - 1; ++i )
2391 const QString bz = stops.at( i ).toList().value( 0 ).toString();
2393 const QString tz = stops.at( i + 1 ).toList().value( 0 ).toString();
2395 const QVariant bcVariant = stops.at( i ).toList().value( 1 );
2396 const QVariant tcVariant = stops.at( i + 1 ).toList().value( 1 );
2398 const QColor bottomColor =
parseColor( bcVariant.toString(), context );
2399 const QColor topColor =
parseColor( tcVariant.toString(), context );
2401 if ( i == 0 && bottomColor.isValid() )
2408 caseString += QStringLiteral(
"WHEN @vector_tile_zoom < %1 THEN color_hsla(%2, %3, %4, %5) " )
2409 .arg( bz ).arg( bcHue ).arg( bcSat ).arg( bcLight ).arg( bcAlpha );
2412 if ( bottomColor.isValid() && topColor.isValid() )
2424 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2425 "%3, %4, %5, %6) " ).arg( bz, tz,
2436 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2437 "%3, %4, %5, %6) " ).arg( bz, tz,
2438 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"hsl_hue" ), colorComponent.arg( topColorExpr ).arg(
"hsl_hue" ), base, 1, &context ),
2439 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"hsl_saturation" ), colorComponent.arg( topColorExpr ).arg(
"hsl_saturation" ), base, 1, &context ),
2440 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"lightness" ), colorComponent.arg( topColorExpr ).arg(
"lightness" ), base, 1, &context ),
2441 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"alpha" ), colorComponent.arg( topColorExpr ).arg(
"alpha" ), base, 1, &context ) );
2446 const QString tz = stops.last().toList().value( 0 ).toString();
2447 const QVariant tcVariant = stops.last().toList().value( 1 );
2449 if ( tcVariant.userType() == QMetaType::Type::QString )
2452 if ( topColor.isValid() )
2459 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2460 "ELSE color_hsla(%2, %3, %4, %5) END" ).arg( tz ).arg( tcHue ).arg( tcSat ).arg( tcLight ).arg( tcAlpha );
2463 else if ( tcVariant.userType() == QMetaType::QVariantList )
2467 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2468 "ELSE color_hsla(%2, %3, %4, %5) END" ).arg( tz )
2469 .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" ) );
2472 if ( !stops.empty() && defaultColor )
2473 *defaultColor =
parseColor( stops.value( 0 ).toList().value( 1 ).toString(), context );
2480 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2481 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2482 if ( stops.empty() )
2485 QString scaleExpression;
2486 if ( stops.size() <= 2 )
2489 stops.value( 0 ).toList().value( 0 ).toDouble(),
2490 stops.last().toList().value( 0 ).toDouble(),
2491 stops.value( 0 ).toList().value( 1 ),
2492 stops.last().toList().value( 1 ),
2493 base, multiplier, &context );
2497 scaleExpression =
parseStops( base, stops, multiplier, context );
2500 if ( !stops.empty() && defaultNumber )
2501 *defaultNumber = stops.value( 0 ).toList().value( 1 ).toDouble() * multiplier;
2511 context = *contextPtr;
2513 const double base = json.value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2514 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2515 if ( stops.empty() )
2518 QString scaleExpression;
2519 if ( stops.length() <= 2 )
2521 const QVariant bv = stops.value( 0 ).toList().value( 1 );
2522 const QVariant tv = stops.last().toList().value( 1 );
2523 double bottom = 0.0;
2525 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2526 scaleExpression = QStringLiteral(
"set_color_part(@symbol_color, 'alpha', %1)" )
2528 stops.value( 0 ).toList().value( 0 ).toDouble(),
2529 stops.last().toList().value( 0 ).toDouble(),
2530 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2531 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ), base, 1, &context ) );
2542 QString caseString = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN set_color_part(@symbol_color, 'alpha', %2)" )
2543 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
2544 .arg( stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity );
2546 for (
int i = 0; i < stops.size() - 1; ++i )
2548 const QVariant bv = stops.value( i ).toList().value( 1 );
2549 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2550 double bottom = 0.0;
2552 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2554 caseString += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
2555 "THEN set_color_part(@symbol_color, 'alpha', %3)" )
2556 .arg( stops.value( i ).toList().value( 0 ).toString(),
2557 stops.value( i + 1 ).toList().value( 0 ).toString(),
2559 stops.value( i ).toList().value( 0 ).toDouble(),
2560 stops.value( i + 1 ).toList().value( 0 ).toDouble(),
2561 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2562 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ),
2563 base, 1, &context ) );
2567 bool numeric =
false;
2568 const QVariant vv = stops.last().toList().value( 1 );
2569 double dv = vv.toDouble( &numeric );
2571 caseString += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
2572 "THEN set_color_part(@symbol_color, 'alpha', %2) END" ).arg(
2573 stops.last().toList().value( 0 ).toString(),
2574 numeric ? QString::number( dv * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( vv, context ) ).arg( maxOpacity )
2581 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2582 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2583 if ( stops.empty() )
2586 QString scaleExpression;
2587 if ( stops.size() <= 2 )
2589 scaleExpression = QStringLiteral(
"array(%1,%2)" ).arg(
interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(),
2590 stops.last().toList().value( 0 ).toDouble(),
2591 stops.value( 0 ).toList().value( 1 ).toList().value( 0 ),
2592 stops.last().toList().value( 1 ).toList().value( 0 ), base, multiplier, &context ),
2594 stops.last().toList().value( 0 ).toDouble(),
2595 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ),
2596 stops.last().toList().value( 1 ).toList().value( 1 ), base, multiplier, &context )
2601 scaleExpression =
parsePointStops( base, stops, context, multiplier );
2604 if ( !stops.empty() && defaultPoint )
2605 *defaultPoint = QPointF( stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble() * multiplier,
2606 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble() * multiplier );
2612 const QVariantMap &conversionMap, QString *defaultString )
2614 const QVariantList stops = json.
value( QStringLiteral(
"stops" ) ).toList();
2615 if ( stops.empty() )
2618 const QString scaleExpression =
parseStringStops( stops, context, conversionMap, defaultString );
2625 QString caseString = QStringLiteral(
"CASE " );
2627 for (
int i = 0; i < stops.length() - 1; ++i )
2630 const QVariant bz = stops.value( i ).toList().value( 0 );
2631 const QVariant bv = stops.value( i ).toList().value( 1 );
2632 if ( bv.userType() != QMetaType::Type::QVariantList && bv.userType() != QMetaType::Type::QStringList )
2634 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( bz.userType() ) ) ) );
2639 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2640 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2641 if ( tv.userType() != QMetaType::Type::QVariantList && tv.userType() != QMetaType::Type::QStringList )
2643 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( tz.userType() ) ) ) );
2647 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2648 "THEN array(%3,%4)" ).arg( bz.toString(),
2650 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 0 ), tv.toList().value( 0 ), base, multiplier, &context ),
2651 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 1 ), tv.toList().value( 1 ), base, multiplier, &context ) );
2653 caseString += QLatin1String(
"END" );
2659 if ( stops.length() < 2 )
2662 QString caseString = QStringLiteral(
"CASE" );
2664 for (
int i = 0; i < stops.length(); ++i )
2666 caseString += QLatin1String(
" WHEN " );
2667 QStringList conditions;
2670 const QVariant bottomZoom = stops.value( i ).toList().value( 0 );
2671 conditions << QStringLiteral(
"@vector_tile_zoom > %1" ).arg( bottomZoom.toString() );
2673 if ( i < stops.length() - 1 )
2675 const QVariant topZoom = stops.value( i + 1 ).toList().value( 0 );
2676 conditions << QStringLiteral(
"@vector_tile_zoom <= %1" ).arg( topZoom.toString() );
2679 const QVariantList values = stops.value( i ).toList().value( 1 ).toList();
2680 QStringList valuesFixed;
2682 for (
const QVariant &value : values )
2684 const double number = value.toDouble( &ok );
2686 valuesFixed << QString::number( number * multiplier );
2690 caseString += QStringLiteral(
"%1 THEN array(%3)" ).arg(
2691 conditions.join( QLatin1String(
" AND " ) ),
2692 valuesFixed.join(
',' )
2695 caseString += QLatin1String(
" END" );
2701 QString caseString = QStringLiteral(
"CASE " );
2703 for (
int i = 0; i < stops.length() - 1; ++i )
2706 const QVariant bz = stops.value( i ).toList().value( 0 );
2707 const QVariant bv = stops.value( i ).toList().value( 1 );
2708 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2710 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2715 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2716 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2717 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2719 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2723 const QString lowerComparator = i == 0 ? QStringLiteral(
">=" ) : QStringLiteral(
">" );
2725 caseString += QStringLiteral(
"WHEN @vector_tile_zoom %1 %2 AND @vector_tile_zoom <= %3 "
2726 "THEN %4 " ).arg( lowerComparator,
2732 const QVariant z = stops.last().toList().value( 0 );
2733 const QVariant v = stops.last().toList().value( 1 );
2734 QString vStr = v.toString();
2735 if ( ( QMetaType::Type )v.userType() == QMetaType::QVariantList )
2738 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 "
2739 "THEN ( ( %2 ) * %3 ) END" ).arg( z.toString() ).arg( vStr ).arg( multiplier );
2743 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 "
2744 "THEN %2 END" ).arg( z.toString() ).arg( v.toDouble() * multiplier );
2752 QString caseString = QStringLiteral(
"CASE " );
2754 for (
int i = 0; i < stops.length() - 1; ++i )
2757 const QVariant bz = stops.value( i ).toList().value( 0 );
2758 const QString bv = stops.value( i ).toList().value( 1 ).toString();
2759 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2761 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2766 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2767 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2769 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2773 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2774 "THEN %3 " ).arg( bz.toString(),
2778 caseString += QStringLiteral(
"ELSE %1 END" ).arg(
QgsExpression::quotedValue( conversionMap.value( stops.constLast().toList().value( 1 ).toString(),
2779 stops.constLast().toList().value( 1 ) ) ) );
2780 if ( defaultString )
2781 *defaultString = stops.constLast().toList().value( 1 ).toString();
2787 QString caseString = QStringLiteral(
"CASE " );
2789 bool isExpression =
false;
2790 for (
int i = 0; i < stops.length() - 1; ++i )
2793 const QVariant bz = stops.value( i ).toList().value( 0 );
2794 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2796 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2801 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2802 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2804 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2808 QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
2809 if ( fieldPart.isEmpty() )
2810 fieldPart = QStringLiteral(
"''" );
2811 else if ( !isExpression )
2814 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom < %2 "
2815 "THEN %3 " ).arg( bz.toString(),
2821 const QVariant bz = stops.constLast().toList().value( 0 );
2822 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2824 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2828 QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
2829 if ( fieldPart.isEmpty() )
2830 fieldPart = QStringLiteral(
"''" );
2831 else if ( !isExpression )
2834 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 "
2835 "THEN %3 " ).arg( bz.toString(),
2839 QString defaultPart = processLabelField( stops.constFirst().toList().value( 1 ).toString(), isExpression );
2840 if ( defaultPart.isEmpty() )
2841 defaultPart = QStringLiteral(
"''" );
2842 else if ( !isExpression )
2844 caseString += QStringLiteral(
"ELSE %1 END" ).arg( defaultPart );
2851 const QString method = json.
value( 0 ).toString();
2852 if ( method == QLatin1String(
"interpolate" ) )
2856 else if ( method == QLatin1String(
"match" ) )
2858 return parseMatchList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
2860 else if ( method == QLatin1String(
"step" ) )
2862 return parseStepList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
2872 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
2873 if ( attribute.isEmpty() )
2875 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
2879 QString caseString = QStringLiteral(
"CASE " );
2881 for (
int i = 2; i < json.length() - 1; i += 2 )
2884 QVariant variantKeys = json.value( i );
2885 if ( variantKeys.userType() == QMetaType::Type::QVariantList || variantKeys.userType() == QMetaType::Type::QStringList )
2886 keys = variantKeys.toList();
2888 keys = {variantKeys};
2890 QStringList matchString;
2891 for (
const QVariant &key : keys )
2896 const QVariant value = json.value( i + 1 );
2898 QString valueString;
2903 if ( value.userType() == QMetaType::Type::QVariantList || value.userType() == QMetaType::Type::QStringList )
2909 const QColor color =
parseColor( value, context );
2917 const double v = value.toDouble() * multiplier;
2918 valueString = QString::number( v );
2924 const double v = value.toDouble() * maxOpacity;
2925 valueString = QString::number( v );
2931 valueString = QStringLiteral(
"array(%1,%2)" ).arg( value.toList().value( 0 ).toDouble() * multiplier,
2932 value.toList().value( 0 ).toDouble() * multiplier );
2938 if ( value.toList().count() == 2 && value.toList().first().toString() == QLatin1String(
"literal" ) )
2940 valueString = QStringLiteral(
"array(%1)" ).arg( value.toList().at( 1 ).toStringList().join(
',' ) );
2944 valueString = QStringLiteral(
"array(%1)" ).arg( value.toStringList().join(
',' ) );
2950 if ( matchString.count() == 1 )
2952 caseString += QStringLiteral(
"WHEN %1 IS %2 THEN %3 " ).arg( attribute, matchString.at( 0 ), valueString );
2956 caseString += QStringLiteral(
"WHEN %1 IN (%2) THEN %3 " ).arg( attribute, matchString.join(
',' ), valueString );
2960 QVariant lastValue = json.constLast();
2963 switch ( lastValue.userType() )
2965 case QMetaType::Type::QVariantList:
2966 case QMetaType::Type::QStringList:
2967 elseValue =
parseValueList( lastValue.toList(), type, context, multiplier, maxOpacity, defaultColor, defaultNumber ).
asExpression();
2976 const QColor color =
parseColor( lastValue, context );
2978 *defaultColor = color;
2986 const double v = json.constLast().toDouble() * multiplier;
2987 if ( defaultNumber )
2989 elseValue = QString::number( v );
2995 const double v = json.constLast().toDouble() * maxOpacity;
2996 if ( defaultNumber )
2998 elseValue = QString::number( v );
3004 elseValue = QStringLiteral(
"array(%1,%2)" )
3005 .arg( json.constLast().toList().value( 0 ).toDouble() * multiplier )
3006 .arg( json.constLast().toList().value( 0 ).toDouble() * multiplier );
3012 if ( json.constLast().toList().count() == 2 && json.constLast().toList().first().toString() == QLatin1String(
"literal" ) )
3014 elseValue = QStringLiteral(
"array(%1)" ).arg( json.constLast().toList().at( 1 ).toStringList().join(
',' ) );
3018 elseValue = QStringLiteral(
"array(%1)" ).arg( json.constLast().toStringList().join(
',' ) );
3028 caseString += QStringLiteral(
"ELSE %1 END" ).arg( elseValue );
3034 const QString expression =
parseExpression( json.value( 1 ).toList(), context );
3035 if ( expression.isEmpty() )
3037 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3041 QString caseString = QStringLiteral(
"CASE " );
3044 for (
int i = json.length() - 2; i > 0; i -= 2 )
3046 const QVariant stepValue = json.value( i + 1 );
3048 QString valueString;
3049 if ( stepValue.canConvert<QVariantList>()
3061 const QColor color =
parseColor( stepValue, context );
3068 const double v = stepValue.toDouble() * multiplier;
3069 valueString = QString::number( v );
3075 const double v = stepValue.toDouble() * maxOpacity;
3076 valueString = QString::number( v );
3082 valueString = QStringLiteral(
"array(%1,%2)" ).arg(
3083 stepValue.toList().value( 0 ).toDouble() * multiplier ).arg(
3084 stepValue.toList().value( 0 ).toDouble() * multiplier
3091 if ( stepValue.toList().count() == 2 && stepValue.toList().first().toString() == QLatin1String(
"literal" ) )
3093 valueString = QStringLiteral(
"array(%1)" ).arg( stepValue.toList().at( 1 ).toStringList().join(
',' ) );
3097 valueString = QStringLiteral(
"array(%1)" ).arg( stepValue.toStringList().join(
',' ) );
3107 caseString += QStringLiteral(
" WHEN %1 >= %2 THEN (%3) " ).arg( expression, stepKey, valueString );
3111 caseString += QStringLiteral(
"ELSE (%1) END" ).arg( valueString );
3119 if ( json.value( 0 ).toString() != QLatin1String(
"interpolate" ) )
3121 context.
pushWarning( QObject::tr(
"%1: Could not interpret value list" ).arg( context.
layerId() ) );
3126 const QString technique = json.
value( 1 ).toList().value( 0 ).toString();
3127 if ( technique == QLatin1String(
"linear" ) )
3129 else if ( technique == QLatin1String(
"exponential" ) )
3130 base = json.value( 1 ).toList(). value( 1 ).toDouble();
3131 else if ( technique == QLatin1String(
"cubic-bezier" ) )
3133 context.
pushWarning( QObject::tr(
"%1: Cubic-bezier interpolation is not supported, linear used instead." ).arg( context.
layerId() ) );
3138 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation method %2" ).arg( context.
layerId(), technique ) );
3142 if ( json.value( 2 ).toList().value( 0 ).toString() != QLatin1String(
"zoom" ) )
3144 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation input %2" ).arg( context.
layerId(), json.value( 2 ).toString() ) );
3150 for (
int i = 3; i < json.length(); i += 2 )
3152 stops.push_back( QVariantList() << json.value( i ).toString() << json.value( i + 1 ) );
3156 props.insert( QStringLiteral(
"stops" ), stops );
3157 props.insert( QStringLiteral(
"base" ), base );
3173 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported numeric array in interpolate" ).arg( context.
layerId() ) );
3182 if ( ( QMetaType::Type )colorExpression.userType() == QMetaType::QVariantList )
3186 return parseValue( colorExpression, context,
true );
3191 if ( color.userType() != QMetaType::Type::QString )
3193 context.
pushWarning( QObject::tr(
"%1: Could not parse non-string color %2, skipping" ).arg( context.
layerId(), color.toString() ) );
3202 hue = std::max( 0, color.hslHue() );
3203 saturation = color.hslSaturation() / 255.0 * 100;
3204 lightness = color.lightness() / 255.0 * 100;
3205 alpha = color.alpha();
3213 context = *contextPtr;
3217 if ( valueMin.canConvert( QMetaType::Double ) && valueMax.canConvert( QMetaType::Double ) )
3219 bool minDoubleOk =
true;
3220 const double min = valueMin.toDouble( &minDoubleOk );
3221 bool maxDoubleOk =
true;
3222 const double max = valueMax.toDouble( &maxDoubleOk );
3223 if ( minDoubleOk && maxDoubleOk &&
qgsDoubleNear( min, max ) )
3225 return QString::number( min * multiplier );
3229 QString minValueExpr = valueMin.toString();
3230 QString maxValueExpr = valueMax.toString();
3231 if ( valueMin.userType() == QMetaType::Type::QVariantList )
3235 if ( valueMax.userType() == QMetaType::Type::QVariantList )
3241 if ( minValueExpr == maxValueExpr )
3243 expression = minValueExpr;
3249 expression = QStringLiteral(
"scale_linear(@vector_tile_zoom,%1,%2,%3,%4)" ).arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr );
3253 expression = QStringLiteral(
"scale_exponential(@vector_tile_zoom,%1,%2,%3,%4,%5)" ).arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr ).arg( base );
3257 if ( multiplier != 1 )
3258 return QStringLiteral(
"(%1) * %2" ).arg( expression ).arg( multiplier );
3265 if ( style == QLatin1String(
"round" ) )
3266 return Qt::RoundCap;
3267 else if ( style == QLatin1String(
"square" ) )
3268 return Qt::SquareCap;
3275 if ( style == QLatin1String(
"bevel" ) )
3276 return Qt::BevelJoin;
3277 else if ( style == QLatin1String(
"round" ) )
3278 return Qt::RoundJoin;
3280 return Qt::MiterJoin;
3285 QString op = expression.value( 0 ).toString();
3286 if ( op == QLatin1String(
"%" ) && expression.size() >= 3 )
3288 return QStringLiteral(
"%1 %2 %3" ).arg( parseValue( expression.value( 1 ), context ),
3290 parseValue( expression.value( 2 ), context ) );
3292 else if ( op == QLatin1String(
"to-number" ) )
3294 return QStringLiteral(
"to_real(%1)" ).arg( parseValue( expression.value( 1 ), context ) );
3296 if ( op == QLatin1String(
"literal" ) )
3298 return expression.value( 1 ).toString();
3300 else if ( op == QLatin1String(
"all" )
3301 || op == QLatin1String(
"any" )
3302 || op == QLatin1String(
"none" ) )
3305 for (
int i = 1; i < expression.size(); ++i )
3307 const QString part = parseValue( expression.at( i ), context );
3308 if ( part.isEmpty() )
3310 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3316 if ( op == QLatin1String(
"none" ) )
3317 return QStringLiteral(
"NOT (%1)" ).arg( parts.join( QLatin1String(
") AND NOT (" ) ) );
3319 QString operatorString;
3320 if ( op == QLatin1String(
"all" ) )
3321 operatorString = QStringLiteral(
") AND (" );
3322 else if ( op == QLatin1String(
"any" ) )
3323 operatorString = QStringLiteral(
") OR (" );
3325 return QStringLiteral(
"(%1)" ).arg( parts.join( operatorString ) );
3327 else if ( op ==
'!' )
3330 QVariantList contraJsonExpr = expression.value( 1 ).toList();
3331 contraJsonExpr[0] = QString( op + contraJsonExpr[0].toString() );
3333 return parseKey( contraJsonExpr, context );
3335 else if ( op == QLatin1String(
"==" )
3336 || op == QLatin1String(
"!=" )
3337 || op == QLatin1String(
">=" )
3339 || op == QLatin1String(
"<=" )
3343 if ( op == QLatin1String(
"==" ) )
3344 op = QStringLiteral(
"IS" );
3345 else if ( op == QLatin1String(
"!=" ) )
3346 op = QStringLiteral(
"IS NOT" );
3347 return QStringLiteral(
"%1 %2 %3" ).arg( parseKey( expression.value( 1 ), context ),
3348 op, parseValue( expression.value( 2 ), context ) );
3350 else if ( op == QLatin1String(
"has" ) )
3352 return parseKey( expression.value( 1 ), context ) + QStringLiteral(
" IS NOT NULL" );
3354 else if ( op == QLatin1String(
"!has" ) )
3356 return parseKey( expression.value( 1 ), context ) + QStringLiteral(
" IS NULL" );
3358 else if ( op == QLatin1String(
"in" ) || op == QLatin1String(
"!in" ) )
3360 const QString key = parseKey( expression.value( 1 ), context );
3363 QVariantList values = expression.mid( 2 );
3364 if ( expression.size() == 3
3365 && expression.at( 2 ).userType() == QMetaType::Type::QVariantList && expression.at( 2 ).toList().count() > 1
3366 && expression.at( 2 ).toList().at( 0 ).toString() == QLatin1String(
"literal" ) )
3368 values = expression.at( 2 ).toList().at( 1 ).toList();
3371 for (
const QVariant &value : std::as_const( values ) )
3373 const QString part = parseValue( value, context );
3374 if ( part.isEmpty() )
3376 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3382 if ( parts.size() == 1 )
3384 if ( op == QLatin1String(
"in" ) )
3385 return QStringLiteral(
"%1 IS %2" ).arg( key, parts.at( 0 ) );
3387 return QStringLiteral(
"(%1 IS NULL OR %1 IS NOT %2)" ).arg( key, parts.at( 0 ) );
3391 if ( op == QLatin1String(
"in" ) )
3392 return QStringLiteral(
"%1 IN (%2)" ).arg( key, parts.join( QLatin1String(
", " ) ) );
3394 return QStringLiteral(
"(%1 IS NULL OR %1 NOT IN (%2))" ).arg( key, parts.join( QLatin1String(
", " ) ) );
3397 else if ( op == QLatin1String(
"get" ) )
3399 return parseKey( expression.value( 1 ), context );
3401 else if ( op == QLatin1String(
"match" ) )
3403 const QString attribute = expression.value( 1 ).toList().value( 1 ).toString();
3405 if ( expression.size() == 5
3406 && expression.at( 3 ).userType() == QMetaType::Type::Bool && expression.at( 3 ).toBool() ==
true
3407 && expression.at( 4 ).userType() == QMetaType::Type::Bool && expression.at( 4 ).toBool() ==
false )
3410 if ( expression.at( 2 ).userType() == QMetaType::Type::QVariantList || expression.at( 2 ).userType() == QMetaType::Type::QStringList )
3413 for (
const QVariant &p : expression.at( 2 ).toList() )
3415 parts << parseValue( p, context );
3418 if ( parts.size() > 1 )
3423 else if ( expression.at( 2 ).userType() == QMetaType::Type::QString || expression.at( 2 ).userType() == QMetaType::Type::Int
3424 || expression.at( 2 ).userType() == QMetaType::Type::Double || expression.at( 2 ).userType() == QMetaType::Type::LongLong )
3430 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3436 QString caseString = QStringLiteral(
"CASE " );
3437 for (
int i = 2; i < expression.size() - 2; i += 2 )
3439 if ( expression.at( i ).userType() == QMetaType::Type::QVariantList || expression.at( i ).userType() == QMetaType::Type::QStringList )
3442 for (
const QVariant &p : expression.at( i ).toList() )
3447 if ( parts.size() > 1 )
3452 else if ( expression.at( i ).userType() == QMetaType::Type::QString || expression.at( i ).userType() == QMetaType::Type::Int
3453 || expression.at( i ).userType() == QMetaType::Type::Double || expression.at( i ).userType() == QMetaType::Type::LongLong )
3458 caseString += QStringLiteral(
"THEN %1 " ).arg( parseValue( expression.at( i + 1 ), context, colorExpected ) );
3460 caseString += QStringLiteral(
"ELSE %1 END" ).arg( parseValue( expression.last(), context, colorExpected ) );
3464 else if ( op == QLatin1String(
"to-string" ) )
3466 return QStringLiteral(
"to_string(%1)" ).arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3468 else if ( op == QLatin1String(
"to-boolean" ) )
3470 return QStringLiteral(
"to_bool(%1)" ).arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3472 else if ( op == QLatin1String(
"case" ) )
3474 QString caseString = QStringLiteral(
"CASE" );
3475 for (
int i = 1; i < expression.size() - 2; i += 2 )
3477 const QString condition =
parseExpression( expression.value( i ).toList(), context );
3478 const QString value = parseValue( expression.value( i + 1 ), context );
3479 caseString += QStringLiteral(
" WHEN (%1) THEN %2" ).arg( condition, value );
3481 const QString value = parseValue( expression.constLast(), context );
3482 caseString += QStringLiteral(
" ELSE %1 END" ).arg( value );
3485 else if ( op == QLatin1String(
"zoom" ) && expression.count() == 1 )
3487 return QStringLiteral(
"@vector_tile_zoom" );
3489 else if ( op == QLatin1String(
"concat" ) )
3491 QString concatString = QStringLiteral(
"concat(" );
3492 for (
int i = 1; i < expression.size(); i++ )
3495 concatString += QLatin1String(
", " );
3496 concatString += parseValue( expression.value( i ), context );
3498 concatString += QLatin1Char(
')' );
3499 return concatString;
3501 else if ( op == QLatin1String(
"length" ) )
3503 return QStringLiteral(
"length(%1)" ).arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3505 else if ( op == QLatin1String(
"step" ) )
3507 const QString stepExpression =
parseExpression( expression.value( 1 ).toList(), context );
3508 if ( stepExpression.isEmpty() )
3510 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3514 QString caseString = QStringLiteral(
"CASE " );
3516 for (
int i = expression.length() - 2; i > 0; i -= 2 )
3518 const QString stepValue = parseValue( expression.value( i + 1 ), context, colorExpected );
3522 caseString += QStringLiteral(
" WHEN %1 >= %2 THEN (%3) " ).arg( stepExpression, stepKey, stepValue );
3526 caseString += QStringLiteral(
"ELSE (%1) END" ).arg( stepValue );
3533 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression \"%2\"" ).arg( context.
layerId(), op ) );
3542 QString actualName = name;
3543 const int categorySeparator = name.indexOf(
':' );
3544 if ( categorySeparator > 0 )
3546 category = name.left( categorySeparator );
3549 actualName = name.mid( categorySeparator + 1 );
3558 if ( category.isEmpty() )
3563 if ( spriteImage.isNull() )
3565 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3569 const QVariantMap spriteDefinition = context.
spriteDefinitions( category ).value( actualName ).toMap();
3570 if ( spriteDefinition.size() == 0 )
3572 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3576 const QImage sprite = spriteImage.copy( spriteDefinition.value( QStringLiteral(
"x" ) ).toInt(),
3577 spriteDefinition.value( QStringLiteral(
"y" ) ).toInt(),
3578 spriteDefinition.value( QStringLiteral(
"width" ) ).toInt(),
3579 spriteDefinition.value( QStringLiteral(
"height" ) ).toInt() );
3580 if ( sprite.isNull() )
3582 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3586 spriteSize = sprite.size() / spriteDefinition.value( QStringLiteral(
"pixelRatio" ) ).toDouble() * context.
pixelSizeConversionFactor();
3594 auto prepareBase64 = [](
const QImage & sprite )
3597 if ( !sprite.isNull() )
3600 QBuffer buffer( &blob );
3601 buffer.open( QIODevice::WriteOnly );
3602 sprite.save( &buffer,
"PNG" );
3604 const QByteArray encoded = blob.toBase64();
3605 path = QString( encoded );
3606 path.prepend( QLatin1String(
"base64:" ) );
3611 switch ( value.userType() )
3613 case QMetaType::Type::QString:
3615 QString spriteName = value.toString();
3616 const thread_local QRegularExpression fieldNameMatch( QStringLiteral(
"{([^}]+)}" ) );
3617 QRegularExpressionMatch match = fieldNameMatch.match( spriteName );
3618 if ( match.hasMatch() )
3620 const QString fieldName = match.captured( 1 );
3621 spriteProperty = QStringLiteral(
"CASE" );
3622 spriteSizeProperty = QStringLiteral(
"CASE" );
3624 spriteName.replace(
"(", QLatin1String(
"\\(" ) );
3625 spriteName.replace(
")", QLatin1String(
"\\)" ) );
3626 spriteName.replace( fieldNameMatch, QStringLiteral(
"([^\\/\\\\]+)" ) );
3627 const QRegularExpression fieldValueMatch( spriteName );
3629 for (
const QString &name : spriteNames )
3631 match = fieldValueMatch.match( name );
3632 if ( match.hasMatch() )
3636 const QString fieldValue = match.captured( 1 );
3638 path = prepareBase64( sprite );
3639 if ( spritePath.isEmpty() && !path.isEmpty() )
3645 spriteProperty += QStringLiteral(
" WHEN \"%1\" = '%2' THEN '%3'" )
3646 .arg( fieldName, fieldValue, path );
3647 spriteSizeProperty += QStringLiteral(
" WHEN \"%1\" = '%2' THEN %3" )
3648 .arg( fieldName ).arg( fieldValue ).arg( size.width() );
3652 spriteProperty += QLatin1String(
" END" );
3653 spriteSizeProperty += QLatin1String(
" END" );
3657 spriteProperty.clear();
3658 spriteSizeProperty.clear();
3659 const QImage sprite =
retrieveSprite( spriteName, context, spriteSize );
3660 spritePath = prepareBase64( sprite );
3665 case QMetaType::Type::QVariantMap:
3667 const QVariantList stops = value.toMap().value( QStringLiteral(
"stops" ) ).toList();
3668 if ( stops.size() == 0 )
3675 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, spriteSize );
3676 spritePath = prepareBase64( sprite );
3678 spriteProperty = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN '%2'" )
3679 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
3681 spriteSizeProperty = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN %2" )
3682 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
3683 .arg( spriteSize.width() );
3685 for (
int i = 0; i < stops.size() - 1; ++i )
3688 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, size );
3689 path = prepareBase64( sprite );
3691 spriteProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
3693 .arg( stops.value( i ).toList().value( 0 ).toString(),
3694 stops.value( i + 1 ).toList().value( 0 ).toString(),
3696 spriteSizeProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
3698 .arg( stops.value( i ).toList().value( 0 ).toString(),
3699 stops.value( i + 1 ).toList().value( 0 ).toString() )
3700 .arg( size.width() );
3702 sprite =
retrieveSprite( stops.last().toList().value( 1 ).toString(), context, size );
3703 path = prepareBase64( sprite );
3705 spriteProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
3707 .arg( stops.last().toList().value( 0 ).toString() )
3709 spriteSizeProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
3711 .arg( stops.last().toList().value( 0 ).toString() )
3712 .arg( size.width() );
3716 case QMetaType::Type::QVariantList:
3718 const QVariantList json = value.toList();
3719 const QString method = json.value( 0 ).toString();
3721 if ( method == QLatin1String(
"match" ) )
3723 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
3724 if ( attribute.isEmpty() )
3726 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
3730 spriteProperty = QStringLiteral(
"CASE" );
3731 spriteSizeProperty = QStringLiteral(
"CASE" );
3733 for (
int i = 2; i < json.length() - 1; i += 2 )
3735 const QVariant matchKey = json.value( i );
3736 const QVariant matchValue = json.value( i + 1 );
3737 QString matchString;
3738 switch ( matchKey.userType() )
3740 case QMetaType::Type::QVariantList:
3741 case QMetaType::Type::QStringList:
3743 const QVariantList keys = matchKey.toList();
3744 QStringList matchStringList;
3745 for (
const QVariant &key : keys )
3749 matchString = matchStringList.join(
',' );
3753 case QMetaType::Type::Bool:
3754 case QMetaType::Type::QString:
3755 case QMetaType::Type::Int:
3756 case QMetaType::Type::LongLong:
3757 case QMetaType::Type::Double:
3764 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported sprite type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( value.userType() ) ) ) );
3769 const QImage sprite =
retrieveSprite( matchValue.toString(), context, spriteSize );
3770 spritePath = prepareBase64( sprite );
3772 spriteProperty += QStringLiteral(
" WHEN %1 IN (%2) "
3773 "THEN '%3'" ).arg( attribute,
3777 spriteSizeProperty += QStringLiteral(
" WHEN %1 IN (%2) "
3778 "THEN %3" ).arg( attribute,
3779 matchString ).arg( spriteSize.width() );
3782 if ( !json.constLast().toString().isEmpty() )
3784 const QImage sprite =
retrieveSprite( json.constLast().toString(), context, spriteSize );
3785 spritePath = prepareBase64( sprite );
3789 spritePath = QString();
3792 spriteProperty += QStringLiteral(
" ELSE '%1' END" ).arg( spritePath );
3793 spriteSizeProperty += QStringLiteral(
" ELSE %3 END" ).arg( spriteSize.width() );
3796 else if ( method == QLatin1String(
"step" ) )
3798 const QString expression =
parseExpression( json.value( 1 ).toList(), context );
3799 if ( expression.isEmpty() )
3801 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3805 spriteProperty = QStringLiteral(
"CASE" );
3806 spriteSizeProperty = QStringLiteral(
"CASE" );
3807 for (
int i = json.length() - 2; i > 2; i -= 2 )
3810 const QString stepValue = json.value( i + 1 ).toString();
3812 const QImage sprite =
retrieveSprite( stepValue, context, spriteSize );
3813 spritePath = prepareBase64( sprite );
3815 spriteProperty += QStringLiteral(
" WHEN %1 >= %2 THEN '%3' " ).arg( expression, stepKey, spritePath );
3816 spriteSizeProperty += QStringLiteral(
" WHEN %1 >= %2 THEN %3 " ).arg( expression ).arg( stepKey ).arg( spriteSize.width() );
3819 const QImage sprite =
retrieveSprite( json.at( 2 ).toString(), context, spriteSize );
3820 spritePath = prepareBase64( sprite );
3822 spriteProperty += QStringLiteral(
"ELSE '%1' END" ).arg( spritePath );
3823 spriteSizeProperty += QStringLiteral(
"ELSE %3 END" ).arg( spriteSize.width() );
3826 else if ( method == QLatin1String(
"case" ) )
3828 spriteProperty = QStringLiteral(
"CASE" );
3829 spriteSizeProperty = QStringLiteral(
"CASE" );
3830 for (
int i = 1; i < json.length() - 2; i += 2 )
3832 const QString caseExpression =
parseExpression( json.value( i ).toList(), context );
3833 const QString caseValue = json.value( i + 1 ).toString();
3835 const QImage sprite =
retrieveSprite( caseValue, context, spriteSize );
3836 spritePath = prepareBase64( sprite );
3838 spriteProperty += QStringLiteral(
" WHEN %1 THEN '%2' " ).arg( caseExpression, spritePath );
3839 spriteSizeProperty += QStringLiteral(
" WHEN %1 THEN %2 " ).arg( caseExpression ).arg( spriteSize.width() );
3841 const QImage sprite =
retrieveSprite( json.last().toString(), context, spriteSize );
3842 spritePath = prepareBase64( sprite );
3844 spriteProperty += QStringLiteral(
"ELSE '%1' END" ).arg( spritePath );
3845 spriteSizeProperty += QStringLiteral(
"ELSE %3 END" ).arg( spriteSize.width() );
3850 context.
pushWarning( QObject::tr(
"%1: Could not interpret sprite value list with method %2" ).arg( context.
layerId(), method ) );
3856 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported sprite type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( value.userType() ) ) ) );
3866 switch ( value.userType() )
3868 case QMetaType::Type::QVariantList:
3869 case QMetaType::Type::QStringList:
3872 case QMetaType::Type::Bool:
3873 case QMetaType::Type::QString:
3874 if ( colorExpected )
3879 return parseValue(
c, context );
3884 case QMetaType::Type::Int:
3885 case QMetaType::Type::LongLong:
3886 case QMetaType::Type::Double:
3887 return value.toString();
3889 case QMetaType::Type::QColor:
3890 c = value.value<QColor>();
3891 return QString(
"color_rgba(%1,%2,%3,%4)" ).arg(
c.red() ).arg(
c.green() ).arg(
c.blue() ).arg(
c.alpha() );
3894 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression part" ).arg( context.
layerId() ) );
3902 if ( value.toString() == QLatin1String(
"$type" ) )
3904 return QStringLiteral(
"_geom_type" );
3906 if ( value.toString() == QLatin1String(
"level" ) )
3908 return QStringLiteral(
"level" );
3910 else if ( ( value.userType() == QMetaType::Type::QVariantList && value.toList().size() == 1 ) || value.userType() == QMetaType::Type::QStringList )
3912 if ( value.toList().size() > 1 )
3913 return value.toList().at( 1 ).toString();
3916 QString valueString = value.toList().value( 0 ).toString();
3917 if ( valueString == QLatin1String(
"geometry-type" ) )
3919 return QStringLiteral(
"_geom_type" );
3924 else if ( value.userType() == QMetaType::Type::QVariantList && value.toList().size() > 1 )
3931QString QgsMapBoxGlStyleConverter::processLabelField(
const QString &
string,
bool &isExpression )
3935 const thread_local QRegularExpression singleFieldRx( QStringLiteral(
"^{([^}]+)}$" ) );
3936 const QRegularExpressionMatch match = singleFieldRx.match(
string );
3937 if ( match.hasMatch() )
3939 isExpression =
false;
3940 return match.captured( 1 );
3943 const thread_local QRegularExpression multiFieldRx( QStringLiteral(
"(?={[^}]+})" ) );
3944 const QStringList parts =
string.split( multiFieldRx );
3945 if ( parts.size() > 1 )
3947 isExpression =
true;
3950 for (
const QString &part : parts )
3952 if ( part.isEmpty() )
3955 if ( !part.contains(
'{' ) )
3962 const QStringList split = part.split(
'}' );
3964 if ( !split.at( 1 ).isEmpty() )
3967 return QStringLiteral(
"concat(%1)" ).arg( res.join(
',' ) );
3971 isExpression =
false;
3978 return mRenderer ? mRenderer->
clone() :
nullptr;
3983 return mLabeling ? mLabeling->
clone() :
nullptr;
3993 return mRasterSubLayers;
3998 QList<QgsMapLayer *> subLayers;
4001 const QString sourceName = subLayer.source();
4002 std::unique_ptr< QgsRasterLayer > rl;
4009 rl->pipe()->setDataDefinedProperties( subLayer.dataDefinedProperties() );
4016 subLayers.append( rl.release() );
4025 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
4028 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
4029 context = tmpContext.get();
4034 if (
string.compare( QLatin1String(
"vector" ), Qt::CaseInsensitive ) == 0 )
4036 else if (
string.compare( QLatin1String(
"raster" ), Qt::CaseInsensitive ) == 0 )
4038 else if (
string.compare( QLatin1String(
"raster-dem" ), Qt::CaseInsensitive ) == 0 )
4040 else if (
string.compare( QLatin1String(
"geojson" ), Qt::CaseInsensitive ) == 0 )
4042 else if (
string.compare( QLatin1String(
"image" ), Qt::CaseInsensitive ) == 0 )
4044 else if (
string.compare( QLatin1String(
"video" ), Qt::CaseInsensitive ) == 0 )
4046 context->
pushWarning( QObject::tr(
"Invalid source type \"%1\" for source \"%2\"" ).arg(
string, name ) );
4052 const QString name = it.key();
4053 const QVariantMap jsonSource = it.value().toMap();
4054 const QString typeString = jsonSource.value( QStringLiteral(
"type" ) ).toString();
4077 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
4080 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
4081 context = tmpContext.get();
4084 auto raster = std::make_unique< QgsMapBoxGlStyleRasterSource >( name );
4085 if ( raster->setFromJson( source, context ) )
4086 mSources.append( raster.release() );
4089bool QgsMapBoxGlStyleConverter::numericArgumentsOnly(
const QVariant &bottomVariant,
const QVariant &topVariant,
double &bottom,
double &top )
4091 if ( bottomVariant.canConvert( QMetaType::Double ) && topVariant.canConvert( QMetaType::Double ) )
4093 bool bDoubleOk, tDoubleOk;
4094 bottom = bottomVariant.toDouble( &bDoubleOk );
4095 top = topVariant.toDouble( &tDoubleOk );
4096 return ( bDoubleOk && tDoubleOk );
4107 mWarnings << warning;
4122 return mSizeConversionFactor;
4127 mSizeConversionFactor = sizeConversionFactor;
4132 return mSpriteImage.keys();
4137 return mSpriteImage.contains( category ) ? mSpriteImage[category] : QImage();
4142 return mSpriteDefinitions.contains( category ) ? mSpriteDefinitions[category] : QVariantMap();
4147 mSpriteImage[category] = image;
4148 mSpriteDefinitions[category] = definitions;
4198 mAttribution = json.value( QStringLiteral(
"attribution" ) ).toString();
4200 const QString scheme = json.value( QStringLiteral(
"scheme" ), QStringLiteral(
"xyz" ) ).toString();
4201 if ( scheme.compare( QLatin1String(
"xyz" ) ) == 0 )
4207 context->
pushWarning( QObject::tr(
"%1 scheme is not supported for raster source %2" ).arg( scheme,
name() ) );
4211 mMinZoom = json.value( QStringLiteral(
"minzoom" ), QStringLiteral(
"0" ) ).toInt();
4212 mMaxZoom = json.value( QStringLiteral(
"maxzoom" ), QStringLiteral(
"22" ) ).toInt();
4213 mTileSize = json.value( QStringLiteral(
"tileSize" ), QStringLiteral(
"512" ) ).toInt();
4215 const QVariantList
tiles = json.value( QStringLiteral(
"tiles" ) ).toList();
4216 for (
const QVariant &tile :
tiles )
4218 mTiles.append( tile.toString() );
4227 parts.insert( QStringLiteral(
"type" ), QStringLiteral(
"xyz" ) );
4228 parts.insert( QStringLiteral(
"url" ), mTiles.value( 0 ) );
4230 if ( mTileSize == 256 )
4231 parts.insert( QStringLiteral(
"tilePixelRation" ), QStringLiteral(
"1" ) );
4232 else if ( mTileSize == 512 )
4233 parts.insert( QStringLiteral(
"tilePixelRation" ), QStringLiteral(
"2" ) );
4235 parts.insert( QStringLiteral(
"zmax" ), QString::number( mMaxZoom ) );
4236 parts.insert( QStringLiteral(
"zmin" ), QString::number( mMinZoom ) );
4239 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.
QImage spriteImage(const QString &category=QString()) const
Returns the sprite image for a given category to use during conversion, or an invalid image if this i...
double pixelSizeConversionFactor() const
Returns the pixel size conversion factor, used to scale the original pixel sizes when converting styl...
void setTargetUnit(Qgis::RenderUnit targetUnit)
Sets the target unit type.
void setSprites(const QImage &image, const QVariantMap &definitions, const QString &category=QString())
Sets the sprite image and definitions JSON for a given category to use during conversion.
void setPixelSizeConversionFactor(double sizeConversionFactor)
Sets the pixel size conversion factor, used to scale the original pixel sizes when converting styles.
QVariantMap spriteDefinitions(const QString &category=QString()) const
Returns the sprite definitions for a given category to use during conversion.
Qgis::RenderUnit targetUnit() const
Returns the target unit type.
QString layerId() const
Returns the layer ID of the layer currently being converted.
QStringList spriteCategories() const
Returns the list of sprite categories to use during conversion, or an empty list of none is set.
void clearWarnings()
Clears the list of warning messages.
static QString parseOpacityStops(double base, const QVariantList &stops, int maxOpacity, QgsMapBoxGlStyleConversionContext &context)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate alpha ...
static QString parseColorExpression(const QVariant &colorExpression, QgsMapBoxGlStyleConversionContext &context)
Converts an expression representing a color to a string (can be color string or an expression where a...
static QgsProperty parseInterpolateOpacityByZoom(const QVariantMap &json, int maxOpacity, QgsMapBoxGlStyleConversionContext *contextPtr=nullptr)
Interpolates opacity with either scale_linear() or scale_exp() (depending on base value).
static QString parseStops(double base, const QVariantList &stops, double multiplier, QgsMapBoxGlStyleConversionContext &context)
Parses a list of interpolation stops.
QgsVectorTileRenderer * renderer() const
Returns a new instance of a vector tile renderer representing the converted style,...
static QString parseExpression(const QVariantList &expression, QgsMapBoxGlStyleConversionContext &context, bool colorExpected=false)
Converts a MapBox GL expression to a QGIS expression.
PropertyType
Property types, for interpolated value conversion.
@ Point
Point/offset property.
@ Numeric
Numeric property (e.g. line width, text size).
@ Opacity
Opacity property.
@ NumericArray
Numeric array for dash arrays or such.
QList< QgsMapBoxGlStyleAbstractSource * > sources()
Returns the list of converted sources.
QgsVectorTileLabeling * labeling() const
Returns a new instance of a vector tile labeling representing the converted style,...
QList< QgsMapBoxGlStyleRasterSubLayer > rasterSubLayers() const
Returns a list of raster sub layers contained in the style.
static QgsProperty parseInterpolateByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, double *defaultNumber=nullptr)
Parses a numeric value which is interpolated by zoom range.
static Qt::PenJoinStyle parseJoinStyle(const QString &style)
Converts a value to Qt::PenJoinStyle enum from JSON value.
static QgsProperty parseInterpolateStringByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, const QVariantMap &conversionMap, QString *defaultString=nullptr)
Interpolates a string by zoom.
static QgsProperty parseStepList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a match function value list.
static QgsProperty parseInterpolatePointByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, QPointF *defaultPoint=nullptr)
Interpolates a point/offset with either scale_linear() or scale_exp() (depending on base value).
static bool parseCircleLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context)
Parses a circle layer.
Result convert(const QVariantMap &style, QgsMapBoxGlStyleConversionContext *context=nullptr)
Converts a JSON style map, and returns the resultant status of the conversion.
static QgsProperty parseInterpolateListByZoom(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Interpolates a list which starts with the interpolate function.
~QgsMapBoxGlStyleConverter()
QList< QgsMapLayer * > createSubLayers() const
Returns a list of new map layers corresponding to sublayers of the style, e.g.
Result
Result of conversion.
@ Success
Conversion was successful.
@ NoLayerList
No layer list was found in JSON input.
QgsMapBoxGlStyleConverter()
Constructor for QgsMapBoxGlStyleConverter.
static QImage retrieveSprite(const QString &name, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize)
Retrieves the sprite image with the specified name, taken from the specified context.
static QString parseLabelStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context)
Parses a list of interpolation stops containing label values.
void parseLayers(const QVariantList &layers, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse list of layers from JSON.
static QgsProperty parseInterpolateColorByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, QColor *defaultColor=nullptr)
Parses a color value which is interpolated by zoom range.
static QString retrieveSpriteAsBase64WithProperties(const QVariant &value, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize, QString &spriteProperty, QString &spriteSizeProperty)
Retrieves the sprite image with the specified name, taken from the specified context as a base64 enco...
void parseSources(const QVariantMap &sources, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse list of sources from JSON.
static QString interpolateExpression(double zoomMin, double zoomMax, QVariant valueMin, QVariant valueMax, double base, double multiplier=1, QgsMapBoxGlStyleConversionContext *contextPtr=nullptr)
Generates an interpolation for values between valueMin and valueMax, scaled between the ranges zoomMi...
static QColor parseColor(const QVariant &color, QgsMapBoxGlStyleConversionContext &context)
Parses a color in one of these supported formats:
static bool parseSymbolLayerAsRenderer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as a renderer.
static bool parseFillLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context, bool isBackgroundStyle=false)
Parses a fill layer.
static void parseSymbolLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, bool &hasRenderer, QgsVectorTileBasicLabelingStyle &labelingStyle, bool &hasLabeling, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as renderer or labeling.
static bool parseLineLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context)
Parses a line layer.
void parseRasterSource(const QVariantMap &source, const QString &name, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse a raster source from JSON.
static void colorAsHslaComponents(const QColor &color, int &hue, int &saturation, int &lightness, int &alpha)
Takes a QColor object and returns HSLA components in required format for QGIS color_hsla() expression...
static Qt::PenCapStyle parseCapStyle(const QString &style)
Converts a value to Qt::PenCapStyle enum from JSON value.
static QString parseStringStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, const QVariantMap &conversionMap, QString *defaultString=nullptr)
Parses a list of interpolation stops containing string values.
static QgsProperty parseMatchList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a match function value list.
static QgsProperty parseValueList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a value list (e.g.
static QString parseArrayStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier=1)
Takes numerical arrays from stops.
static QString parsePointStops(double base, const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier=1)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate point/...
Encapsulates a MapBox GL style raster source.
Qgis::MapBoxGlStyleSourceType type() const override
Returns the source type.
QgsMapBoxGlStyleRasterSource(const QString &name)
Constructor for QgsMapBoxGlStyleRasterSource.
QgsRasterLayer * toRasterLayer() const
Returns a new raster layer representing the raster source, or nullptr if the source cannot be represe...
bool setFromJson(const QVariantMap &json, QgsMapBoxGlStyleConversionContext *context) override
Sets the source's state from a json map.
QStringList tiles() const
Returns the list of tile sources.
Encapsulates a MapBox GL style raster sub layer.
QString source() const
Returns the layer's source.
QString id() const
Returns the layer's ID.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the layer's data defined properties.
QgsMapBoxGlStyleRasterSubLayer(const QString &id, const QString &source)
Constructor for QgsMapBoxGlStyleRasterSubLayer, with the given id and source.
Line symbol layer type which draws repeating marker symbols along a line feature.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
virtual void setSize(double size)
Sets the symbol size.
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's offset.
void setAngle(double angle)
Sets the rotation angle for the marker.
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's size.
A marker symbol type, for rendering Point and MultiPoint geometries.
void setEnabled(bool enabled)
Sets whether the effect is enabled.
Contains settings for how a map layer will be labeled.
double yOffset
Vertical offset of label.
const QgsLabelObstacleSettings & obstacleSettings() const
Returns the label obstacle settings.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
double xOffset
Horizontal offset of label.
Qgis::LabelPlacement placement
Label placement mode.
Qgis::LabelMultiLineAlignment multilineAlign
Horizontal alignment of multi-line labels.
int priority
Label priority.
double angleOffset
Label rotation, in degrees clockwise.
Qgis::RenderUnit offsetUnits
Units for offsets of label.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the label's property collection, used for data defined overrides.
bool isExpression
true if this label is made from a expression string, e.g., FieldName || 'mm'
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
double dist
Distance from feature to the label.
Qgis::RenderUnit distUnits
Units the distance from feature to the label.
@ LinePlacementOptions
Line placement flags.
@ LabelRotation
Label rotation.
@ FontStyle
Font style name.
@ FontLetterSpacing
Letter spacing.
QString fieldName
Name of field (or an expression) to use for label text.
int autoWrapLength
If non-zero, indicates that label text should be automatically wrapped to (ideally) the specified num...
const QgsLabelPointSettings & pointSettings() const
Returns the label point settings, which contain settings related to how the label engine places and f...
A grouped map of multiple QgsProperty objects, each referenced by an integer key value.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const final
Returns the calculated value of the property with the specified key from within the collection.
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
QgsProperty property(int key) const final
Returns a matching property from the collection, if one exists.
A store for object properties.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
QString expressionString() const
Returns the expression used for the property value.
QVariant value(const QgsExpressionContext &context, const QVariant &defaultValue=QVariant(), bool *ok=nullptr) const
Calculates the current value of the property, including any transforms which are set for the property...
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
void setExpressionString(const QString &expression)
Sets the expression to use for the property value.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
A fill symbol layer which fills polygons with a repeated raster image.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the unit for the image's width and height.
void setOpacity(double opacity)
Sets the opacity for the raster image used in the fill.
void setImageFilePath(const QString &imagePath)
Sets the path to the raster image used for the fill.
void setWidth(double width)
Sets the width for scaling the image used in the fill.
void setCoordinateMode(Qgis::SymbolCoordinateReference mode)
Set the coordinate mode for fill.
Represents a raster layer.
Line symbol layer type which draws line sections using a raster image file.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Raster marker symbol layer class.
void setOpacity(double opacity)
Set the marker opacity.
void setPath(const QString &path)
Set the marker raster image path.
@ RendererOpacity
Raster renderer global opacity.
Renders polygons using a single fill and stroke color.
void setBrushStyle(Qt::BrushStyle style)
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setStrokeWidth(double strokeWidth)
void setStrokeStyle(Qt::PenStyle strokeStyle)
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the unit for the fill's offset.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
void setOffset(QPointF offset)
Sets an offset by which polygons will be translated during rendering.
void setStrokeColor(const QColor &strokeColor) override
Sets the stroke color for the symbol layer.
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
void setPenCapStyle(Qt::PenCapStyle style)
Sets the pen cap style used to render the line (e.g.
void setUseCustomDashPattern(bool b)
Sets whether the line uses a custom dash pattern.
void setCustomDashVector(const QVector< qreal > &vector)
Sets the custom dash vector, which is the pattern of alternating drawn/skipped lengths used while ren...
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the pen join style used to render the line (e.g.
Simple marker symbol layer, consisting of a rendered shape with solid fill color and a stroke.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
void setStrokeWidthUnit(Qgis::RenderUnit u)
Sets the unit for the width of the marker's stroke.
void setStrokeWidth(double w)
Sets the width of the marker's stroke.
void setStrokeColor(const QColor &color) override
Sets the marker's stroke color.
static QColor parseColor(const QString &colorStr, bool strictEval=false)
Attempts to parse a string as a color using a variety of common formats, including hex codes,...
@ File
Filename, eg for svg files.
@ CustomDash
Custom dash pattern.
@ Name
Name, eg shape name for simple markers.
@ StrokeColor
Stroke color.
@ Interval
Line marker interval.
@ StrokeWidth
Stroke width.
@ LayerEnabled
Whether symbol layer is enabled.
virtual void setColor(const QColor &color)
Sets the "representative" color for the symbol layer.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the symbol layer's property collection, used for data defined overrides.
void setPlacements(Qgis::MarkerLinePlacements placements)
Sets the placement of the symbols.
Container for settings relating to a text background object.
void setMarkerSymbol(QgsMarkerSymbol *symbol)
Sets the current marker symbol for the background shape.
void setSizeType(SizeType type)
Sets the method used to determine the size of the background shape (e.g., fixed size or buffer around...
@ ShapeMarkerSymbol
Marker symbol.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units used for the shape's size.
void setType(ShapeType type)
Sets the type of background shape to draw (e.g., square, ellipse, SVG).
void setEnabled(bool enabled)
Sets whether the text background will be drawn.
void setSize(QSizeF size)
Sets the size of the background shape.
void setColor(const QColor &color)
Sets the color for the buffer.
void setOpacity(double opacity)
Sets the buffer opacity.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units used for the buffer size.
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the buffer.
void setSize(double size)
Sets the size of the buffer.
Container for all settings relating to text rendering.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void setSize(double size)
Sets the size for rendered text.
void setFont(const QFont &font)
Sets the font used for rendering text.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the size of rendered text.
void setBackground(const QgsTextBackgroundSettings &backgroundSettings)
Sets the text's background settings.q.
void setNamedStyle(const QString &style)
Sets the named style for the font used for rendering text.
QFont font() const
Returns the font used for rendering text.
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
Configuration of a single style within QgsVectorTileBasicLabeling.
void setLayerName(const QString &name)
Sets name of the sub-layer to render (empty layer means that all layers match).
void setMinZoomLevel(int minZoom)
Sets minimum zoom level index (negative number means no limit).
void setFilterExpression(const QString &expr)
Sets filter expression (empty filter means that all features match).
void setMaxZoomLevel(int maxZoom)
Sets maximum zoom level index (negative number means no limit).
void setStyleName(const QString &name)
Sets human readable name of this style.
void setGeometryType(Qgis::GeometryType geomType)
Sets type of the geometry that will be used (point / line / polygon).
void setLabelSettings(const QgsPalLayerSettings &settings)
Sets labeling configuration of this style.
void setEnabled(bool enabled)
Sets whether this style is enabled (used for rendering).
Definition of map rendering of a subset of vector tile data.
void setEnabled(bool enabled)
Sets whether this style is enabled (used for rendering).
void setMinZoomLevel(int minZoom)
Sets minimum zoom level index (negative number means no limit).
void setLayerName(const QString &name)
Sets name of the sub-layer to render (empty layer means that all layers match).
void setFilterExpression(const QString &expr)
Sets filter expression (empty filter means that all features match).
void setSymbol(QgsSymbol *sym)
Sets symbol for rendering. Takes ownership of the symbol.
void setStyleName(const QString &name)
Sets human readable name of this style.
void setMaxZoomLevel(int maxZoom)
Sets maximum zoom level index (negative number means no limit).
void setGeometryType(Qgis::GeometryType geomType)
Sets type of the geometry that will be used (point / line / polygon).
Base class for labeling configuration classes for vector tile layers.
virtual QgsVectorTileLabeling * clone() const =0SIP_FACTORY
Returns a new copy of the object.
Abstract base class for all vector tile renderer implementations.
virtual QgsVectorTileRenderer * clone() const =0
Returns a clone of the renderer.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
#define QgsDebugError(str)
QList< QgsSymbolLayer * > QgsSymbolLayerList