22#include "moc_qgsmapboxglstyleconverter.cpp"
48#include <QRegularExpression>
59 if ( style.contains( QStringLiteral(
"sources" ) ) )
61 parseSources( style.value( QStringLiteral(
"sources" ) ).toMap(), context );
64 if ( style.contains( QStringLiteral(
"layers" ) ) )
66 parseLayers( style.value( QStringLiteral(
"layers" ) ).toList(), context );
70 mError = QObject::tr(
"Could not find layers list in JSON" );
83 qDeleteAll( mSources );
88 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
91 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
92 context = tmpContext.get();
95 QList<QgsVectorTileBasicRendererStyle> rendererStyles;
96 QList<QgsVectorTileBasicLabelingStyle> labelingStyles;
99 bool hasRendererBackgroundStyle =
false;
101 for (
const QVariant &layer : layers )
103 const QVariantMap jsonLayer = layer.toMap();
105 const QString layerType = jsonLayer.value( QStringLiteral(
"type" ) ).toString();
106 if ( layerType == QLatin1String(
"background" ) )
108 hasRendererBackgroundStyle =
parseFillLayer( jsonLayer, rendererBackgroundStyle, *context,
true );
109 if ( hasRendererBackgroundStyle )
119 const QString styleId = jsonLayer.value( QStringLiteral(
"id" ) ).toString();
122 if ( layerType.compare( QLatin1String(
"raster" ), Qt::CaseInsensitive ) == 0 )
125 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
126 if ( jsonPaint.contains( QStringLiteral(
"raster-opacity" ) ) )
128 const QVariant jsonRasterOpacity = jsonPaint.value( QStringLiteral(
"raster-opacity" ) );
129 double defaultOpacity = 1;
133 mRasterSubLayers.append( raster );
137 const QString layerName = jsonLayer.value( QStringLiteral(
"source-layer" ) ).toString();
139 const int minZoom = jsonLayer.value( QStringLiteral(
"minzoom" ), QStringLiteral(
"-1" ) ).toInt();
148 int maxZoom = jsonLayer.value( QStringLiteral(
"maxzoom" ), QStringLiteral(
"-1" ) ).toInt();
153 if ( jsonLayer.contains( QStringLiteral(
"visibility" ) ) )
155 visibilyStr = jsonLayer.value( QStringLiteral(
"visibility" ) ).toString();
157 else if ( jsonLayer.contains( QStringLiteral(
"layout" ) ) && jsonLayer.value( QStringLiteral(
"layout" ) ).userType() == QMetaType::Type::QVariantMap )
159 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
160 visibilyStr = jsonLayout.value( QStringLiteral(
"visibility" ) ).toString();
163 const bool enabled = visibilyStr != QLatin1String(
"none" );
165 QString filterExpression;
166 if ( jsonLayer.contains( QStringLiteral(
"filter" ) ) )
168 filterExpression =
parseExpression( jsonLayer.value( QStringLiteral(
"filter" ) ).toList(), *context );
174 bool hasRendererStyle =
false;
175 bool hasLabelingStyle =
false;
176 if ( layerType == QLatin1String(
"fill" ) )
178 hasRendererStyle =
parseFillLayer( jsonLayer, rendererStyle, *context );
180 else if ( layerType == QLatin1String(
"line" ) )
182 hasRendererStyle =
parseLineLayer( jsonLayer, rendererStyle, *context );
184 else if ( layerType == QLatin1String(
"circle" ) )
188 else if ( layerType == QLatin1String(
"symbol" ) )
190 parseSymbolLayer( jsonLayer, rendererStyle, hasRendererStyle, labelingStyle, hasLabelingStyle, *context );
194 mWarnings << QObject::tr(
"%1: Skipping unknown layer type %2" ).arg( context->
layerId(), layerType );
199 if ( hasRendererStyle )
207 rendererStyles.append( rendererStyle );
210 if ( hasLabelingStyle )
218 labelingStyles.append( labelingStyle );
221 mWarnings.append( context->
warnings() );
225 if ( hasRendererBackgroundStyle )
226 rendererStyles.prepend( rendererBackgroundStyle );
228 mRenderer = std::make_unique< QgsVectorTileBasicRenderer >();
230 renderer->setStyles( rendererStyles );
232 mLabeling = std::make_unique< QgsVectorTileBasicLabeling >();
234 labeling->setStyles( labelingStyles );
239 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
244 bool colorIsDataDefined =
false;
246 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsFillSymbol >() );
250 if ( jsonPaint.contains( isBackgroundStyle ? QStringLiteral(
"background-color" ) : QStringLiteral(
"fill-color" ) ) )
252 const QVariant jsonFillColor = jsonPaint.value( isBackgroundStyle ? QStringLiteral(
"background-color" ) : QStringLiteral(
"fill-color" ) );
253 switch ( jsonFillColor.userType() )
255 case QMetaType::Type::QVariantMap:
259 case QMetaType::Type::QVariantList:
260 case QMetaType::Type::QStringList:
261 colorIsDataDefined =
true;
265 case QMetaType::Type::QString:
266 fillColor =
parseColor( jsonFillColor.toString(), context );
271 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillColor.userType() ) ) ) );
279 fillColor = QColor( 0, 0, 0 );
282 QColor fillOutlineColor;
283 if ( !isBackgroundStyle )
285 if ( !jsonPaint.contains( QStringLiteral(
"fill-outline-color" ) ) )
287 if ( fillColor.isValid() )
288 fillOutlineColor = fillColor;
296 const QVariant jsonFillOutlineColor = jsonPaint.value( QStringLiteral(
"fill-outline-color" ) );
297 switch ( jsonFillOutlineColor.userType() )
299 case QMetaType::Type::QVariantMap:
303 case QMetaType::Type::QVariantList:
304 case QMetaType::Type::QStringList:
308 case QMetaType::Type::QString:
309 fillOutlineColor =
parseColor( jsonFillOutlineColor.toString(), context );
313 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-outline-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillOutlineColor.userType() ) ) ) );
319 double fillOpacity = -1.0;
320 double rasterOpacity = -1.0;
321 if ( jsonPaint.contains( isBackgroundStyle ? QStringLiteral(
"background-opacity" ) : QStringLiteral(
"fill-opacity" ) ) )
323 const QVariant jsonFillOpacity = jsonPaint.value( isBackgroundStyle ? QStringLiteral(
"background-opacity" ) : QStringLiteral(
"fill-opacity" ) );
324 switch ( jsonFillOpacity.userType() )
326 case QMetaType::Type::Int:
327 case QMetaType::Type::LongLong:
328 case QMetaType::Type::Double:
329 fillOpacity = jsonFillOpacity.toDouble();
330 rasterOpacity = fillOpacity;
333 case QMetaType::Type::QVariantMap:
346 case QMetaType::Type::QVariantList:
347 case QMetaType::Type::QStringList:
361 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillOpacity.userType() ) ) ) );
367 QPointF fillTranslate;
368 if ( jsonPaint.contains( QStringLiteral(
"fill-translate" ) ) )
370 const QVariant jsonFillTranslate = jsonPaint.value( QStringLiteral(
"fill-translate" ) );
371 switch ( jsonFillTranslate.userType() )
374 case QMetaType::Type::QVariantMap:
378 case QMetaType::Type::QVariantList:
379 case QMetaType::Type::QStringList:
385 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-translate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonFillTranslate.userType() ) ) ) );
391 Q_ASSERT( fillSymbol );
394 symbol->setOutputUnit( context.
targetUnit() );
397 if ( !fillTranslate.isNull() )
403 if ( jsonPaint.contains( isBackgroundStyle ? QStringLiteral(
"background-pattern" ) : QStringLiteral(
"fill-pattern" ) ) )
407 const QVariant fillPatternJson = jsonPaint.value( isBackgroundStyle ? QStringLiteral(
"background-pattern" ) : QStringLiteral(
"fill-pattern" ) );
410 fillColor = QColor();
411 fillOutlineColor = QColor();
418 QString spriteProperty, spriteSizeProperty;
420 if ( !sprite.isEmpty() )
425 rasterFill->
setWidth( spriteSize.width() );
429 if ( rasterOpacity >= 0 )
434 if ( !spriteProperty.isEmpty() )
441 symbol->appendSymbolLayer( rasterFill );
447 if ( fillOpacity != -1 )
449 symbol->setOpacity( fillOpacity );
460 if ( fillOutlineColor.isValid() && ( fillOutlineColor.alpha() == 255 || fillOutlineColor != fillColor ) )
471 if ( fillColor.isValid() )
475 else if ( colorIsDataDefined )
491 if ( !jsonLayer.contains( QStringLiteral(
"paint" ) ) )
493 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
498 QString rasterLineSprite;
500 const QVariantMap jsonPaint = jsonLayer.
value( QStringLiteral(
"paint" ) ).toMap();
501 if ( jsonPaint.contains( QStringLiteral(
"line-pattern" ) ) )
503 const QVariant jsonLinePattern = jsonPaint.value( QStringLiteral(
"line-pattern" ) );
504 switch ( jsonLinePattern.userType() )
506 case QMetaType::Type::QVariantMap:
507 case QMetaType::Type::QString:
510 QString spriteProperty, spriteSizeProperty;
516 case QMetaType::Type::QVariantList:
517 case QMetaType::Type::QStringList:
522 if ( rasterLineSprite.isEmpty() )
525 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-pattern property" ).arg( context.
layerId() ) );
532 if ( jsonPaint.contains( QStringLiteral(
"line-color" ) ) )
534 const QVariant jsonLineColor = jsonPaint.value( QStringLiteral(
"line-color" ) );
535 switch ( jsonLineColor.userType() )
537 case QMetaType::Type::QVariantMap:
542 case QMetaType::Type::QVariantList:
543 case QMetaType::Type::QStringList:
548 case QMetaType::Type::QString:
549 lineColor =
parseColor( jsonLineColor.toString(), context );
553 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineColor.userType() ) ) ) );
560 lineColor = QColor( 0, 0, 0 );
566 if ( jsonPaint.contains( QStringLiteral(
"line-width" ) ) )
568 const QVariant jsonLineWidth = jsonPaint.
value( QStringLiteral(
"line-width" ) );
569 switch ( jsonLineWidth.userType() )
571 case QMetaType::Type::Int:
572 case QMetaType::Type::LongLong:
573 case QMetaType::Type::Double:
577 case QMetaType::Type::QVariantMap:
589 case QMetaType::Type::QVariantList:
590 case QMetaType::Type::QStringList:
602 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported fill-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineWidth.userType() ) ) ) );
607 double lineOffset = 0.0;
608 if ( jsonPaint.contains( QStringLiteral(
"line-offset" ) ) )
610 const QVariant jsonLineOffset = jsonPaint.value( QStringLiteral(
"line-offset" ) );
611 switch ( jsonLineOffset.userType() )
613 case QMetaType::Type::Int:
614 case QMetaType::Type::LongLong:
615 case QMetaType::Type::Double:
619 case QMetaType::Type::QVariantMap:
624 case QMetaType::Type::QVariantList:
625 case QMetaType::Type::QStringList:
630 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineOffset.userType() ) ) ) );
635 double lineOpacity = -1.0;
637 if ( jsonPaint.contains( QStringLiteral(
"line-opacity" ) ) )
639 const QVariant jsonLineOpacity = jsonPaint.
value( QStringLiteral(
"line-opacity" ) );
640 switch ( jsonLineOpacity.userType() )
642 case QMetaType::Type::Int:
643 case QMetaType::Type::LongLong:
644 case QMetaType::Type::Double:
645 lineOpacity = jsonLineOpacity.toDouble();
648 case QMetaType::Type::QVariantMap:
651 double defaultValue = 1.0;
660 case QMetaType::Type::QVariantList:
661 case QMetaType::Type::QStringList:
664 double defaultValue = 1.0;
675 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineOpacity.userType() ) ) ) );
680 QVector< double > dashVector;
681 if ( jsonPaint.contains( QStringLiteral(
"line-dasharray" ) ) )
683 const QVariant jsonLineDashArray = jsonPaint.value( QStringLiteral(
"line-dasharray" ) );
684 switch ( jsonLineDashArray.userType() )
686 case QMetaType::Type::QVariantMap:
688 QString arrayExpression;
691 arrayExpression = QStringLiteral(
"array_to_string(array_foreach(%1,@element * (%2)), ';')" )
692 .arg(
parseArrayStops( jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList(), context, 1 ),
697 arrayExpression = QStringLiteral(
"array_to_string(%1, ';')" ).arg(
parseArrayStops( jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList(), context, lineWidth ) );
701 const QVariantList dashSource = jsonLineDashArray.toMap().value( QStringLiteral(
"stops" ) ).toList().first().toList().value( 1 ).toList();
702 for (
const QVariant &v : dashSource )
704 dashVector << v.toDouble() * lineWidth;
709 case QMetaType::Type::QVariantList:
710 case QMetaType::Type::QStringList:
712 const QVariantList dashSource = jsonLineDashArray.toList();
714 if ( dashSource.at( 0 ).userType() == QMetaType::Type::QString )
720 .arg( property.asExpression(), lineWidthProperty.
asExpression() ) );
730 QVector< double > rawDashVectorSizes;
731 rawDashVectorSizes.reserve( dashSource.size() );
732 for (
const QVariant &v : dashSource )
734 rawDashVectorSizes << v.toDouble();
738 if ( rawDashVectorSizes.size() == 1 )
741 rawDashVectorSizes.clear();
743 else if ( rawDashVectorSizes.size() % 2 == 1 )
747 rawDashVectorSizes[0] = rawDashVectorSizes[0] + rawDashVectorSizes[rawDashVectorSizes.size() - 1];
748 rawDashVectorSizes.resize( rawDashVectorSizes.size() - 1 );
751 if ( !rawDashVectorSizes.isEmpty() && ( !lineWidthProperty.
asExpression().isEmpty() ) )
753 QStringList dashArrayStringParts;
754 dashArrayStringParts.reserve( rawDashVectorSizes.size() );
755 for (
double v : std::as_const( rawDashVectorSizes ) )
760 QString arrayExpression = QStringLiteral(
"array_to_string(array_foreach(array(%1),@element * (%2)), ';')" )
761 .arg( dashArrayStringParts.join(
',' ),
767 for (
double v : std::as_const( rawDashVectorSizes ) )
769 dashVector << v *lineWidth;
776 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported line-dasharray type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonLineDashArray.userType() ) ) ) );
781 Qt::PenCapStyle penCapStyle = Qt::FlatCap;
782 Qt::PenJoinStyle penJoinStyle = Qt::MiterJoin;
783 if ( jsonLayer.contains( QStringLiteral(
"layout" ) ) )
785 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
786 if ( jsonLayout.contains( QStringLiteral(
"line-cap" ) ) )
788 penCapStyle =
parseCapStyle( jsonLayout.value( QStringLiteral(
"line-cap" ) ).toString() );
790 if ( jsonLayout.contains( QStringLiteral(
"line-join" ) ) )
792 penJoinStyle =
parseJoinStyle( jsonLayout.value( QStringLiteral(
"line-join" ) ).toString() );
796 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsLineSymbol >() );
797 symbol->setOutputUnit( context.
targetUnit() );
799 if ( !rasterLineSprite.isEmpty() )
809 if ( lineOpacity != -1 )
811 symbol->setOpacity( lineOpacity );
817 symbol->setDataDefinedProperties( ddProperties );
819 if ( lineWidth != -1 )
823 symbol->changeSymbolLayer( 0, lineSymbol );
828 Q_ASSERT( lineSymbol );
838 if ( lineOpacity != -1 )
840 symbol->setOpacity( lineOpacity );
846 symbol->setDataDefinedProperties( ddProperties );
848 if ( lineColor.isValid() )
852 if ( lineWidth != -1 )
856 if ( !dashVector.empty() )
870 if ( !jsonLayer.contains( QStringLiteral(
"paint" ) ) )
872 context.
pushWarning( QObject::tr(
"%1: Style has no paint property, skipping" ).arg( context.
layerId() ) );
876 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
880 QColor circleFillColor;
881 if ( jsonPaint.contains( QStringLiteral(
"circle-color" ) ) )
883 const QVariant jsonCircleColor = jsonPaint.
value( QStringLiteral(
"circle-color" ) );
884 switch ( jsonCircleColor.userType() )
886 case QMetaType::Type::QVariantMap:
890 case QMetaType::Type::QVariantList:
891 case QMetaType::Type::QStringList:
895 case QMetaType::Type::QString:
896 circleFillColor =
parseColor( jsonCircleColor.toString(), context );
900 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleColor.userType() ) ) ) );
907 circleFillColor = QColor( 0, 0, 0 );
911 double circleDiameter = 10.0;
912 if ( jsonPaint.contains( QStringLiteral(
"circle-radius" ) ) )
914 const QVariant jsonCircleRadius = jsonPaint.value( QStringLiteral(
"circle-radius" ) );
915 switch ( jsonCircleRadius.userType() )
917 case QMetaType::Type::Int:
918 case QMetaType::Type::LongLong:
919 case QMetaType::Type::Double:
923 case QMetaType::Type::QVariantMap:
928 case QMetaType::Type::QVariantList:
929 case QMetaType::Type::QStringList:
934 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-radius type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleRadius.userType() ) ) ) );
939 double circleOpacity = -1.0;
940 if ( jsonPaint.contains( QStringLiteral(
"circle-opacity" ) ) )
942 const QVariant jsonCircleOpacity = jsonPaint.value( QStringLiteral(
"circle-opacity" ) );
943 switch ( jsonCircleOpacity.userType() )
945 case QMetaType::Type::Int:
946 case QMetaType::Type::LongLong:
947 case QMetaType::Type::Double:
948 circleOpacity = jsonCircleOpacity.toDouble();
951 case QMetaType::Type::QVariantMap:
955 case QMetaType::Type::QVariantList:
956 case QMetaType::Type::QStringList:
961 context.pushWarning( QObject::tr(
"%1: Skipping unsupported circle-opacity type (%2)" ).arg( context.layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleOpacity.userType() ) ) ) );
965 if ( ( circleOpacity != -1 ) && circleFillColor.isValid() )
967 circleFillColor.setAlphaF( circleOpacity );
971 QColor circleStrokeColor;
972 if ( jsonPaint.contains( QStringLiteral(
"circle-stroke-color" ) ) )
974 const QVariant jsonCircleStrokeColor = jsonPaint.value( QStringLiteral(
"circle-stroke-color" ) );
975 switch ( jsonCircleStrokeColor.userType() )
977 case QMetaType::Type::QVariantMap:
981 case QMetaType::Type::QVariantList:
982 case QMetaType::Type::QStringList:
986 case QMetaType::Type::QString:
987 circleStrokeColor =
parseColor( jsonCircleStrokeColor.toString(), context );
991 context.pushWarning( QObject::tr(
"%1: Skipping unsupported circle-stroke-color type (%2)" ).arg( context.layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleStrokeColor.userType() ) ) ) );
997 double circleStrokeWidth = -1.0;
998 if ( jsonPaint.contains( QStringLiteral(
"circle-stroke-width" ) ) )
1000 const QVariant circleStrokeWidthJson = jsonPaint.value( QStringLiteral(
"circle-stroke-width" ) );
1001 switch ( circleStrokeWidthJson.userType() )
1003 case QMetaType::Type::Int:
1004 case QMetaType::Type::LongLong:
1005 case QMetaType::Type::Double:
1009 case QMetaType::Type::QVariantMap:
1010 circleStrokeWidth = -1.0;
1014 case QMetaType::Type::QVariantList:
1015 case QMetaType::Type::QStringList:
1020 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-stroke-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( circleStrokeWidthJson.userType() ) ) ) );
1025 double circleStrokeOpacity = -1.0;
1026 if ( jsonPaint.contains( QStringLiteral(
"circle-stroke-opacity" ) ) )
1028 const QVariant jsonCircleStrokeOpacity = jsonPaint.value( QStringLiteral(
"circle-stroke-opacity" ) );
1029 switch ( jsonCircleStrokeOpacity.userType() )
1031 case QMetaType::Type::Int:
1032 case QMetaType::Type::LongLong:
1033 case QMetaType::Type::Double:
1034 circleStrokeOpacity = jsonCircleStrokeOpacity.toDouble();
1037 case QMetaType::Type::QVariantMap:
1041 case QMetaType::Type::QVariantList:
1042 case QMetaType::Type::QStringList:
1047 context.pushWarning( QObject::tr(
"%1: Skipping unsupported circle-stroke-opacity type (%2)" ).arg( context.layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleStrokeOpacity.userType() ) ) ) );
1051 if ( ( circleStrokeOpacity != -1 ) && circleStrokeColor.isValid() )
1053 circleStrokeColor.setAlphaF( circleStrokeOpacity );
1057 QPointF circleTranslate;
1058 if ( jsonPaint.contains( QStringLiteral(
"circle-translate" ) ) )
1060 const QVariant jsonCircleTranslate = jsonPaint.value( QStringLiteral(
"circle-translate" ) );
1061 switch ( jsonCircleTranslate.userType() )
1064 case QMetaType::Type::QVariantMap:
1068 case QMetaType::Type::QVariantList:
1069 case QMetaType::Type::QStringList:
1075 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported circle-translate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonCircleTranslate.userType() ) ) ) );
1080 std::unique_ptr< QgsSymbol > symbol( std::make_unique< QgsMarkerSymbol >() );
1082 Q_ASSERT( markerSymbolLayer );
1085 symbol->setOutputUnit( context.
targetUnit() );
1086 symbol->setDataDefinedProperties( ddProperties );
1088 if ( !circleTranslate.isNull() )
1090 markerSymbolLayer->
setOffset( circleTranslate );
1094 if ( circleFillColor.isValid() )
1098 if ( circleDiameter != -1 )
1100 markerSymbolLayer->
setSize( circleDiameter );
1103 if ( circleStrokeColor.isValid() )
1107 if ( circleStrokeWidth != -1 )
1120 hasLabeling =
false;
1121 hasRenderer =
false;
1123 if ( !jsonLayer.contains( QStringLiteral(
"layout" ) ) )
1125 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
1128 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
1129 if ( !jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
1135 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
1141 if ( jsonLayout.contains( QStringLiteral(
"text-size" ) ) )
1143 const QVariant jsonTextSize = jsonLayout.
value( QStringLiteral(
"text-size" ) );
1144 switch ( jsonTextSize.userType() )
1146 case QMetaType::Type::Int:
1147 case QMetaType::Type::LongLong:
1148 case QMetaType::Type::Double:
1152 case QMetaType::Type::QVariantMap:
1158 case QMetaType::Type::QVariantList:
1159 case QMetaType::Type::QStringList:
1165 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextSize.userType() ) ) ) );
1169 if ( textSizeProperty )
1176 constexpr double EM_TO_CHARS = 2.0;
1178 double textMaxWidth = -1;
1179 if ( jsonLayout.contains( QStringLiteral(
"text-max-width" ) ) )
1181 const QVariant jsonTextMaxWidth = jsonLayout.value( QStringLiteral(
"text-max-width" ) );
1182 switch ( jsonTextMaxWidth.userType() )
1184 case QMetaType::Type::Int:
1185 case QMetaType::Type::LongLong:
1186 case QMetaType::Type::Double:
1187 textMaxWidth = jsonTextMaxWidth.toDouble() * EM_TO_CHARS;
1190 case QMetaType::Type::QVariantMap:
1194 case QMetaType::Type::QVariantList:
1195 case QMetaType::Type::QStringList:
1200 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-max-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextMaxWidth.userType() ) ) ) );
1207 textMaxWidth = 10 * EM_TO_CHARS;
1210 double textLetterSpacing = -1;
1211 if ( jsonLayout.contains( QStringLiteral(
"text-letter-spacing" ) ) )
1213 const QVariant jsonTextLetterSpacing = jsonLayout.value( QStringLiteral(
"text-letter-spacing" ) );
1214 switch ( jsonTextLetterSpacing.userType() )
1216 case QMetaType::Type::Int:
1217 case QMetaType::Type::LongLong:
1218 case QMetaType::Type::Double:
1219 textLetterSpacing = jsonTextLetterSpacing.toDouble();
1222 case QMetaType::Type::QVariantMap:
1226 case QMetaType::Type::QVariantList:
1227 case QMetaType::Type::QStringList:
1232 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-letter-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextLetterSpacing.userType() ) ) ) );
1238 bool foundFont =
false;
1240 QString fontStyleName;
1242 if ( jsonLayout.contains( QStringLiteral(
"text-font" ) ) )
1244 auto splitFontFamily = [](
const QString & fontName, QString & family, QString & style ) ->
bool
1246 QString matchedFamily;
1247 const QStringList textFontParts = fontName.split(
' ' );
1248 for (
int i = textFontParts.size() - 1; i >= 1; --i )
1250 const QString candidateFontFamily = textFontParts.mid( 0, i ).join(
' ' );
1251 const QString candidateFontStyle = textFontParts.mid( i ).join(
' ' );
1256 family = processedFontFamily;
1257 style = candidateFontStyle;
1262 if ( processedFontFamily == matchedFamily )
1264 family = processedFontFamily;
1265 style = candidateFontStyle;
1269 family = matchedFamily;
1270 style = processedFontFamily;
1271 style.replace( matchedFamily, QString() );
1272 style = style.trimmed();
1273 if ( !style.isEmpty() && !candidateFontStyle.isEmpty() )
1275 style += QStringLiteral(
" %1" ).arg( candidateFontStyle );
1283 if ( QFontDatabase().hasFamily( processedFontFamily ) )
1286 family = processedFontFamily;
1292 family = matchedFamily;
1299 const QVariant jsonTextFont = jsonLayout.value( QStringLiteral(
"text-font" ) );
1300 if ( jsonTextFont.userType() != QMetaType::Type::QVariantList && jsonTextFont.userType() != QMetaType::Type::QStringList && jsonTextFont.userType() != QMetaType::Type::QString
1301 && jsonTextFont.userType() != QMetaType::Type::QVariantMap )
1303 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-font type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextFont.userType() ) ) ) );
1307 switch ( jsonTextFont.userType() )
1309 case QMetaType::Type::QVariantList:
1310 case QMetaType::Type::QStringList:
1311 fontName = jsonTextFont.toList().value( 0 ).toString();
1314 case QMetaType::Type::QString:
1315 fontName = jsonTextFont.toString();
1318 case QMetaType::Type::QVariantMap:
1320 QString familyCaseString = QStringLiteral(
"CASE " );
1321 QString styleCaseString = QStringLiteral(
"CASE " );
1323 const QVariantList stops = jsonTextFont.toMap().value( QStringLiteral(
"stops" ) ).toList();
1326 for (
int i = 0; i < stops.length() - 1; ++i )
1329 const QVariant bz = stops.value( i ).toList().value( 0 );
1330 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();
1331 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
1333 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1339 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
1340 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
1342 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
1347 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1349 familyCaseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1350 "THEN %3 " ).arg( bz.toString(),
1353 styleCaseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1354 "THEN %3 " ).arg( bz.toString(),
1360 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1366 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();
1367 if ( splitFontFamily( bv, fontFamily, fontStyleName ) )
1374 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), bv ) );
1381 fontName = fontFamily;
1391 if ( splitFontFamily( fontName, fontFamily, fontStyleName ) )
1394 if ( !fontStyleName.isEmpty() )
1395 textFont.setStyleName( fontStyleName );
1405 fontName = QStringLiteral(
"Open Sans" );
1407 textFont.setStyleName( QStringLiteral(
"Regular" ) );
1408 fontStyleName = QStringLiteral(
"Regular" );
1413 fontName = QStringLiteral(
"Arial Unicode MS" );
1415 textFont.setStyleName( QStringLiteral(
"Regular" ) );
1416 fontStyleName = QStringLiteral(
"Regular" );
1421 fontName = QStringLiteral(
"Open Sans, Arial Unicode MS" );
1424 if ( !foundFont && !fontName.isEmpty() )
1426 context.
pushWarning( QObject::tr(
"%1: Referenced font %2 is not available on system" ).arg( context.
layerId(), fontName ) );
1431 if ( jsonPaint.contains( QStringLiteral(
"text-color" ) ) )
1433 const QVariant jsonTextColor = jsonPaint.value( QStringLiteral(
"text-color" ) );
1434 switch ( jsonTextColor.userType() )
1436 case QMetaType::Type::QVariantMap:
1440 case QMetaType::Type::QVariantList:
1441 case QMetaType::Type::QStringList:
1445 case QMetaType::Type::QString:
1446 textColor =
parseColor( jsonTextColor.toString(), context );
1450 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextColor.userType() ) ) ) );
1457 textColor = QColor( 0, 0, 0 );
1461 QColor bufferColor( 0, 0, 0, 0 );
1462 if ( jsonPaint.contains( QStringLiteral(
"text-halo-color" ) ) )
1464 const QVariant jsonBufferColor = jsonPaint.value( QStringLiteral(
"text-halo-color" ) );
1465 switch ( jsonBufferColor.userType() )
1467 case QMetaType::Type::QVariantMap:
1471 case QMetaType::Type::QVariantList:
1472 case QMetaType::Type::QStringList:
1476 case QMetaType::Type::QString:
1477 bufferColor =
parseColor( jsonBufferColor.toString(), context );
1481 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-halo-color type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonBufferColor.userType() ) ) ) );
1486 double bufferSize = 0.0;
1490 constexpr double BUFFER_SIZE_SCALE = 2.0;
1491 if ( jsonPaint.contains( QStringLiteral(
"text-halo-width" ) ) )
1493 const QVariant jsonHaloWidth = jsonPaint.value( QStringLiteral(
"text-halo-width" ) );
1494 QString bufferSizeDataDefined;
1495 switch ( jsonHaloWidth.userType() )
1497 case QMetaType::Type::Int:
1498 case QMetaType::Type::LongLong:
1499 case QMetaType::Type::Double:
1503 case QMetaType::Type::QVariantMap:
1508 case QMetaType::Type::QVariantList:
1509 case QMetaType::Type::QStringList:
1515 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-halo-width type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonHaloWidth.userType() ) ) ) );
1521 if ( bufferSize > 0 )
1523 if ( textSize > 0 && bufferSizeDataDefined.isEmpty() )
1525 bufferSize = std::min( bufferSize, textSize * BUFFER_SIZE_SCALE / 4 );
1527 else if ( textSize > 0 && !bufferSizeDataDefined.isEmpty() )
1529 bufferSizeDataDefined = QStringLiteral(
"min(%1/4, %2)" ).arg( textSize * BUFFER_SIZE_SCALE ).arg( bufferSizeDataDefined );
1532 else if ( !bufferSizeDataDefined.isEmpty() )
1534 bufferSizeDataDefined = QStringLiteral(
"min(%1*%2/4, %3)" )
1536 .arg( BUFFER_SIZE_SCALE )
1537 .arg( bufferSizeDataDefined );
1540 else if ( bufferSizeDataDefined.isEmpty() )
1542 bufferSizeDataDefined = QStringLiteral(
"min(%1*%2/4, %3)" )
1544 .arg( BUFFER_SIZE_SCALE )
1551 double haloBlurSize = 0;
1552 if ( jsonPaint.contains( QStringLiteral(
"text-halo-blur" ) ) )
1554 const QVariant jsonTextHaloBlur = jsonPaint.value( QStringLiteral(
"text-halo-blur" ) );
1555 switch ( jsonTextHaloBlur.userType() )
1557 case QMetaType::Type::Int:
1558 case QMetaType::Type::LongLong:
1559 case QMetaType::Type::Double:
1566 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-halo-blur type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextHaloBlur.userType() ) ) ) );
1573 if ( textColor.isValid() )
1575 if ( textSize >= 0 )
1580 if ( !fontStyleName.isEmpty() )
1583 if ( textLetterSpacing > 0 )
1585 QFont f = format.
font();
1586 f.setLetterSpacing( QFont::AbsoluteSpacing, textLetterSpacing );
1590 if ( bufferSize > 0 )
1593 const double opacity = bufferColor.alphaF();
1594 bufferColor.setAlphaF( 1.0 );
1602 if ( haloBlurSize > 0 )
1618 if ( textMaxWidth > 0 )
1624 if ( jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
1626 const QVariant jsonTextField = jsonLayout.value( QStringLiteral(
"text-field" ) );
1627 switch ( jsonTextField.userType() )
1629 case QMetaType::Type::QString:
1631 labelSettings.
fieldName = processLabelField( jsonTextField.toString(), labelSettings.
isExpression );
1635 case QMetaType::Type::QVariantList:
1636 case QMetaType::Type::QStringList:
1638 const QVariantList textFieldList = jsonTextField.toList();
1646 if ( textFieldList.size() > 2 && textFieldList.at( 0 ).toString() == QLatin1String(
"format" ) )
1649 for (
int i = 1; i < textFieldList.size(); ++i )
1651 bool isExpression =
false;
1652 const QString part = processLabelField( textFieldList.at( i ).toString(), isExpression );
1653 if ( !isExpression )
1660 labelSettings.
fieldName = QStringLiteral(
"concat(%1)" ).arg( parts.join(
',' ) );
1675 case QMetaType::Type::QVariantMap:
1677 const QVariantList stops = jsonTextField.toMap().value( QStringLiteral(
"stops" ) ).toList();
1678 if ( !stops.empty() )
1685 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-field dictionary" ).arg( context.
layerId() ) );
1691 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-field type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextField.userType() ) ) ) );
1696 if ( jsonLayout.contains( QStringLiteral(
"text-rotate" ) ) )
1698 const QVariant jsonTextRotate = jsonLayout.value( QStringLiteral(
"text-rotate" ) );
1699 switch ( jsonTextRotate.userType() )
1701 case QMetaType::Type::Double:
1702 case QMetaType::Type::Int:
1704 labelSettings.
angleOffset = jsonTextRotate.toDouble();
1708 case QMetaType::Type::QVariantList:
1709 case QMetaType::Type::QStringList:
1716 case QMetaType::Type::QVariantMap:
1718 QVariantMap rotateMap = jsonTextRotate.toMap();
1719 if ( rotateMap.contains( QStringLiteral(
"property" ) ) && rotateMap[QStringLiteral(
"type" )].toString() == QStringLiteral(
"identity" ) )
1725 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-rotate map content (%2)" ).arg( context.
layerId(), QString( QJsonDocument::fromVariant( rotateMap ).toJson() ) ) );
1730 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextRotate.userType() ) ) ) );
1735 if ( jsonLayout.contains( QStringLiteral(
"text-transform" ) ) )
1737 const QString textTransform = jsonLayout.value( QStringLiteral(
"text-transform" ) ).toString();
1738 if ( textTransform == QLatin1String(
"uppercase" ) )
1742 else if ( textTransform == QLatin1String(
"lowercase" ) )
1751 if ( jsonLayout.contains( QStringLiteral(
"symbol-placement" ) ) )
1753 const QString symbolPlacement = jsonLayout.value( QStringLiteral(
"symbol-placement" ) ).toString();
1754 if ( symbolPlacement == QLatin1String(
"line" ) )
1760 if ( jsonLayout.contains( QStringLiteral(
"text-rotation-alignment" ) ) )
1762 const QString textRotationAlignment = jsonLayout.value( QStringLiteral(
"text-rotation-alignment" ) ).toString();
1763 if ( textRotationAlignment == QLatin1String(
"viewport" ) )
1773 if ( jsonLayout.contains( QStringLiteral(
"text-offset" ) ) )
1775 const QVariant jsonTextOffset = jsonLayout.
value( QStringLiteral(
"text-offset" ) );
1778 switch ( jsonTextOffset.userType() )
1780 case QMetaType::Type::QVariantMap:
1781 textOffsetProperty =
parseInterpolatePointByZoom( jsonTextOffset.toMap(), context, !textSizeProperty ? textSize : 1.0, &textOffset );
1782 if ( !textSizeProperty )
1793 case QMetaType::Type::QVariantList:
1794 case QMetaType::Type::QStringList:
1795 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1796 jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1800 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextOffset.userType() ) ) ) );
1804 if ( !textOffset.isNull() )
1807 labelSettings.
dist = std::abs( textOffset.y() ) - textSize;
1809 if ( textSizeProperty && !textOffsetProperty )
1816 if ( textOffset.isNull() )
1824 if ( jsonLayout.contains( QStringLiteral(
"text-justify" ) ) )
1826 const QVariant jsonTextJustify = jsonLayout.value( QStringLiteral(
"text-justify" ) );
1829 QString textAlign = QStringLiteral(
"center" );
1831 const QVariantMap conversionMap
1833 { QStringLiteral(
"left" ), QStringLiteral(
"left" ) },
1834 { QStringLiteral(
"center" ), QStringLiteral(
"center" ) },
1835 { QStringLiteral(
"right" ), QStringLiteral(
"right" ) },
1836 { QStringLiteral(
"auto" ), QStringLiteral(
"follow" ) }
1839 switch ( jsonTextJustify.userType() )
1841 case QMetaType::Type::QString:
1842 textAlign = jsonTextJustify.toString();
1845 case QMetaType::Type::QVariantList:
1849 case QMetaType::Type::QVariantMap:
1854 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-justify type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextJustify.userType() ) ) ) );
1858 if ( textAlign == QLatin1String(
"left" ) )
1860 else if ( textAlign == QLatin1String(
"right" ) )
1862 else if ( textAlign == QLatin1String(
"center" ) )
1864 else if ( textAlign == QLatin1String(
"follow" ) )
1874 if ( jsonLayout.contains( QStringLiteral(
"text-anchor" ) ) )
1876 const QVariant jsonTextAnchor = jsonLayout.value( QStringLiteral(
"text-anchor" ) );
1879 const QVariantMap conversionMap
1881 { QStringLiteral(
"center" ), 4 },
1882 { QStringLiteral(
"left" ), 5 },
1883 { QStringLiteral(
"right" ), 3 },
1884 { QStringLiteral(
"top" ), 7 },
1885 { QStringLiteral(
"bottom" ), 1 },
1886 { QStringLiteral(
"top-left" ), 8 },
1887 { QStringLiteral(
"top-right" ), 6 },
1888 { QStringLiteral(
"bottom-left" ), 2 },
1889 { QStringLiteral(
"bottom-right" ), 0 },
1892 switch ( jsonTextAnchor.userType() )
1894 case QMetaType::Type::QString:
1895 textAnchor = jsonTextAnchor.toString();
1898 case QMetaType::Type::QVariantList:
1902 case QMetaType::Type::QVariantMap:
1907 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-anchor type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextAnchor.userType() ) ) ) );
1911 if ( textAnchor == QLatin1String(
"center" ) )
1913 else if ( textAnchor == QLatin1String(
"left" ) )
1915 else if ( textAnchor == QLatin1String(
"right" ) )
1917 else if ( textAnchor == QLatin1String(
"top" ) )
1919 else if ( textAnchor == QLatin1String(
"bottom" ) )
1921 else if ( textAnchor == QLatin1String(
"top-left" ) )
1923 else if ( textAnchor == QLatin1String(
"top-right" ) )
1925 else if ( textAnchor == QLatin1String(
"bottom-left" ) )
1927 else if ( textAnchor == QLatin1String(
"bottom-right" ) )
1932 if ( jsonLayout.contains( QStringLiteral(
"text-offset" ) ) )
1934 const QVariant jsonTextOffset = jsonLayout.value( QStringLiteral(
"text-offset" ) );
1937 switch ( jsonTextOffset.userType() )
1939 case QMetaType::Type::QVariantMap:
1943 case QMetaType::Type::QVariantList:
1944 case QMetaType::Type::QStringList:
1945 textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1946 jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1950 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported text-offset type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonTextOffset.userType() ) ) ) );
1954 if ( !textOffset.isNull() )
1957 labelSettings.
xOffset = textOffset.x();
1958 labelSettings.
yOffset = textOffset.y();
1963 if ( jsonLayout.contains( QStringLiteral(
"icon-image" ) ) &&
1967 QString spriteProperty, spriteSizeProperty;
1969 if ( !sprite.isEmpty() )
1972 if ( jsonLayout.contains( QStringLiteral(
"icon-size" ) ) )
1975 const QVariant jsonIconSize = jsonLayout.
value( QStringLiteral(
"icon-size" ) );
1976 switch ( jsonIconSize.userType() )
1978 case QMetaType::Type::Int:
1979 case QMetaType::Type::LongLong:
1980 case QMetaType::Type::Double:
1982 size = jsonIconSize.toDouble();
1983 if ( !spriteSizeProperty.isEmpty() )
1986 QgsProperty::fromExpression( QStringLiteral(
"with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
1991 case QMetaType::Type::QVariantMap:
1995 case QMetaType::Type::QVariantList:
1996 case QMetaType::Type::QStringList:
2000 context.
pushWarning( QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) ) );
2006 if ( !spriteSizeProperty.isEmpty() )
2020 markerLayer->
setPath( sprite );
2021 markerLayer->
setSize( spriteSize.width() );
2024 if ( !spriteProperty.isEmpty() )
2034 backgroundSettings.
setSize( spriteSize * size );
2042 if ( textSize >= 0 )
2065 if ( !jsonLayer.contains( QStringLiteral(
"layout" ) ) )
2067 context.
pushWarning( QObject::tr(
"%1: Style layer has no layout property, skipping" ).arg( context.
layerId() ) );
2070 const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral(
"layout" ) ).toMap();
2072 if ( jsonLayout.value( QStringLiteral(
"symbol-placement" ) ).toString() == QLatin1String(
"line" ) && !jsonLayout.contains( QStringLiteral(
"text-field" ) ) )
2076 double spacing = -1.0;
2077 if ( jsonLayout.contains( QStringLiteral(
"symbol-spacing" ) ) )
2079 const QVariant jsonSpacing = jsonLayout.
value( QStringLiteral(
"symbol-spacing" ) );
2080 switch ( jsonSpacing.userType() )
2082 case QMetaType::Type::Int:
2083 case QMetaType::Type::LongLong:
2084 case QMetaType::Type::Double:
2088 case QMetaType::Type::QVariantMap:
2092 case QMetaType::Type::QVariantList:
2093 case QMetaType::Type::QStringList:
2098 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported symbol-spacing type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonSpacing.userType() ) ) ) );
2108 bool rotateMarkers =
true;
2109 if ( jsonLayout.contains( QStringLiteral(
"icon-rotation-alignment" ) ) )
2111 const QString alignment = jsonLayout.value( QStringLiteral(
"icon-rotation-alignment" ) ).toString();
2112 if ( alignment == QLatin1String(
"map" ) || alignment == QLatin1String(
"auto" ) )
2114 rotateMarkers =
true;
2116 else if ( alignment == QLatin1String(
"viewport" ) )
2118 rotateMarkers =
false;
2123 double rotation = 0.0;
2124 if ( jsonLayout.contains( QStringLiteral(
"icon-rotate" ) ) )
2126 const QVariant jsonIconRotate = jsonLayout.
value( QStringLiteral(
"icon-rotate" ) );
2127 switch ( jsonIconRotate.userType() )
2129 case QMetaType::Type::Int:
2130 case QMetaType::Type::LongLong:
2131 case QMetaType::Type::Double:
2132 rotation = jsonIconRotate.toDouble();
2135 case QMetaType::Type::QVariantMap:
2139 case QMetaType::Type::QVariantList:
2140 case QMetaType::Type::QStringList:
2145 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconRotate.userType() ) ) ) );
2161 QString spriteProperty, spriteSizeProperty;
2163 if ( !sprite.isNull() )
2165 markerLayer->
setPath( sprite );
2166 markerLayer->
setSize( spriteSize.width() );
2169 if ( !spriteProperty.isEmpty() )
2176 if ( jsonLayout.contains( QStringLiteral(
"icon-size" ) ) )
2178 const QVariant jsonIconSize = jsonLayout.value( QStringLiteral(
"icon-size" ) );
2181 switch ( jsonIconSize.userType() )
2183 case QMetaType::Type::Int:
2184 case QMetaType::Type::LongLong:
2185 case QMetaType::Type::Double:
2187 size = jsonIconSize.toDouble();
2188 if ( !spriteSizeProperty.isEmpty() )
2191 QgsProperty::fromExpression( QStringLiteral(
"with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
2196 case QMetaType::Type::QVariantMap:
2200 case QMetaType::Type::QVariantList:
2201 case QMetaType::Type::QStringList:
2205 context.
pushWarning( QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) ) );
2208 markerLayer->
setSize( size * spriteSize.width() );
2211 if ( !spriteSizeProperty.isEmpty() )
2228 std::unique_ptr< QgsSymbol > symbol = std::make_unique< QgsLineSymbol >(
QgsSymbolLayerList() << lineSymbol );
2231 symbol->setOutputUnit( context.
targetUnit() );
2235 rendererStyle.
setSymbol( symbol.release() );
2238 else if ( jsonLayout.contains( QStringLiteral(
"icon-image" ) ) )
2240 const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral(
"paint" ) ).toMap();
2243 QString spriteProperty, spriteSizeProperty;
2245 if ( !sprite.isEmpty() || !spriteProperty.isEmpty() )
2248 rasterMarker->
setPath( sprite );
2249 rasterMarker->
setSize( spriteSize.width() );
2253 if ( !spriteProperty.isEmpty() )
2259 if ( jsonLayout.contains( QStringLiteral(
"icon-size" ) ) )
2261 const QVariant jsonIconSize = jsonLayout.value( QStringLiteral(
"icon-size" ) );
2264 switch ( jsonIconSize.userType() )
2266 case QMetaType::Type::Int:
2267 case QMetaType::Type::LongLong:
2268 case QMetaType::Type::Double:
2270 size = jsonIconSize.toDouble();
2271 if ( !spriteSizeProperty.isEmpty() )
2274 QgsProperty::fromExpression( QStringLiteral(
"with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
2279 case QMetaType::Type::QVariantMap:
2283 case QMetaType::Type::QVariantList:
2284 case QMetaType::Type::QStringList:
2288 context.
pushWarning( QObject::tr(
"%1: Skipping non-implemented icon-size type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconSize.userType() ) ) ) );
2291 rasterMarker->
setSize( size * spriteSize.width() );
2294 if ( !spriteSizeProperty.isEmpty() )
2307 double rotation = 0.0;
2308 if ( jsonLayout.contains( QStringLiteral(
"icon-rotate" ) ) )
2310 const QVariant jsonIconRotate = jsonLayout.value( QStringLiteral(
"icon-rotate" ) );
2311 switch ( jsonIconRotate.userType() )
2313 case QMetaType::Type::Int:
2314 case QMetaType::Type::LongLong:
2315 case QMetaType::Type::Double:
2316 rotation = jsonIconRotate.toDouble();
2319 case QMetaType::Type::QVariantMap:
2323 case QMetaType::Type::QVariantList:
2324 case QMetaType::Type::QStringList:
2329 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconRotate.userType() ) ) ) );
2334 double iconOpacity = -1.0;
2335 if ( jsonPaint.contains( QStringLiteral(
"icon-opacity" ) ) )
2337 const QVariant jsonIconOpacity = jsonPaint.value( QStringLiteral(
"icon-opacity" ) );
2338 switch ( jsonIconOpacity.userType() )
2340 case QMetaType::Type::Int:
2341 case QMetaType::Type::LongLong:
2342 case QMetaType::Type::Double:
2343 iconOpacity = jsonIconOpacity.toDouble();
2346 case QMetaType::Type::QVariantMap:
2350 case QMetaType::Type::QVariantList:
2351 case QMetaType::Type::QStringList:
2356 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported icon-opacity type (%2)" ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( jsonIconOpacity.userType() ) ) ) );
2362 rasterMarker->
setAngle( rotation );
2363 if ( iconOpacity >= 0 )
2367 rendererStyle.
setSymbol( markerSymbol );
2378 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2379 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2380 if ( stops.empty() )
2383 QString caseString = QStringLiteral(
"CASE " );
2384 const QString colorComponent(
"color_part(%1,'%2')" );
2386 for (
int i = 0; i < stops.length() - 1; ++i )
2389 const QString bz = stops.at( i ).toList().value( 0 ).toString();
2391 const QString tz = stops.at( i + 1 ).toList().value( 0 ).toString();
2393 const QVariant bcVariant = stops.at( i ).toList().value( 1 );
2394 const QVariant tcVariant = stops.at( i + 1 ).toList().value( 1 );
2396 const QColor bottomColor =
parseColor( bcVariant.toString(), context );
2397 const QColor topColor =
parseColor( tcVariant.toString(), context );
2399 if ( i == 0 && bottomColor.isValid() )
2406 caseString += QStringLiteral(
"WHEN @vector_tile_zoom < %1 THEN color_hsla(%2, %3, %4, %5) " )
2407 .arg( bz ).arg( bcHue ).arg( bcSat ).arg( bcLight ).arg( bcAlpha );
2410 if ( bottomColor.isValid() && topColor.isValid() )
2422 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2423 "%3, %4, %5, %6) " ).arg( bz, tz,
2434 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
2435 "%3, %4, %5, %6) " ).arg( bz, tz,
2436 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"hsl_hue" ), colorComponent.arg( topColorExpr ).arg(
"hsl_hue" ), base, 1, &context ),
2437 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"hsl_saturation" ), colorComponent.arg( topColorExpr ).arg(
"hsl_saturation" ), base, 1, &context ),
2438 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"lightness" ), colorComponent.arg( topColorExpr ).arg(
"lightness" ), base, 1, &context ),
2439 interpolateExpression( bz.toDouble(), tz.toDouble(), colorComponent.arg( bottomColorExpr ).arg(
"alpha" ), colorComponent.arg( topColorExpr ).arg(
"alpha" ), base, 1, &context ) );
2444 const QString tz = stops.last().toList().value( 0 ).toString();
2445 const QVariant tcVariant = stops.last().toList().value( 1 );
2447 if ( tcVariant.userType() == QMetaType::Type::QString )
2450 if ( topColor.isValid() )
2457 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2458 "ELSE color_hsla(%2, %3, %4, %5) END" ).arg( tz ).arg( tcHue ).arg( tcSat ).arg( tcLight ).arg( tcAlpha );
2461 else if ( tcVariant.userType() == QMetaType::QVariantList )
2465 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
2466 "ELSE color_hsla(%2, %3, %4, %5) END" ).arg( tz )
2467 .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" ) );
2470 if ( !stops.empty() && defaultColor )
2471 *defaultColor =
parseColor( stops.value( 0 ).toList().value( 1 ).toString(), context );
2478 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2479 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2480 if ( stops.empty() )
2483 QString scaleExpression;
2484 if ( stops.size() <= 2 )
2487 stops.value( 0 ).toList().value( 0 ).toDouble(),
2488 stops.last().toList().value( 0 ).toDouble(),
2489 stops.value( 0 ).toList().value( 1 ),
2490 stops.last().toList().value( 1 ),
2491 base, multiplier, &context );
2495 scaleExpression =
parseStops( base, stops, multiplier, context );
2498 if ( !stops.empty() && defaultNumber )
2499 *defaultNumber = stops.value( 0 ).toList().value( 1 ).toDouble() * multiplier;
2509 context = *contextPtr;
2511 const double base = json.value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2512 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2513 if ( stops.empty() )
2516 QString scaleExpression;
2517 if ( stops.length() <= 2 )
2519 const QVariant bv = stops.value( 0 ).toList().value( 1 );
2520 const QVariant tv = stops.last().toList().value( 1 );
2521 double bottom = 0.0;
2523 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2524 scaleExpression = QStringLiteral(
"set_color_part(@symbol_color, 'alpha', %1)" )
2526 stops.value( 0 ).toList().value( 0 ).toDouble(),
2527 stops.last().toList().value( 0 ).toDouble(),
2528 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2529 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ), base, 1, &context ) );
2540 QString caseString = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN set_color_part(@symbol_color, 'alpha', %2)" )
2541 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
2542 .arg( stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity );
2544 for (
int i = 0; i < stops.size() - 1; ++i )
2546 const QVariant bv = stops.value( i ).toList().value( 1 );
2547 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2548 double bottom = 0.0;
2550 const bool numeric = numericArgumentsOnly( bv, tv, bottom, top );
2552 caseString += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
2553 "THEN set_color_part(@symbol_color, 'alpha', %3)" )
2554 .arg( stops.value( i ).toList().value( 0 ).toString(),
2555 stops.value( i + 1 ).toList().value( 0 ).toString(),
2557 stops.value( i ).toList().value( 0 ).toDouble(),
2558 stops.value( i + 1 ).toList().value( 0 ).toDouble(),
2559 numeric ? QString::number( bottom * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( bv, context ) ).arg( maxOpacity ),
2560 numeric ? QString::number( top * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( tv, context ) ).arg( maxOpacity ),
2561 base, 1, &context ) );
2565 bool numeric =
false;
2566 const QVariant vv = stops.last().toList().value( 1 );
2567 double dv = vv.toDouble( &numeric );
2569 caseString += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
2570 "THEN set_color_part(@symbol_color, 'alpha', %2) END" ).arg(
2571 stops.last().toList().value( 0 ).toString(),
2572 numeric ? QString::number( dv * maxOpacity ) : QString(
"(%1) * %2" ).arg( parseValue( vv, context ) ).arg( maxOpacity )
2579 const double base = json.
value( QStringLiteral(
"base" ), QStringLiteral(
"1" ) ).toDouble();
2580 const QVariantList stops = json.value( QStringLiteral(
"stops" ) ).toList();
2581 if ( stops.empty() )
2584 QString scaleExpression;
2585 if ( stops.size() <= 2 )
2587 scaleExpression = QStringLiteral(
"array(%1,%2)" ).arg(
interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(),
2588 stops.last().toList().value( 0 ).toDouble(),
2589 stops.value( 0 ).toList().value( 1 ).toList().value( 0 ),
2590 stops.last().toList().value( 1 ).toList().value( 0 ), base, multiplier, &context ),
2592 stops.last().toList().value( 0 ).toDouble(),
2593 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ),
2594 stops.last().toList().value( 1 ).toList().value( 1 ), base, multiplier, &context )
2599 scaleExpression =
parsePointStops( base, stops, context, multiplier );
2602 if ( !stops.empty() && defaultPoint )
2603 *defaultPoint = QPointF( stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble() * multiplier,
2604 stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble() * multiplier );
2610 const QVariantMap &conversionMap, QString *defaultString )
2612 const QVariantList stops = json.
value( QStringLiteral(
"stops" ) ).toList();
2613 if ( stops.empty() )
2616 const QString scaleExpression =
parseStringStops( stops, context, conversionMap, defaultString );
2623 QString caseString = QStringLiteral(
"CASE " );
2625 for (
int i = 0; i < stops.length() - 1; ++i )
2628 const QVariant bz = stops.value( i ).toList().value( 0 );
2629 const QVariant bv = stops.value( i ).toList().value( 1 );
2630 if ( bv.userType() != QMetaType::Type::QVariantList && bv.userType() != QMetaType::Type::QStringList )
2632 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( bz.userType() ) ) ) );
2637 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2638 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2639 if ( tv.userType() != QMetaType::Type::QVariantList && tv.userType() != QMetaType::Type::QStringList )
2641 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( tz.userType() ) ) ) );
2645 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2646 "THEN array(%3,%4)" ).arg( bz.toString(),
2648 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 0 ), tv.toList().value( 0 ), base, multiplier, &context ),
2649 interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 1 ), tv.toList().value( 1 ), base, multiplier, &context ) );
2651 caseString += QLatin1String(
"END" );
2657 if ( stops.length() < 2 )
2660 QString caseString = QStringLiteral(
"CASE" );
2662 for (
int i = 0; i < stops.length(); ++i )
2664 caseString += QLatin1String(
" WHEN " );
2665 QStringList conditions;
2668 const QVariant bottomZoom = stops.value( i ).toList().value( 0 );
2669 conditions << QStringLiteral(
"@vector_tile_zoom > %1" ).arg( bottomZoom.toString() );
2671 if ( i < stops.length() - 1 )
2673 const QVariant topZoom = stops.value( i + 1 ).toList().value( 0 );
2674 conditions << QStringLiteral(
"@vector_tile_zoom <= %1" ).arg( topZoom.toString() );
2677 const QVariantList values = stops.value( i ).toList().value( 1 ).toList();
2678 QStringList valuesFixed;
2680 for (
const QVariant &value : values )
2682 const double number = value.toDouble( &ok );
2684 valuesFixed << QString::number( number * multiplier );
2688 caseString += QStringLiteral(
"%1 THEN array(%3)" ).arg(
2689 conditions.join( QLatin1String(
" AND " ) ),
2690 valuesFixed.join(
',' )
2693 caseString += QLatin1String(
" END" );
2699 QString caseString = QStringLiteral(
"CASE " );
2701 for (
int i = 0; i < stops.length() - 1; ++i )
2704 const QVariant bz = stops.value( i ).toList().value( 0 );
2705 const QVariant bv = stops.value( i ).toList().value( 1 );
2706 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2708 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2713 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2714 const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
2715 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2717 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2721 const QString lowerComparator = i == 0 ? QStringLiteral(
">=" ) : QStringLiteral(
">" );
2723 caseString += QStringLiteral(
"WHEN @vector_tile_zoom %1 %2 AND @vector_tile_zoom <= %3 "
2724 "THEN %4 " ).arg( lowerComparator,
2730 const QVariant z = stops.last().toList().value( 0 );
2731 const QVariant v = stops.last().toList().value( 1 );
2732 QString vStr = v.toString();
2733 if ( ( QMetaType::Type )v.userType() == QMetaType::QVariantList )
2736 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 "
2737 "THEN ( ( %2 ) * %3 ) END" ).arg( z.toString() ).arg( vStr ).arg( multiplier );
2741 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 "
2742 "THEN %2 END" ).arg( z.toString() ).arg( v.toDouble() * multiplier );
2750 QString caseString = QStringLiteral(
"CASE " );
2752 for (
int i = 0; i < stops.length() - 1; ++i )
2755 const QVariant bz = stops.value( i ).toList().value( 0 );
2756 const QString bv = stops.value( i ).toList().value( 1 ).toString();
2757 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2759 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2764 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2765 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2767 context.
pushWarning( QObject::tr(
"%1: Expressions in interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2771 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2772 "THEN %3 " ).arg( bz.toString(),
2776 caseString += QStringLiteral(
"ELSE %1 END" ).arg(
QgsExpression::quotedValue( conversionMap.value( stops.constLast().toList().value( 1 ).toString(),
2777 stops.constLast().toList().value( 1 ) ) ) );
2778 if ( defaultString )
2779 *defaultString = stops.constLast().toList().value( 1 ).toString();
2785 QString caseString = QStringLiteral(
"CASE " );
2787 bool isExpression =
false;
2788 for (
int i = 0; i < stops.length() - 1; ++i )
2791 const QVariant bz = stops.value( i ).toList().value( 0 );
2792 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2794 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2799 const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2800 if ( tz.userType() == QMetaType::Type::QVariantList || tz.userType() == QMetaType::Type::QStringList )
2802 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2806 QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
2807 if ( fieldPart.isEmpty() )
2808 fieldPart = QStringLiteral(
"''" );
2809 else if ( !isExpression )
2812 caseString += QStringLiteral(
"WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom < %2 "
2813 "THEN %3 " ).arg( bz.toString(),
2819 const QVariant bz = stops.constLast().toList().value( 0 );
2820 if ( bz.userType() == QMetaType::Type::QVariantList || bz.userType() == QMetaType::Type::QStringList )
2822 context.
pushWarning( QObject::tr(
"%1: Lists in label interpolation function are not supported, skipping." ).arg( context.
layerId() ) );
2826 QString fieldPart = processLabelField( stops.constLast().toList().value( 1 ).toString(), isExpression );
2827 if ( fieldPart.isEmpty() )
2828 fieldPart = QStringLiteral(
"''" );
2829 else if ( !isExpression )
2832 caseString += QStringLiteral(
"WHEN @vector_tile_zoom >= %1 "
2833 "THEN %3 " ).arg( bz.toString(),
2837 QString defaultPart = processLabelField( stops.constFirst().toList().value( 1 ).toString(), isExpression );
2838 if ( defaultPart.isEmpty() )
2839 defaultPart = QStringLiteral(
"''" );
2840 else if ( !isExpression )
2842 caseString += QStringLiteral(
"ELSE %1 END" ).arg( defaultPart );
2849 const QString method = json.
value( 0 ).toString();
2850 if ( method == QLatin1String(
"interpolate" ) )
2854 else if ( method == QLatin1String(
"match" ) )
2856 return parseMatchList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
2858 else if ( method == QLatin1String(
"step" ) )
2860 return parseStepList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
2870 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
2871 if ( attribute.isEmpty() )
2873 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
2877 QString caseString = QStringLiteral(
"CASE " );
2879 for (
int i = 2; i < json.length() - 1; i += 2 )
2882 QVariant variantKeys = json.value( i );
2883 if ( variantKeys.userType() == QMetaType::Type::QVariantList || variantKeys.userType() == QMetaType::Type::QStringList )
2884 keys = variantKeys.toList();
2886 keys = {variantKeys};
2888 QStringList matchString;
2889 for (
const QVariant &key : keys )
2894 const QVariant value = json.value( i + 1 );
2896 QString valueString;
2901 if ( value.userType() == QMetaType::Type::QVariantList || value.userType() == QMetaType::Type::QStringList )
2907 const QColor color =
parseColor( value, context );
2915 const double v = value.toDouble() * multiplier;
2916 valueString = QString::number( v );
2922 const double v = value.toDouble() * maxOpacity;
2923 valueString = QString::number( v );
2929 valueString = QStringLiteral(
"array(%1,%2)" ).arg( value.toList().value( 0 ).toDouble() * multiplier,
2930 value.toList().value( 0 ).toDouble() * multiplier );
2936 if ( value.toList().count() == 2 && value.toList().first().toString() == QLatin1String(
"literal" ) )
2938 valueString = QStringLiteral(
"array(%1)" ).arg( value.toList().at( 1 ).toStringList().join(
',' ) );
2942 valueString = QStringLiteral(
"array(%1)" ).arg( value.toStringList().join(
',' ) );
2948 if ( matchString.count() == 1 )
2950 caseString += QStringLiteral(
"WHEN %1 IS %2 THEN %3 " ).arg( attribute, matchString.at( 0 ), valueString );
2954 caseString += QStringLiteral(
"WHEN %1 IN (%2) THEN %3 " ).arg( attribute, matchString.join(
',' ), valueString );
2958 QVariant lastValue = json.constLast();
2961 switch ( lastValue.userType() )
2963 case QMetaType::Type::QVariantList:
2964 case QMetaType::Type::QStringList:
2965 elseValue =
parseValueList( lastValue.toList(), type, context, multiplier, maxOpacity, defaultColor, defaultNumber ).
asExpression();
2974 const QColor color =
parseColor( lastValue, context );
2976 *defaultColor = color;
2984 const double v = json.constLast().toDouble() * multiplier;
2985 if ( defaultNumber )
2987 elseValue = QString::number( v );
2993 const double v = json.constLast().toDouble() * maxOpacity;
2994 if ( defaultNumber )
2996 elseValue = QString::number( v );
3002 elseValue = QStringLiteral(
"array(%1,%2)" )
3003 .arg( json.constLast().toList().value( 0 ).toDouble() * multiplier )
3004 .arg( json.constLast().toList().value( 0 ).toDouble() * multiplier );
3010 if ( json.constLast().toList().count() == 2 && json.constLast().toList().first().toString() == QLatin1String(
"literal" ) )
3012 elseValue = QStringLiteral(
"array(%1)" ).arg( json.constLast().toList().at( 1 ).toStringList().join(
',' ) );
3016 elseValue = QStringLiteral(
"array(%1)" ).arg( json.constLast().toStringList().join(
',' ) );
3026 caseString += QStringLiteral(
"ELSE %1 END" ).arg( elseValue );
3032 const QString expression =
parseExpression( json.value( 1 ).toList(), context );
3033 if ( expression.isEmpty() )
3035 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3039 QString caseString = QStringLiteral(
"CASE " );
3042 for (
int i = json.length() - 2; i > 0; i -= 2 )
3044 const QVariant stepValue = json.value( i + 1 );
3046 QString valueString;
3047 if ( stepValue.canConvert<QVariantList>()
3059 const QColor color =
parseColor( stepValue, context );
3066 const double v = stepValue.toDouble() * multiplier;
3067 valueString = QString::number( v );
3073 const double v = stepValue.toDouble() * maxOpacity;
3074 valueString = QString::number( v );
3080 valueString = QStringLiteral(
"array(%1,%2)" ).arg(
3081 stepValue.toList().value( 0 ).toDouble() * multiplier ).arg(
3082 stepValue.toList().value( 0 ).toDouble() * multiplier
3089 if ( stepValue.toList().count() == 2 && stepValue.toList().first().toString() == QLatin1String(
"literal" ) )
3091 valueString = QStringLiteral(
"array(%1)" ).arg( stepValue.toList().at( 1 ).toStringList().join(
',' ) );
3095 valueString = QStringLiteral(
"array(%1)" ).arg( stepValue.toStringList().join(
',' ) );
3105 caseString += QStringLiteral(
" WHEN %1 >= %2 THEN (%3) " ).arg( expression, stepKey, valueString );
3109 caseString += QStringLiteral(
"ELSE (%1) END" ).arg( valueString );
3117 if ( json.value( 0 ).toString() != QLatin1String(
"interpolate" ) )
3119 context.
pushWarning( QObject::tr(
"%1: Could not interpret value list" ).arg( context.
layerId() ) );
3124 const QString technique = json.value( 1 ).toList().value( 0 ).toString();
3125 if ( technique == QLatin1String(
"linear" ) )
3127 else if ( technique == QLatin1String(
"exponential" ) )
3128 base = json.value( 1 ).toList(). value( 1 ).toDouble();
3129 else if ( technique == QLatin1String(
"cubic-bezier" ) )
3131 context.
pushWarning( QObject::tr(
"%1: Cubic-bezier interpolation is not supported, linear used instead." ).arg( context.
layerId() ) );
3136 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation method %2" ).arg( context.
layerId(), technique ) );
3140 if ( json.value( 2 ).toList().value( 0 ).toString() != QLatin1String(
"zoom" ) )
3142 context.
pushWarning( QObject::tr(
"%1: Skipping not implemented interpolation input %2" ).arg( context.
layerId(), json.value( 2 ).toString() ) );
3148 for (
int i = 3; i < json.length(); i += 2 )
3150 stops.push_back( QVariantList() << json.value( i ).toString() << json.value( i + 1 ) );
3154 props.insert( QStringLiteral(
"stops" ), stops );
3155 props.insert( QStringLiteral(
"base" ), base );
3171 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported numeric array in interpolate" ).arg( context.
layerId() ) );
3180 if ( ( QMetaType::Type )colorExpression.userType() == QMetaType::QVariantList )
3184 return parseValue( colorExpression, context,
true );
3189 if ( color.userType() != QMetaType::Type::QString )
3191 context.
pushWarning( QObject::tr(
"%1: Could not parse non-string color %2, skipping" ).arg( context.
layerId(), color.toString() ) );
3200 hue = std::max( 0, color.hslHue() );
3201 saturation = color.hslSaturation() / 255.0 * 100;
3202 lightness = color.lightness() / 255.0 * 100;
3203 alpha = color.alpha();
3211 context = *contextPtr;
3215 if ( valueMin.canConvert( QMetaType::Double ) && valueMax.canConvert( QMetaType::Double ) )
3217 bool minDoubleOk =
true;
3218 const double min = valueMin.toDouble( &minDoubleOk );
3219 bool maxDoubleOk =
true;
3220 const double max = valueMax.toDouble( &maxDoubleOk );
3221 if ( minDoubleOk && maxDoubleOk &&
qgsDoubleNear( min, max ) )
3223 return QString::number( min * multiplier );
3227 QString minValueExpr = valueMin.toString();
3228 QString maxValueExpr = valueMax.toString();
3229 if ( valueMin.userType() == QMetaType::Type::QVariantList )
3233 if ( valueMax.userType() == QMetaType::Type::QVariantList )
3239 if ( minValueExpr == maxValueExpr )
3241 expression = minValueExpr;
3247 expression = QStringLiteral(
"scale_linear(@vector_tile_zoom,%1,%2,%3,%4)" ).arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr );
3251 expression = QStringLiteral(
"scale_exponential(@vector_tile_zoom,%1,%2,%3,%4,%5)" ).arg( zoomMin ).arg( zoomMax ).arg( minValueExpr ).arg( maxValueExpr ).arg( base );
3255 if ( multiplier != 1 )
3256 return QStringLiteral(
"(%1) * %2" ).arg( expression ).arg( multiplier );
3263 if ( style == QLatin1String(
"round" ) )
3264 return Qt::RoundCap;
3265 else if ( style == QLatin1String(
"square" ) )
3266 return Qt::SquareCap;
3273 if ( style == QLatin1String(
"bevel" ) )
3274 return Qt::BevelJoin;
3275 else if ( style == QLatin1String(
"round" ) )
3276 return Qt::RoundJoin;
3278 return Qt::MiterJoin;
3283 QString op = expression.value( 0 ).toString();
3284 if ( op == QLatin1String(
"%" ) && expression.size() >= 3 )
3286 return QStringLiteral(
"%1 %2 %3" ).arg( parseValue( expression.value( 1 ), context ),
3288 parseValue( expression.value( 2 ), context ) );
3290 else if ( op == QLatin1String(
"to-number" ) )
3292 return QStringLiteral(
"to_real(%1)" ).arg( parseValue( expression.value( 1 ), context ) );
3294 if ( op == QLatin1String(
"literal" ) )
3296 return expression.value( 1 ).toString();
3298 else if ( op == QLatin1String(
"all" )
3299 || op == QLatin1String(
"any" )
3300 || op == QLatin1String(
"none" ) )
3303 for (
int i = 1; i < expression.size(); ++i )
3305 const QString part = parseValue( expression.at( i ), context );
3306 if ( part.isEmpty() )
3308 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3314 if ( op == QLatin1String(
"none" ) )
3315 return QStringLiteral(
"NOT (%1)" ).arg( parts.join( QLatin1String(
") AND NOT (" ) ) );
3317 QString operatorString;
3318 if ( op == QLatin1String(
"all" ) )
3319 operatorString = QStringLiteral(
") AND (" );
3320 else if ( op == QLatin1String(
"any" ) )
3321 operatorString = QStringLiteral(
") OR (" );
3323 return QStringLiteral(
"(%1)" ).arg( parts.join( operatorString ) );
3325 else if ( op ==
'!' )
3328 QVariantList contraJsonExpr = expression.value( 1 ).toList();
3329 contraJsonExpr[0] = QString( op + contraJsonExpr[0].toString() );
3331 return parseKey( contraJsonExpr, context );
3333 else if ( op == QLatin1String(
"==" )
3334 || op == QLatin1String(
"!=" )
3335 || op == QLatin1String(
">=" )
3337 || op == QLatin1String(
"<=" )
3341 if ( op == QLatin1String(
"==" ) )
3342 op = QStringLiteral(
"IS" );
3343 else if ( op == QLatin1String(
"!=" ) )
3344 op = QStringLiteral(
"IS NOT" );
3345 return QStringLiteral(
"%1 %2 %3" ).arg( parseKey( expression.value( 1 ), context ),
3346 op, parseValue( expression.value( 2 ), context ) );
3348 else if ( op == QLatin1String(
"has" ) )
3350 return parseKey( expression.value( 1 ), context ) + QStringLiteral(
" IS NOT NULL" );
3352 else if ( op == QLatin1String(
"!has" ) )
3354 return parseKey( expression.value( 1 ), context ) + QStringLiteral(
" IS NULL" );
3356 else if ( op == QLatin1String(
"in" ) || op == QLatin1String(
"!in" ) )
3358 const QString key = parseKey( expression.value( 1 ), context );
3361 QVariantList values = expression.mid( 2 );
3362 if ( expression.size() == 3
3363 && expression.at( 2 ).userType() == QMetaType::Type::QVariantList && expression.at( 2 ).toList().count() > 1
3364 && expression.at( 2 ).toList().at( 0 ).toString() == QLatin1String(
"literal" ) )
3366 values = expression.at( 2 ).toList().at( 1 ).toList();
3369 for (
const QVariant &value : std::as_const( values ) )
3371 const QString part = parseValue( value, context );
3372 if ( part.isEmpty() )
3374 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3379 if ( op == QLatin1String(
"in" ) )
3380 return QStringLiteral(
"%1 IN (%2)" ).arg( key, parts.join( QLatin1String(
", " ) ) );
3382 return QStringLiteral(
"(%1 IS NULL OR %1 NOT IN (%2))" ).arg( key, parts.join( QLatin1String(
", " ) ) );
3384 else if ( op == QLatin1String(
"get" ) )
3386 return parseKey( expression.value( 1 ), context );
3388 else if ( op == QLatin1String(
"match" ) )
3390 const QString attribute = expression.value( 1 ).toList().value( 1 ).toString();
3392 if ( expression.size() == 5
3393 && expression.at( 3 ).userType() == QMetaType::Type::Bool && expression.at( 3 ).toBool() ==
true
3394 && expression.at( 4 ).userType() == QMetaType::Type::Bool && expression.at( 4 ).toBool() ==
false )
3397 if ( expression.at( 2 ).userType() == QMetaType::Type::QVariantList || expression.at( 2 ).userType() == QMetaType::Type::QStringList )
3400 for (
const QVariant &p : expression.at( 2 ).toList() )
3402 parts << parseValue( p, context );
3405 if ( parts.size() > 1 )
3410 else if ( expression.at( 2 ).userType() == QMetaType::Type::QString || expression.at( 2 ).userType() == QMetaType::Type::Int
3411 || expression.at( 2 ).userType() == QMetaType::Type::Double || expression.at( 2 ).userType() == QMetaType::Type::LongLong )
3417 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression" ).arg( context.
layerId() ) );
3423 QString caseString = QStringLiteral(
"CASE " );
3424 for (
int i = 2; i < expression.size() - 2; i += 2 )
3426 if ( expression.at( i ).userType() == QMetaType::Type::QVariantList || expression.at( i ).userType() == QMetaType::Type::QStringList )
3429 for (
const QVariant &p : expression.at( i ).toList() )
3434 if ( parts.size() > 1 )
3439 else if ( expression.at( i ).userType() == QMetaType::Type::QString || expression.at( i ).userType() == QMetaType::Type::Int
3440 || expression.at( i ).userType() == QMetaType::Type::Double || expression.at( i ).userType() == QMetaType::Type::LongLong )
3445 caseString += QStringLiteral(
"THEN %1 " ).arg( parseValue( expression.at( i + 1 ), context, colorExpected ) );
3447 caseString += QStringLiteral(
"ELSE %1 END" ).arg( parseValue( expression.last(), context, colorExpected ) );
3451 else if ( op == QLatin1String(
"to-string" ) )
3453 return QStringLiteral(
"to_string(%1)" ).arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3455 else if ( op == QLatin1String(
"to-boolean" ) )
3457 return QStringLiteral(
"to_bool(%1)" ).arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3459 else if ( op == QLatin1String(
"case" ) )
3461 QString caseString = QStringLiteral(
"CASE" );
3462 for (
int i = 1; i < expression.size() - 2; i += 2 )
3464 const QString condition =
parseExpression( expression.value( i ).toList(), context );
3465 const QString value = parseValue( expression.value( i + 1 ), context );
3466 caseString += QStringLiteral(
" WHEN (%1) THEN %2" ).arg( condition, value );
3468 const QString value = parseValue( expression.constLast(), context );
3469 caseString += QStringLiteral(
" ELSE %1 END" ).arg( value );
3472 else if ( op == QLatin1String(
"zoom" ) && expression.count() == 1 )
3474 return QStringLiteral(
"@vector_tile_zoom" );
3476 else if ( op == QLatin1String(
"concat" ) )
3478 QString concatString = QStringLiteral(
"concat(" );
3479 for (
int i = 1; i < expression.size(); i++ )
3482 concatString += QLatin1String(
", " );
3483 concatString += parseValue( expression.value( i ), context );
3485 concatString += QLatin1Char(
')' );
3486 return concatString;
3488 else if ( op == QLatin1String(
"length" ) )
3490 return QStringLiteral(
"length(%1)" ).arg(
parseExpression( expression.value( 1 ).toList(), context ) );
3492 else if ( op == QLatin1String(
"step" ) )
3494 const QString stepExpression =
parseExpression( expression.value( 1 ).toList(), context );
3495 if ( stepExpression.isEmpty() )
3497 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3501 QString caseString = QStringLiteral(
"CASE " );
3503 for (
int i = expression.length() - 2; i > 0; i -= 2 )
3505 const QString stepValue = parseValue( expression.value( i + 1 ), context, colorExpected );
3509 caseString += QStringLiteral(
" WHEN %1 >= %2 THEN (%3) " ).arg( stepExpression, stepKey, stepValue );
3513 caseString += QStringLiteral(
"ELSE (%1) END" ).arg( stepValue );
3520 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression \"%2\"" ).arg( context.
layerId(), op ) );
3529 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3533 const QVariantMap spriteDefinition = context.
spriteDefinitions().value( name ).toMap();
3534 if ( spriteDefinition.size() == 0 )
3536 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3540 const QImage sprite = context.
spriteImage().copy( spriteDefinition.value( QStringLiteral(
"x" ) ).toInt(),
3541 spriteDefinition.value( QStringLiteral(
"y" ) ).toInt(),
3542 spriteDefinition.value( QStringLiteral(
"width" ) ).toInt(),
3543 spriteDefinition.value( QStringLiteral(
"height" ) ).toInt() );
3544 if ( sprite.isNull() )
3546 context.
pushWarning( QObject::tr(
"%1: Could not retrieve sprite '%2'" ).arg( context.
layerId(), name ) );
3550 spriteSize = sprite.size() / spriteDefinition.value( QStringLiteral(
"pixelRatio" ) ).toDouble() * context.
pixelSizeConversionFactor();
3558 auto prepareBase64 = [](
const QImage & sprite )
3561 if ( !sprite.isNull() )
3564 QBuffer buffer( &blob );
3565 buffer.open( QIODevice::WriteOnly );
3566 sprite.save( &buffer,
"PNG" );
3568 const QByteArray encoded = blob.toBase64();
3569 path = QString( encoded );
3570 path.prepend( QLatin1String(
"base64:" ) );
3575 switch ( value.userType() )
3577 case QMetaType::Type::QString:
3579 QString spriteName = value.toString();
3580 const thread_local QRegularExpression fieldNameMatch( QStringLiteral(
"{([^}]+)}" ) );
3581 QRegularExpressionMatch match = fieldNameMatch.match( spriteName );
3582 if ( match.hasMatch() )
3584 const QString fieldName = match.captured( 1 );
3585 spriteProperty = QStringLiteral(
"CASE" );
3586 spriteSizeProperty = QStringLiteral(
"CASE" );
3588 spriteName.replace(
"(", QLatin1String(
"\\(" ) );
3589 spriteName.replace(
")", QLatin1String(
"\\)" ) );
3590 spriteName.replace( fieldNameMatch, QStringLiteral(
"([^\\/\\\\]+)" ) );
3591 const QRegularExpression fieldValueMatch( spriteName );
3593 for (
const QString &name : spriteNames )
3595 match = fieldValueMatch.match( name );
3596 if ( match.hasMatch() )
3600 const QString fieldValue = match.captured( 1 );
3602 path = prepareBase64( sprite );
3603 if ( spritePath.isEmpty() && !path.isEmpty() )
3609 spriteProperty += QStringLiteral(
" WHEN \"%1\" = '%2' THEN '%3'" )
3610 .arg( fieldName, fieldValue, path );
3611 spriteSizeProperty += QStringLiteral(
" WHEN \"%1\" = '%2' THEN %3" )
3612 .arg( fieldName ).arg( fieldValue ).arg( size.width() );
3616 spriteProperty += QLatin1String(
" END" );
3617 spriteSizeProperty += QLatin1String(
" END" );
3621 spriteProperty.clear();
3622 spriteSizeProperty.clear();
3623 const QImage sprite =
retrieveSprite( spriteName, context, spriteSize );
3624 spritePath = prepareBase64( sprite );
3629 case QMetaType::Type::QVariantMap:
3631 const QVariantList stops = value.toMap().value( QStringLiteral(
"stops" ) ).toList();
3632 if ( stops.size() == 0 )
3639 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, spriteSize );
3640 spritePath = prepareBase64( sprite );
3642 spriteProperty = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN '%2'" )
3643 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
3645 spriteSizeProperty = QStringLiteral(
"CASE WHEN @vector_tile_zoom < %1 THEN %2" )
3646 .arg( stops.value( 0 ).toList().value( 0 ).toString() )
3647 .arg( spriteSize.width() );
3649 for (
int i = 0; i < stops.size() - 1; ++i )
3652 sprite =
retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, size );
3653 path = prepareBase64( sprite );
3655 spriteProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
3657 .arg( stops.value( i ).toList().value( 0 ).toString(),
3658 stops.value( i + 1 ).toList().value( 0 ).toString(),
3660 spriteSizeProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
3662 .arg( stops.value( i ).toList().value( 0 ).toString(),
3663 stops.value( i + 1 ).toList().value( 0 ).toString() )
3664 .arg( size.width() );
3666 sprite =
retrieveSprite( stops.last().toList().value( 1 ).toString(), context, size );
3667 path = prepareBase64( sprite );
3669 spriteProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
3671 .arg( stops.last().toList().value( 0 ).toString() )
3673 spriteSizeProperty += QStringLiteral(
" WHEN @vector_tile_zoom >= %1 "
3675 .arg( stops.last().toList().value( 0 ).toString() )
3676 .arg( size.width() );
3680 case QMetaType::Type::QVariantList:
3682 const QVariantList json = value.toList();
3683 const QString method = json.value( 0 ).toString();
3685 if ( method == QLatin1String(
"match" ) )
3687 const QString attribute =
parseExpression( json.value( 1 ).toList(), context );
3688 if ( attribute.isEmpty() )
3690 context.
pushWarning( QObject::tr(
"%1: Could not interpret match list" ).arg( context.
layerId() ) );
3694 spriteProperty = QStringLiteral(
"CASE" );
3695 spriteSizeProperty = QStringLiteral(
"CASE" );
3697 for (
int i = 2; i < json.length() - 1; i += 2 )
3699 const QVariant matchKey = json.value( i );
3700 const QVariant matchValue = json.value( i + 1 );
3701 QString matchString;
3702 switch ( matchKey.userType() )
3704 case QMetaType::Type::QVariantList:
3705 case QMetaType::Type::QStringList:
3707 const QVariantList keys = matchKey.toList();
3708 QStringList matchStringList;
3709 for (
const QVariant &key : keys )
3713 matchString = matchStringList.join(
',' );
3717 case QMetaType::Type::Bool:
3718 case QMetaType::Type::QString:
3719 case QMetaType::Type::Int:
3720 case QMetaType::Type::LongLong:
3721 case QMetaType::Type::Double:
3728 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported sprite type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( value.userType() ) ) ) );
3733 const QImage sprite =
retrieveSprite( matchValue.toString(), context, spriteSize );
3734 spritePath = prepareBase64( sprite );
3736 spriteProperty += QStringLiteral(
" WHEN %1 IN (%2) "
3737 "THEN '%3'" ).arg( attribute,
3741 spriteSizeProperty += QStringLiteral(
" WHEN %1 IN (%2) "
3742 "THEN %3" ).arg( attribute,
3743 matchString ).arg( spriteSize.width() );
3746 if ( !json.constLast().toString().isEmpty() )
3748 const QImage sprite =
retrieveSprite( json.constLast().toString(), context, spriteSize );
3749 spritePath = prepareBase64( sprite );
3753 spritePath = QString();
3756 spriteProperty += QStringLiteral(
" ELSE '%1' END" ).arg( spritePath );
3757 spriteSizeProperty += QStringLiteral(
" ELSE %3 END" ).arg( spriteSize.width() );
3760 else if ( method == QLatin1String(
"step" ) )
3762 const QString expression =
parseExpression( json.value( 1 ).toList(), context );
3763 if ( expression.isEmpty() )
3765 context.
pushWarning( QObject::tr(
"%1: Could not interpret step list" ).arg( context.
layerId() ) );
3769 spriteProperty = QStringLiteral(
"CASE" );
3770 spriteSizeProperty = QStringLiteral(
"CASE" );
3771 for (
int i = json.length() - 2; i > 2; i -= 2 )
3774 const QString stepValue = json.value( i + 1 ).toString();
3776 const QImage sprite =
retrieveSprite( stepValue, context, spriteSize );
3777 spritePath = prepareBase64( sprite );
3779 spriteProperty += QStringLiteral(
" WHEN %1 >= %2 THEN '%3' " ).arg( expression, stepKey, spritePath );
3780 spriteSizeProperty += QStringLiteral(
" WHEN %1 >= %2 THEN %3 " ).arg( expression ).arg( stepKey ).arg( spriteSize.width() );
3783 const QImage sprite =
retrieveSprite( json.at( 2 ).toString(), context, spriteSize );
3784 spritePath = prepareBase64( sprite );
3786 spriteProperty += QStringLiteral(
"ELSE '%1' END" ).arg( spritePath );
3787 spriteSizeProperty += QStringLiteral(
"ELSE %3 END" ).arg( spriteSize.width() );
3790 else if ( method == QLatin1String(
"case" ) )
3792 spriteProperty = QStringLiteral(
"CASE" );
3793 spriteSizeProperty = QStringLiteral(
"CASE" );
3794 for (
int i = 1; i < json.length() - 2; i += 2 )
3796 const QString caseExpression =
parseExpression( json.value( i ).toList(), context );
3797 const QString caseValue = json.value( i + 1 ).toString();
3799 const QImage sprite =
retrieveSprite( caseValue, context, spriteSize );
3800 spritePath = prepareBase64( sprite );
3802 spriteProperty += QStringLiteral(
" WHEN %1 THEN '%2' " ).arg( caseExpression, spritePath );
3803 spriteSizeProperty += QStringLiteral(
" WHEN %1 THEN %2 " ).arg( caseExpression ).arg( spriteSize.width() );
3805 const QImage sprite =
retrieveSprite( json.last().toString(), context, spriteSize );
3806 spritePath = prepareBase64( sprite );
3808 spriteProperty += QStringLiteral(
"ELSE '%1' END" ).arg( spritePath );
3809 spriteSizeProperty += QStringLiteral(
"ELSE %3 END" ).arg( spriteSize.width() );
3814 context.
pushWarning( QObject::tr(
"%1: Could not interpret sprite value list with method %2" ).arg( context.
layerId(), method ) );
3820 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported sprite type (%2)." ).arg( context.
layerId(), QMetaType::typeName(
static_cast<QMetaType::Type
>( value.userType() ) ) ) );
3830 switch ( value.userType() )
3832 case QMetaType::Type::QVariantList:
3833 case QMetaType::Type::QStringList:
3836 case QMetaType::Type::Bool:
3837 case QMetaType::Type::QString:
3838 if ( colorExpected )
3843 return parseValue(
c, context );
3848 case QMetaType::Type::Int:
3849 case QMetaType::Type::LongLong:
3850 case QMetaType::Type::Double:
3851 return value.toString();
3853 case QMetaType::Type::QColor:
3854 c = value.value<QColor>();
3855 return QString(
"color_rgba(%1,%2,%3,%4)" ).arg(
c.red() ).arg(
c.green() ).arg(
c.blue() ).arg(
c.alpha() );
3858 context.
pushWarning( QObject::tr(
"%1: Skipping unsupported expression part" ).arg( context.
layerId() ) );
3866 if ( value.toString() == QLatin1String(
"$type" ) )
3868 return QStringLiteral(
"_geom_type" );
3870 if ( value.toString() == QLatin1String(
"level" ) )
3872 return QStringLiteral(
"level" );
3874 else if ( ( value.userType() == QMetaType::Type::QVariantList && value.toList().size() == 1 ) || value.userType() == QMetaType::Type::QStringList )
3876 if ( value.toList().size() > 1 )
3877 return value.toList().at( 1 ).toString();
3880 QString valueString = value.toList().value( 0 ).toString();
3881 if ( valueString == QLatin1String(
"geometry-type" ) )
3883 return QStringLiteral(
"_geom_type" );
3888 else if ( value.userType() == QMetaType::Type::QVariantList && value.toList().size() > 1 )
3895QString QgsMapBoxGlStyleConverter::processLabelField(
const QString &
string,
bool &isExpression )
3899 const thread_local QRegularExpression singleFieldRx( QStringLiteral(
"^{([^}]+)}$" ) );
3900 const QRegularExpressionMatch match = singleFieldRx.match(
string );
3901 if ( match.hasMatch() )
3903 isExpression =
false;
3904 return match.captured( 1 );
3907 const thread_local QRegularExpression multiFieldRx( QStringLiteral(
"(?={[^}]+})" ) );
3908 const QStringList parts =
string.split( multiFieldRx );
3909 if ( parts.size() > 1 )
3911 isExpression =
true;
3914 for (
const QString &part : parts )
3916 if ( part.isEmpty() )
3919 if ( !part.contains(
'{' ) )
3926 const QStringList split = part.split(
'}' );
3928 if ( !split.at( 1 ).isEmpty() )
3931 return QStringLiteral(
"concat(%1)" ).arg( res.join(
',' ) );
3935 isExpression =
false;
3942 return mRenderer ? mRenderer->clone() :
nullptr;
3947 return mLabeling ? mLabeling->clone() :
nullptr;
3957 return mRasterSubLayers;
3962 QList<QgsMapLayer *> subLayers;
3965 const QString sourceName = subLayer.source();
3966 std::unique_ptr< QgsRasterLayer > rl;
3973 rl->pipe()->setDataDefinedProperties( subLayer.dataDefinedProperties() );
3980 subLayers.append( rl.release() );
3989 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
3992 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
3993 context = tmpContext.get();
3998 if (
string.compare( QLatin1String(
"vector" ), Qt::CaseInsensitive ) == 0 )
4000 else if (
string.compare( QLatin1String(
"raster" ), Qt::CaseInsensitive ) == 0 )
4002 else if (
string.compare( QLatin1String(
"raster-dem" ), Qt::CaseInsensitive ) == 0 )
4004 else if (
string.compare( QLatin1String(
"geojson" ), Qt::CaseInsensitive ) == 0 )
4006 else if (
string.compare( QLatin1String(
"image" ), Qt::CaseInsensitive ) == 0 )
4008 else if (
string.compare( QLatin1String(
"video" ), Qt::CaseInsensitive ) == 0 )
4010 context->
pushWarning( QObject::tr(
"Invalid source type \"%1\" for source \"%2\"" ).arg(
string, name ) );
4016 const QString name = it.key();
4017 const QVariantMap jsonSource = it.value().toMap();
4018 const QString typeString = jsonSource.value( QStringLiteral(
"type" ) ).toString();
4041 std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
4044 tmpContext = std::make_unique< QgsMapBoxGlStyleConversionContext >();
4045 context = tmpContext.get();
4048 std::unique_ptr< QgsMapBoxGlStyleRasterSource > raster = std::make_unique< QgsMapBoxGlStyleRasterSource >( name );
4049 if ( raster->setFromJson( source, context ) )
4050 mSources.append( raster.release() );
4053bool QgsMapBoxGlStyleConverter::numericArgumentsOnly(
const QVariant &bottomVariant,
const QVariant &topVariant,
double &bottom,
double &top )
4055 if ( bottomVariant.canConvert( QMetaType::Double ) && topVariant.canConvert( QMetaType::Double ) )
4057 bool bDoubleOk, tDoubleOk;
4058 bottom = bottomVariant.toDouble( &bDoubleOk );
4059 top = topVariant.toDouble( &tDoubleOk );
4060 return ( bDoubleOk && tDoubleOk );
4071 mWarnings << warning;
4086 return mSizeConversionFactor;
4091 mSizeConversionFactor = sizeConversionFactor;
4096 return mSpriteImage;
4101 return mSpriteDefinitions;
4106 mSpriteImage = image;
4107 mSpriteDefinitions = definitions;
4157 mAttribution = json.value( QStringLiteral(
"attribution" ) ).toString();
4159 const QString scheme = json.value( QStringLiteral(
"scheme" ), QStringLiteral(
"xyz" ) ).toString();
4160 if ( scheme.compare( QLatin1String(
"xyz" ) ) == 0 )
4166 context->
pushWarning( QObject::tr(
"%1 scheme is not supported for raster source %2" ).arg( scheme,
name() ) );
4170 mMinZoom = json.value( QStringLiteral(
"minzoom" ), QStringLiteral(
"0" ) ).toInt();
4171 mMaxZoom = json.value( QStringLiteral(
"maxzoom" ), QStringLiteral(
"22" ) ).toInt();
4172 mTileSize = json.value( QStringLiteral(
"tileSize" ), QStringLiteral(
"512" ) ).toInt();
4174 const QVariantList
tiles = json.value( QStringLiteral(
"tiles" ) ).toList();
4175 for (
const QVariant &tile :
tiles )
4177 mTiles.append( tile.toString() );
4186 parts.insert( QStringLiteral(
"type" ), QStringLiteral(
"xyz" ) );
4187 parts.insert( QStringLiteral(
"url" ), mTiles.value( 0 ) );
4189 if ( mTileSize == 256 )
4190 parts.insert( QStringLiteral(
"tilePixelRation" ), QStringLiteral(
"1" ) );
4191 else if ( mTileSize == 512 )
4192 parts.insert( QStringLiteral(
"tilePixelRation" ), QStringLiteral(
"2" ) );
4194 parts.insert( QStringLiteral(
"zmax" ), QString::number( mMaxZoom ) );
4195 parts.insert( QStringLiteral(
"zmin" ), QString::number( mMinZoom ) );
4197 std::unique_ptr< QgsRasterLayer > rl = std::make_unique< QgsRasterLayer >(
QgsProviderRegistry::instance()->encodeUri( QStringLiteral(
"wms" ), parts ),
name(), QStringLiteral(
"wms" ) );
4198 return rl.release();
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
@ OnLine
Labels can be placed directly over a line feature.
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
@ CentralPoint
Place symbols at the mid point of the line.
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
@ Curved
Arranges candidates following the curvature of a line feature. Applies to line layers only.
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
@ FollowPlacement
Alignment follows placement of label, e.g., labels to the left of a feature will be drawn with right ...
RenderUnit
Rendering size units.
MapBoxGlStyleSourceType
Available MapBox GL style source types.
@ RasterDem
Raster DEM source.
@ Unknown
Other/unknown source type.
@ Viewport
Relative to the whole viewport/output device.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the pen join style used to render the line (e.g.
void setPenCapStyle(Qt::PenCapStyle style)
Sets the pen cap style used to render the line (e.g.
static QgsFontManager * fontManager()
Returns the application font manager, which manages available fonts and font installation for the QGI...
A paint effect which blurs a source picture, using a number of different blur methods.
void setBlurUnit(const Qgis::RenderUnit unit)
Sets the units used for the blur level (radius).
@ StackBlur
Stack blur, a fast but low quality blur. Valid blur level values are between 0 - 16.
void setBlurMethod(const BlurMethod method)
Sets the blur method (algorithm) to use for performing the blur.
void setBlurLevel(const double level)
Sets blur level (radius)
A paint effect which consists of a stack of other chained paint effects.
void appendEffect(QgsPaintEffect *effect)
Appends an effect to the end of the stack.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
Create an expression allowing to evaluate if a field is equal to a value.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
QString processFontFamilyName(const QString &name) const
Processes a font family name, applying any matching fontFamilyReplacements() to the name.
static QFont createFont(const QString &family, int pointSize=-1, int weight=-1, bool italic=false)
Creates a font with the specified family.
static bool fontFamilyHasStyle(const QString &family, const QString &style)
Check whether font family on system has specific style.
static QVariant parseJson(const std::string &jsonString)
Converts JSON jsonString to a QVariant, in case of parsing error an invalid QVariant is returned and ...
void setPlacementFlags(Qgis::LabelLinePlacementFlags flags)
Returns the line placement flags, which dictate how line labels can be placed above or below the line...
void setFactor(double factor)
Sets the obstacle factor, where 1.0 = default, < 1.0 more likely to be covered by labels,...
void setQuadrant(Qgis::LabelQuadrantPosition quadrant)
Sets the quadrant in which to offset labels from the point.
virtual void setWidth(double width)
Sets the width of the line symbol layer.
void setOffset(double offset)
Sets the line's offset.
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the unit for the line's offset.
Abstract base class for MapBox GL style sources.
QString name() const
Returns the source's name.
virtual ~QgsMapBoxGlStyleAbstractSource()
QgsMapBoxGlStyleAbstractSource(const QString &name)
Constructor for QgsMapBoxGlStyleAbstractSource.
Context for a MapBox GL style conversion operation.
void setLayerId(const QString &value)
Sets the layer ID of the layer currently being converted.
QStringList warnings() const
Returns a list of warning messages generated during the conversion.
void pushWarning(const QString &warning)
Pushes a warning message generated during the conversion.
double pixelSizeConversionFactor() const
Returns the pixel size conversion factor, used to scale the original pixel sizes when converting styl...
void setTargetUnit(Qgis::RenderUnit targetUnit)
Sets the target unit type.
void setPixelSizeConversionFactor(double sizeConversionFactor)
Sets the pixel size conversion factor, used to scale the original pixel sizes when converting styles.
Qgis::RenderUnit targetUnit() const
Returns the target unit type.
void setSprites(const QImage &image, const QVariantMap &definitions)
Sets the sprite image and definitions JSON to use during conversion.
QString layerId() const
Returns the layer ID of the layer currently being converted.
QImage spriteImage() const
Returns the sprite image to use during conversion, or an invalid image if this is not set.
void clearWarnings()
Clears the list of warning messages.
QVariantMap spriteDefinitions() const
Returns the sprite definitions to use during conversion.
static QString parseOpacityStops(double base, const QVariantList &stops, int maxOpacity, QgsMapBoxGlStyleConversionContext &context)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate alpha ...
static QString parseColorExpression(const QVariant &colorExpression, QgsMapBoxGlStyleConversionContext &context)
Converts an expression representing a color to a string (can be color string or an expression where a...
static QString parseStops(double base, const QVariantList &stops, double multiplier, QgsMapBoxGlStyleConversionContext &context)
Parses a list of interpolation stops.
QgsVectorTileRenderer * renderer() const
Returns a new instance of a vector tile renderer representing the converted style,...
static QString parseExpression(const QVariantList &expression, QgsMapBoxGlStyleConversionContext &context, bool colorExpected=false)
Converts a MapBox GL expression to a QGIS expression.
PropertyType
Property types, for interpolated value conversion.
@ Point
Point/offset property.
@ Numeric
Numeric property (e.g. line width, text size)
@ Opacity
Opacity property.
@ 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 QString interpolateExpression(double zoomMin, double zoomMax, QVariant valueMin, QVariant valueMax, double base, double multiplier=1, QgsMapBoxGlStyleConversionContext *contextPtr=0)
Generates an interpolation for values between valueMin and valueMax, scaled between the ranges zoomMi...
static QgsProperty parseStepList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a match function value list.
static QgsProperty parseInterpolatePointByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, QPointF *defaultPoint=nullptr)
Interpolates a point/offset with either scale_linear() or scale_exp() (depending on base value).
static bool parseCircleLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context)
Parses a circle layer.
Result convert(const QVariantMap &style, QgsMapBoxGlStyleConversionContext *context=nullptr)
Converts a JSON style map, and returns the resultant status of the conversion.
static QgsProperty parseInterpolateListByZoom(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Interpolates a list which starts with the interpolate function.
~QgsMapBoxGlStyleConverter()
QList< QgsMapLayer * > createSubLayers() const
Returns a list of new map layers corresponding to sublayers of the style, e.g.
Result
Result of conversion.
@ Success
Conversion was successful.
@ NoLayerList
No layer list was found in JSON input.
QgsMapBoxGlStyleConverter()
Constructor for QgsMapBoxGlStyleConverter.
static QImage retrieveSprite(const QString &name, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize)
Retrieves the sprite image with the specified name, taken from the specified context.
static QString parseLabelStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context)
Parses a list of interpolation stops containing label values.
void parseLayers(const QVariantList &layers, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse list of layers from JSON.
static QgsProperty parseInterpolateColorByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, QColor *defaultColor=nullptr)
Parses a color value which is interpolated by zoom range.
static QString retrieveSpriteAsBase64WithProperties(const QVariant &value, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize, QString &spriteProperty, QString &spriteSizeProperty)
Retrieves the sprite image with the specified name, taken from the specified context as a base64 enco...
static QgsProperty parseInterpolateOpacityByZoom(const QVariantMap &json, int maxOpacity, QgsMapBoxGlStyleConversionContext *contextPtr=0)
Interpolates opacity with either scale_linear() or scale_exp() (depending on base value).
void parseSources(const QVariantMap &sources, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse list of sources from JSON.
static QColor parseColor(const QVariant &color, QgsMapBoxGlStyleConversionContext &context)
Parses a color in one of these supported formats:
static bool parseSymbolLayerAsRenderer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as a renderer.
static bool parseFillLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context, bool isBackgroundStyle=false)
Parses a fill layer.
static void parseSymbolLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, bool &hasRenderer, QgsVectorTileBasicLabelingStyle &labelingStyle, bool &hasLabeling, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as renderer or labeling.
static bool parseLineLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context)
Parses a line layer.
void parseRasterSource(const QVariantMap &source, const QString &name, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse a raster source from JSON.
static void colorAsHslaComponents(const QColor &color, int &hue, int &saturation, int &lightness, int &alpha)
Takes a QColor object and returns HSLA components in required format for QGIS color_hsla() expression...
static Qt::PenCapStyle parseCapStyle(const QString &style)
Converts a value to Qt::PenCapStyle enum from JSON value.
static QString parseStringStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, const QVariantMap &conversionMap, QString *defaultString=nullptr)
Parses a list of interpolation stops containing string values.
static QgsProperty parseMatchList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a match function value list.
static QgsProperty parseValueList(const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, int maxOpacity=255, QColor *defaultColor=nullptr, double *defaultNumber=nullptr)
Parses and converts a value list (e.g.
static QString parseArrayStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier=1)
Takes numerical arrays from stops.
static QString parsePointStops(double base, const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier=1)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate point/...
Encapsulates a MapBox GL style raster source.
Qgis::MapBoxGlStyleSourceType type() const override
Returns the source type.
QgsMapBoxGlStyleRasterSource(const QString &name)
Constructor for QgsMapBoxGlStyleRasterSource.
QgsRasterLayer * toRasterLayer() const
Returns a new raster layer representing the raster source, or nullptr if the source cannot be represe...
bool setFromJson(const QVariantMap &json, QgsMapBoxGlStyleConversionContext *context) override
Sets the source's state from a json map.
QStringList tiles() const
Returns the list of tile sources.
Encapsulates a MapBox GL style raster sub layer.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the layer's data defined properties.
QgsMapBoxGlStyleRasterSubLayer(const QString &id, const QString &source)
Constructor for QgsMapBoxGlStyleRasterSubLayer, with the given id and source.
Line symbol layer type which draws repeating marker symbols along a line feature.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
virtual void setSize(double size)
Sets the symbol size.
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's offset.
void setAngle(double angle)
Sets the rotation angle for the marker.
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's size.
A marker symbol type, for rendering Point and MultiPoint geometries.
void setEnabled(bool enabled)
Sets whether the effect is enabled.
Contains settings for how a map layer will be labeled.
double yOffset
Vertical offset of label.
const QgsLabelObstacleSettings & obstacleSettings() const
Returns the label obstacle settings.
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
double xOffset
Horizontal offset of label.
Qgis::LabelPlacement placement
Label placement mode.
Qgis::LabelMultiLineAlignment multilineAlign
Horizontal alignment of multi-line labels.
int priority
Label priority.
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 a integer key value.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const final
Returns the calculated value of the property with the specified key from within the collection.
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
QgsProperty property(int key) const final
Returns a matching property from the collection, if one exists.
A store for object properties.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
QString expressionString() const
Returns the expression used for the property value.
QVariant value(const QgsExpressionContext &context, const QVariant &defaultValue=QVariant(), bool *ok=nullptr) const
Calculates the current value of the property, including any transforms which are set for the property...
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
void setExpressionString(const QString &expression)
Sets the expression to use for the property value.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
A class for filling symbols with a repeated raster image.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the unit for the image's width and height.
void setOpacity(double opacity)
Sets the opacity for the raster image used in the fill.
void setImageFilePath(const QString &imagePath)
Sets the path to the raster image used for the fill.
void setWidth(double width)
Sets the width for scaling the image used in the fill.
void setCoordinateMode(Qgis::SymbolCoordinateReference mode)
Set the coordinate mode for fill.
Represents a raster layer.
Line symbol layer type which draws line sections using a raster image file.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Raster marker symbol layer class.
void setOpacity(double opacity)
Set the marker opacity.
void setPath(const QString &path)
Set the marker raster image path.
@ RendererOpacity
Raster renderer global opacity.
Renders polygons using a single fill and stroke color.
void setBrushStyle(Qt::BrushStyle style)
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setStrokeWidth(double strokeWidth)
void setStrokeStyle(Qt::PenStyle strokeStyle)
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the unit for the fill's offset.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
void setOffset(QPointF offset)
Sets an offset by which polygons will be translated during rendering.
void setStrokeColor(const QColor &strokeColor) override
Sets the stroke color for the symbol layer.
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
void setPenCapStyle(Qt::PenCapStyle style)
Sets the pen cap style used to render the line (e.g.
void setUseCustomDashPattern(bool b)
Sets whether the line uses a custom dash pattern.
void setCustomDashVector(const QVector< qreal > &vector)
Sets the custom dash vector, which is the pattern of alternating drawn/skipped lengths used while ren...
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the pen join style used to render the line (e.g.
Simple marker symbol layer, consisting of a rendered shape with solid fill color and an stroke.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
void setStrokeWidthUnit(Qgis::RenderUnit u)
Sets the unit for the width of the marker's stroke.
void setStrokeWidth(double w)
Sets the width of the marker's stroke.
void setStrokeColor(const QColor &color) override
Sets the marker's stroke color.
static QColor parseColor(const QString &colorStr, bool strictEval=false)
Attempts to parse a string as a color using a variety of common formats, including hex codes,...
@ File
Filename, eg for svg files.
@ CustomDash
Custom dash pattern.
@ Name
Name, eg shape name for simple markers.
@ StrokeColor
Stroke color.
@ Interval
Line marker interval.
@ StrokeWidth
Stroke width.
@ LayerEnabled
Whether symbol layer is enabled.
virtual void setColor(const QColor &color)
Sets the "representative" color for the symbol layer.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the symbol layer's property collection, used for data defined overrides.
void setPlacements(Qgis::MarkerLinePlacements placements)
Sets the placement of the symbols.
Container for settings relating to a text background object.
void setMarkerSymbol(QgsMarkerSymbol *symbol)
Sets the current marker symbol for the background shape.
void setSizeType(SizeType type)
Sets the method used to determine the size of the background shape (e.g., fixed size or buffer around...
@ ShapeMarkerSymbol
Marker symbol.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units used for the shape's size.
void setType(ShapeType type)
Sets the type of background shape to draw (e.g., square, ellipse, SVG).
void setEnabled(bool enabled)
Sets whether the text background will be drawn.
void setSize(QSizeF size)
Sets the size of the background shape.
void setColor(const QColor &color)
Sets the color for the buffer.
void setOpacity(double opacity)
Sets the buffer opacity.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units used for the buffer size.
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the buffer.
void setSize(double size)
Sets the size of the buffer.
Container for all settings relating to text rendering.
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void setSize(double size)
Sets the size for rendered text.
void setFont(const QFont &font)
Sets the font used for rendering text.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the size of rendered text.
void setBackground(const QgsTextBackgroundSettings &backgroundSettings)
Sets the text's background settings.q.
void setNamedStyle(const QString &style)
Sets the named style for the font used for rendering text.
QFont font() const
Returns the font used for rendering text.
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
Configuration of a single style within QgsVectorTileBasicLabeling.
void setLayerName(const QString &name)
Sets name of the sub-layer to render (empty layer means that all layers match)
void setMinZoomLevel(int minZoom)
Sets minimum zoom level index (negative number means no limit).
void setFilterExpression(const QString &expr)
Sets filter expression (empty filter means that all features match)
void setMaxZoomLevel(int maxZoom)
Sets maximum zoom level index (negative number means no limit).
void setStyleName(const QString &name)
Sets human readable name of this style.
void setGeometryType(Qgis::GeometryType geomType)
Sets type of the geometry that will be used (point / line / polygon)
void setLabelSettings(const QgsPalLayerSettings &settings)
Sets labeling configuration of this style.
void setEnabled(bool enabled)
Sets whether this style is enabled (used for rendering)
Basic labeling configuration for vector tile layers.
Definition of map rendering of a subset of vector tile data.
void setEnabled(bool enabled)
Sets whether this style is enabled (used for rendering)
void setMinZoomLevel(int minZoom)
Sets minimum zoom level index (negative number means no limit).
void setLayerName(const QString &name)
Sets name of the sub-layer to render (empty layer means that all layers match)
void setFilterExpression(const QString &expr)
Sets filter expression (empty filter means that all features match)
void setSymbol(QgsSymbol *sym)
Sets symbol for rendering. Takes ownership of the symbol.
void setStyleName(const QString &name)
Sets human readable name of this style.
void setMaxZoomLevel(int maxZoom)
Sets maximum zoom level index (negative number means no limit).
void setGeometryType(Qgis::GeometryType geomType)
Sets type of the geometry that will be used (point / line / polygon)
The default vector tile renderer implementation.
Base class for labeling configuration classes for vector tile layers.
Abstract base class for all vector tile renderer implementations.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
#define QgsDebugError(str)
QList< QgsSymbolLayer * > QgsSymbolLayerList