QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgsmapboxglstyleconverter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmapboxglstyleconverter.cpp
3  --------------------------------------
4  Date : September 2020
5  Copyright : (C) 2020 by Nyall Dawson
6  Email : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 
17 /*
18  * Ported from original work by Martin Dobias, and extended by the MapTiler team!
19  */
20 
24 #include "qgssymbollayer.h"
25 #include "qgssymbollayerutils.h"
26 #include "qgslogger.h"
27 #include "qgsfillsymbollayer.h"
28 #include "qgslinesymbollayer.h"
29 #include "qgsfontutils.h"
30 #include "qgsjsonutils.h"
31 #include "qgspainteffect.h"
32 #include "qgseffectstack.h"
33 #include "qgsblureffect.h"
34 #include "qgsmarkersymbollayer.h"
36 
37 
39 {
40 }
41 
43 {
44  mError.clear();
45  mWarnings.clear();
46  if ( style.contains( QStringLiteral( "layers" ) ) )
47  {
48  parseLayers( style.value( QStringLiteral( "layers" ) ).toList(), context );
49  }
50  else
51  {
52  mError = QObject::tr( "Could not find layers list in JSON" );
53  return NoLayerList;
54  }
55  return Success;
56 }
57 
59 {
60  return convert( QgsJsonUtils::parseJson( style ).toMap(), context );
61 }
62 
64 
66 {
67  std::unique_ptr< QgsMapBoxGlStyleConversionContext > tmpContext;
68  if ( !context )
69  {
70  tmpContext = qgis::make_unique< QgsMapBoxGlStyleConversionContext >();
71  context = tmpContext.get();
72  }
73 
74  QList<QgsVectorTileBasicRendererStyle> rendererStyles;
75  QList<QgsVectorTileBasicLabelingStyle> labelingStyles;
76 
77  for ( const QVariant &layer : layers )
78  {
79  const QVariantMap jsonLayer = layer.toMap();
80 
81  const QString layerType = jsonLayer.value( QStringLiteral( "type" ) ).toString();
82  if ( layerType == QLatin1String( "background" ) )
83  continue;
84 
85  const QString styleId = jsonLayer.value( QStringLiteral( "id" ) ).toString();
86  context->setLayerId( styleId );
87  const QString layerName = jsonLayer.value( QStringLiteral( "source-layer" ) ).toString();
88 
89  const int minZoom = jsonLayer.value( QStringLiteral( "minzoom" ), QStringLiteral( "-1" ) ).toInt();
90  const int maxZoom = jsonLayer.value( QStringLiteral( "maxzoom" ), QStringLiteral( "-1" ) ).toInt();
91 
92  const bool enabled = jsonLayer.value( QStringLiteral( "visibility" ) ).toString() != QLatin1String( "none" );
93 
94  QString filterExpression;
95  if ( jsonLayer.contains( QStringLiteral( "filter" ) ) )
96  {
97  filterExpression = parseExpression( jsonLayer.value( QStringLiteral( "filter" ) ).toList(), *context );
98  }
99 
100  QgsVectorTileBasicRendererStyle rendererStyle;
101  QgsVectorTileBasicLabelingStyle labelingStyle;
102 
103  bool hasRendererStyle = false;
104  bool hasLabelingStyle = false;
105  if ( layerType == QLatin1String( "fill" ) )
106  {
107  hasRendererStyle = parseFillLayer( jsonLayer, rendererStyle, *context );
108  }
109  else if ( layerType == QLatin1String( "line" ) )
110  {
111  hasRendererStyle = parseLineLayer( jsonLayer, rendererStyle, *context );
112  }
113  else if ( layerType == QLatin1String( "symbol" ) )
114  {
115  parseSymbolLayer( jsonLayer, rendererStyle, hasRendererStyle, labelingStyle, hasLabelingStyle, *context );
116  }
117  else
118  {
119  mWarnings << QObject::tr( "%1: Skipping unknown layer type %2" ).arg( context->layerId(), layerType );
120  QgsDebugMsg( mWarnings.constLast() );
121  continue;
122  }
123 
124  if ( hasRendererStyle )
125  {
126  rendererStyle.setStyleName( styleId );
127  rendererStyle.setLayerName( layerName );
128  rendererStyle.setFilterExpression( filterExpression );
129  rendererStyle.setMinZoomLevel( minZoom );
130  rendererStyle.setMaxZoomLevel( maxZoom );
131  rendererStyle.setEnabled( enabled );
132  rendererStyles.append( rendererStyle );
133  }
134 
135  if ( hasLabelingStyle )
136  {
137  labelingStyle.setStyleName( styleId );
138  labelingStyle.setLayerName( layerName );
139  labelingStyle.setFilterExpression( filterExpression );
140  labelingStyle.setMinZoomLevel( minZoom );
141  labelingStyle.setMaxZoomLevel( maxZoom );
142  labelingStyle.setEnabled( enabled );
143  labelingStyles.append( labelingStyle );
144  }
145 
146  mWarnings.append( context->warnings() );
147  context->clearWarnings();
148  }
149 
150  mRenderer = qgis::make_unique< QgsVectorTileBasicRenderer >();
151  QgsVectorTileBasicRenderer *renderer = dynamic_cast< QgsVectorTileBasicRenderer *>( mRenderer.get() );
152  renderer->setStyles( rendererStyles );
153 
154  mLabeling = qgis::make_unique< QgsVectorTileBasicLabeling >();
155  QgsVectorTileBasicLabeling *labeling = dynamic_cast< QgsVectorTileBasicLabeling * >( mLabeling.get() );
156  labeling->setStyles( labelingStyles );
157 }
158 
160 {
161  if ( !jsonLayer.contains( QStringLiteral( "paint" ) ) )
162  {
163  context.pushWarning( QObject::tr( "%1: Layer has no paint property, skipping" ).arg( jsonLayer.value( QStringLiteral( "id" ) ).toString() ) );
164  return false;
165  }
166 
167  const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral( "paint" ) ).toMap();
168 
169  QgsPropertyCollection ddProperties;
170  QgsPropertyCollection ddRasterProperties;
171 
172  // fill color
173  QColor fillColor;
174  if ( jsonPaint.contains( QStringLiteral( "fill-color" ) ) )
175  {
176  const QVariant jsonFillColor = jsonPaint.value( QStringLiteral( "fill-color" ) );
177  switch ( jsonFillColor.type() )
178  {
179  case QVariant::Map:
180  ddProperties.setProperty( QgsSymbolLayer::PropertyFillColor, parseInterpolateColorByZoom( jsonFillColor.toMap(), context, &fillColor ) );
181  break;
182 
183  case QVariant::List:
184  case QVariant::StringList:
185  ddProperties.setProperty( QgsSymbolLayer::PropertyFillColor, parseValueList( jsonFillColor.toList(), PropertyType::Color, context, 1, 255, &fillColor ) );
186  break;
187 
188  case QVariant::String:
189  fillColor = parseColor( jsonFillColor.toString(), context );
190  break;
191 
192  default:
193  {
194  context.pushWarning( QObject::tr( "%1: Skipping unsupported fill-color type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonFillColor.type() ) ) );
195  break;
196  }
197  }
198  }
199  else
200  {
201  // defaults to #000000
202  fillColor = QColor( 0, 0, 0 );
203  }
204 
205  QColor fillOutlineColor;
206  if ( !jsonPaint.contains( QStringLiteral( "fill-outline-color" ) ) )
207  {
208  // fill-outline-color
209  if ( fillColor.isValid() )
210  fillOutlineColor = fillColor;
211  else
212  {
213  // use fill color data defined property
214  if ( ddProperties.isActive( QgsSymbolLayer::PropertyFillColor ) )
216  }
217  }
218  else
219  {
220  const QVariant jsonFillOutlineColor = jsonPaint.value( QStringLiteral( "fill-outline-color" ) );
221  switch ( jsonFillOutlineColor.type() )
222  {
223  case QVariant::Map:
224  ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeColor, parseInterpolateColorByZoom( jsonFillOutlineColor.toMap(), context, &fillOutlineColor ) );
225  break;
226 
227  case QVariant::List:
228  case QVariant::StringList:
229  ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeColor, parseValueList( jsonFillOutlineColor.toList(), PropertyType::Color, context, 1, 255, &fillOutlineColor ) );
230  break;
231 
232  case QVariant::String:
233  fillOutlineColor = parseColor( jsonFillOutlineColor.toString(), context );
234  break;
235 
236  default:
237  context.pushWarning( QObject::tr( "%1: Skipping unsupported fill-outline-color type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonFillOutlineColor.type() ) ) );
238  break;
239  }
240  }
241 
242  double fillOpacity = -1.0;
243  double rasterOpacity = -1.0;
244  if ( jsonPaint.contains( QStringLiteral( "fill-opacity" ) ) )
245  {
246  const QVariant jsonFillOpacity = jsonPaint.value( QStringLiteral( "fill-opacity" ) );
247  switch ( jsonFillOpacity.type() )
248  {
249  case QVariant::Int:
250  case QVariant::Double:
251  fillOpacity = jsonFillOpacity.toDouble();
252  rasterOpacity = fillOpacity;
253  break;
254 
255  case QVariant::Map:
256  if ( ddProperties.isActive( QgsSymbolLayer::PropertyFillColor ) )
257  {
258  context.pushWarning( QObject::tr( "%1: Could not set opacity of layer, opacity already defined in fill color" ).arg( context.layerId() ) );
259  }
260  else
261  {
262  ddProperties.setProperty( QgsSymbolLayer::PropertyFillColor, parseInterpolateOpacityByZoom( jsonFillOpacity.toMap(), fillColor.isValid() ? fillColor.alpha() : 255 ) );
263  ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeColor, parseInterpolateOpacityByZoom( jsonFillOpacity.toMap(), fillOutlineColor.isValid() ? fillOutlineColor.alpha() : 255 ) );
264  ddRasterProperties.setProperty( QgsSymbolLayer::PropertyOpacity, parseInterpolateByZoom( jsonFillOpacity.toMap(), context, 100, &rasterOpacity ) );
265  }
266  break;
267 
268  case QVariant::List:
269  case QVariant::StringList:
270  if ( ddProperties.isActive( QgsSymbolLayer::PropertyFillColor ) )
271  {
272  context.pushWarning( QObject::tr( "%1: Could not set opacity of layer, opacity already defined in fill color" ).arg( context.layerId() ) );
273  }
274  else
275  {
276  ddProperties.setProperty( QgsSymbolLayer::PropertyFillColor, parseValueList( jsonFillOpacity.toList(), PropertyType::Opacity, context, 1, fillColor.isValid() ? fillColor.alpha() : 255 ) );
277  ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeColor, parseValueList( jsonFillOpacity.toList(), PropertyType::Opacity, context, 1, fillOutlineColor.isValid() ? fillOutlineColor.alpha() : 255 ) );
278  ddRasterProperties.setProperty( QgsSymbolLayer::PropertyOpacity, parseValueList( jsonFillOpacity.toList(), PropertyType::Numeric, context, 100, 255, nullptr, &rasterOpacity ) );
279  }
280  break;
281 
282  default:
283  context.pushWarning( QObject::tr( "%1: Skipping unsupported fill-opacity type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonFillOpacity.type() ) ) );
284  break;
285  }
286  }
287 
288  // fill-translate
289  QPointF fillTranslate;
290  if ( jsonPaint.contains( QStringLiteral( "fill-translate" ) ) )
291  {
292  const QVariant jsonFillTranslate = jsonPaint.value( QStringLiteral( "fill-translate" ) );
293  switch ( jsonFillTranslate.type() )
294  {
295 
296  case QVariant::Map:
297  ddProperties.setProperty( QgsSymbolLayer::PropertyOffset, parseInterpolatePointByZoom( jsonFillTranslate.toMap(), context, context.pixelSizeConversionFactor(), &fillTranslate ) );
298  break;
299 
300  case QVariant::List:
301  case QVariant::StringList:
302  fillTranslate = QPointF( jsonFillTranslate.toList().value( 0 ).toDouble() * context.pixelSizeConversionFactor(),
303  jsonFillTranslate.toList().value( 1 ).toDouble() * context.pixelSizeConversionFactor() );
304  break;
305 
306  default:
307  context.pushWarning( QObject::tr( "%1: Skipping unsupported fill-translate type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonFillTranslate.type() ) ) );
308  break;
309  }
310  }
311 
312  std::unique_ptr< QgsSymbol > symbol( QgsSymbol::defaultSymbol( QgsWkbTypes::PolygonGeometry ) );
313  QgsSimpleFillSymbolLayer *fillSymbol = dynamic_cast< QgsSimpleFillSymbolLayer * >( symbol->symbolLayer( 0 ) );
314 
315  // set render units
316  symbol->setOutputUnit( context.targetUnit() );
317  fillSymbol->setOutputUnit( context.targetUnit() );
318 
319  if ( !fillTranslate.isNull() )
320  {
321  fillSymbol->setOffset( fillTranslate );
322  }
323  fillSymbol->setOffsetUnit( context.targetUnit() );
324 
325  if ( jsonPaint.contains( QStringLiteral( "fill-pattern" ) ) )
326  {
327  // get fill-pattern to set sprite
328 
329  const QVariant fillPatternJson = jsonPaint.value( QStringLiteral( "fill-pattern" ) );
330 
331  // fill-pattern disabled dillcolor
332  fillColor = QColor();
333  fillOutlineColor = QColor();
334 
335  // fill-pattern can be String or Object
336  // String: {"fill-pattern": "dash-t"}
337  // Object: {"fill-pattern":{"stops":[[11,"wetland8"],[12,"wetland16"]]}}
338 
339  QSize spriteSize;
340  QString spriteProperty, spriteSizeProperty;
341  const QString sprite = retrieveSpriteAsBase64( fillPatternJson, context, spriteSize, spriteProperty, spriteSizeProperty );
342  if ( !sprite.isEmpty() )
343  {
344  // when fill-pattern exists, set and insert QgsRasterFillSymbolLayer
346  rasterFill->setImageFilePath( sprite );
347  rasterFill->setWidth( spriteSize.width() );
348  rasterFill->setWidthUnit( context.targetUnit() );
350 
351  if ( rasterOpacity >= 0 )
352  {
353  rasterFill->setOpacity( rasterOpacity );
354  }
355 
356  if ( !spriteProperty.isEmpty() )
357  {
358  ddRasterProperties.setProperty( QgsSymbolLayer::PropertyFile, QgsProperty::fromExpression( spriteProperty ) );
359  ddRasterProperties.setProperty( QgsSymbolLayer::PropertyWidth, QgsProperty::fromExpression( spriteSizeProperty ) );
360  }
361 
362  rasterFill->setDataDefinedProperties( ddRasterProperties );
363  symbol->appendSymbolLayer( rasterFill );
364  }
365  }
366 
367  fillSymbol->setDataDefinedProperties( ddProperties );
368 
369  if ( fillOpacity != -1 )
370  {
371  symbol->setOpacity( fillOpacity );
372  }
373 
374  if ( fillOutlineColor.isValid() )
375  {
376  fillSymbol->setStrokeColor( fillOutlineColor );
377  }
378  else
379  {
380  fillSymbol->setStrokeStyle( Qt::NoPen );
381  }
382 
383  if ( fillColor.isValid() )
384  {
385  fillSymbol->setFillColor( fillColor );
386  }
387  else
388  {
389  fillSymbol->setBrushStyle( Qt::NoBrush );
390  }
391 
393  style.setSymbol( symbol.release() );
394  return true;
395 }
396 
398 {
399  if ( !jsonLayer.contains( QStringLiteral( "paint" ) ) )
400  {
401  context.pushWarning( QObject::tr( "%1: Style has no paint property, skipping" ).arg( context.layerId() ) );
402  return false;
403  }
404 
405  const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral( "paint" ) ).toMap();
406  if ( jsonPaint.contains( QStringLiteral( "line-pattern" ) ) )
407  {
408  context.pushWarning( QObject::tr( "%1: Skipping unsupported line-pattern property" ).arg( context.layerId() ) );
409  return false;
410  }
411 
412  QgsPropertyCollection ddProperties;
413 
414  // line color
415  QColor lineColor;
416  if ( jsonPaint.contains( QStringLiteral( "line-color" ) ) )
417  {
418  const QVariant jsonLineColor = jsonPaint.value( QStringLiteral( "line-color" ) );
419  switch ( jsonLineColor.type() )
420  {
421  case QVariant::Map:
422  ddProperties.setProperty( QgsSymbolLayer::PropertyFillColor, parseInterpolateColorByZoom( jsonLineColor.toMap(), context, &lineColor ) );
424  break;
425 
426  case QVariant::List:
427  case QVariant::StringList:
428  ddProperties.setProperty( QgsSymbolLayer::PropertyFillColor, parseValueList( jsonLineColor.toList(), PropertyType::Color, context, 1, 255, &lineColor ) );
430  break;
431 
432  case QVariant::String:
433  lineColor = parseColor( jsonLineColor.toString(), context );
434  break;
435 
436  default:
437  context.pushWarning( QObject::tr( "%1: Skipping unsupported line-color type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonLineColor.type() ) ) );
438  break;
439  }
440  }
441  else
442  {
443  // defaults to #000000
444  lineColor = QColor( 0, 0, 0 );
445  }
446 
447 
448  double lineWidth = 1.0;
449  if ( jsonPaint.contains( QStringLiteral( "line-width" ) ) )
450  {
451  const QVariant jsonLineWidth = jsonPaint.value( QStringLiteral( "line-width" ) );
452  switch ( jsonLineWidth.type() )
453  {
454  case QVariant::Int:
455  case QVariant::Double:
456  lineWidth = jsonLineWidth.toDouble() * context.pixelSizeConversionFactor();
457  break;
458 
459  case QVariant::Map:
460  lineWidth = -1;
461  ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeWidth, parseInterpolateByZoom( jsonLineWidth.toMap(), context, context.pixelSizeConversionFactor(), &lineWidth ) );
462  break;
463 
464  case QVariant::List:
465  case QVariant::StringList:
466  ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeWidth, parseValueList( jsonLineWidth.toList(), PropertyType::Numeric, context, context.pixelSizeConversionFactor(), 255, nullptr, &lineWidth ) );
467  break;
468 
469  default:
470  context.pushWarning( QObject::tr( "%1: Skipping unsupported fill-width type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonLineWidth.type() ) ) );
471  break;
472  }
473  }
474 
475  double lineOffset = 0.0;
476  if ( jsonPaint.contains( QStringLiteral( "line-offset" ) ) )
477  {
478  const QVariant jsonLineOffset = jsonPaint.value( QStringLiteral( "line-offset" ) );
479  switch ( jsonLineOffset.type() )
480  {
481  case QVariant::Int:
482  case QVariant::Double:
483  lineOffset = -jsonLineOffset.toDouble() * context.pixelSizeConversionFactor();
484  break;
485 
486  case QVariant::Map:
487  lineWidth = -1;
488  ddProperties.setProperty( QgsSymbolLayer::PropertyOffset, parseInterpolateByZoom( jsonLineOffset.toMap(), context, context.pixelSizeConversionFactor() * -1, &lineOffset ) );
489  break;
490 
491  case QVariant::List:
492  case QVariant::StringList:
493  ddProperties.setProperty( QgsSymbolLayer::PropertyOffset, parseValueList( jsonLineOffset.toList(), PropertyType::Numeric, context, context.pixelSizeConversionFactor() * -1, 255, nullptr, &lineOffset ) );
494  break;
495 
496  default:
497  context.pushWarning( QObject::tr( "%1: Skipping unsupported line-offset type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonLineOffset.type() ) ) );
498  break;
499  }
500  }
501 
502  double lineOpacity = -1.0;
503  if ( jsonPaint.contains( QStringLiteral( "line-opacity" ) ) )
504  {
505  const QVariant jsonLineOpacity = jsonPaint.value( QStringLiteral( "line-opacity" ) );
506  switch ( jsonLineOpacity.type() )
507  {
508  case QVariant::Int:
509  case QVariant::Double:
510  lineOpacity = jsonLineOpacity.toDouble();
511  break;
512 
513  case QVariant::Map:
514  if ( ddProperties.isActive( QgsSymbolLayer::PropertyStrokeColor ) )
515  {
516  context.pushWarning( QObject::tr( "%1: Could not set opacity of layer, opacity already defined in stroke color" ).arg( context.layerId() ) );
517  }
518  else
519  {
520  ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeColor, parseInterpolateOpacityByZoom( jsonLineOpacity.toMap(), lineColor.isValid() ? lineColor.alpha() : 255 ) );
521  }
522  break;
523 
524  case QVariant::List:
525  case QVariant::StringList:
526  if ( ddProperties.isActive( QgsSymbolLayer::PropertyStrokeColor ) )
527  {
528  context.pushWarning( QObject::tr( "%1: Could not set opacity of layer, opacity already defined in stroke color" ).arg( context.layerId() ) );
529  }
530  else
531  {
532  ddProperties.setProperty( QgsSymbolLayer::PropertyStrokeColor, parseValueList( jsonLineOpacity.toList(), PropertyType::Opacity, context, 1, lineColor.isValid() ? lineColor.alpha() : 255 ) );
533  }
534  break;
535 
536  default:
537  context.pushWarning( QObject::tr( "%1: Skipping unsupported line-opacity type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonLineOpacity.type() ) ) );
538  break;
539  }
540  }
541 
542  QVector< double > dashVector;
543  if ( jsonPaint.contains( QStringLiteral( "line-dasharray" ) ) )
544  {
545  const QVariant jsonLineDashArray = jsonPaint.value( QStringLiteral( "line-dasharray" ) );
546  switch ( jsonLineDashArray.type() )
547  {
548  case QVariant::Map:
549  {
550  //TODO improve parsing (use PropertyCustomDash?)
551  const QVariantList dashSource = jsonLineDashArray.toMap().value( QStringLiteral( "stops" ) ).toList().last().toList().value( 1 ).toList();
552  for ( const QVariant &v : dashSource )
553  {
554  dashVector << v.toDouble() * context.pixelSizeConversionFactor();
555  }
556  break;
557  }
558 
559  case QVariant::List:
560  case QVariant::StringList:
561  {
562  const QVariantList dashSource = jsonLineDashArray.toList();
563  for ( const QVariant &v : dashSource )
564  {
565  dashVector << v.toDouble() * context.pixelSizeConversionFactor();
566  }
567  break;
568  }
569 
570  default:
571  context.pushWarning( QObject::tr( "%1: Skipping unsupported line-dasharray type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonLineDashArray.type() ) ) );
572  break;
573  }
574  }
575 
576  Qt::PenCapStyle penCapStyle = Qt::FlatCap;
577  Qt::PenJoinStyle penJoinStyle = Qt::MiterJoin;
578  if ( jsonLayer.contains( QStringLiteral( "layout" ) ) )
579  {
580  const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral( "layout" ) ).toMap();
581  if ( jsonLayout.contains( QStringLiteral( "line-cap" ) ) )
582  {
583  penCapStyle = parseCapStyle( jsonLayout.value( QStringLiteral( "line-cap" ) ).toString() );
584  }
585  if ( jsonLayout.contains( QStringLiteral( "line-join" ) ) )
586  {
587  penJoinStyle = parseJoinStyle( jsonLayout.value( QStringLiteral( "line-join" ) ).toString() );
588  }
589  }
590 
591  std::unique_ptr< QgsSymbol > symbol( QgsSymbol::defaultSymbol( QgsWkbTypes::LineGeometry ) );
592  QgsSimpleLineSymbolLayer *lineSymbol = dynamic_cast< QgsSimpleLineSymbolLayer * >( symbol->symbolLayer( 0 ) );
593 
594  // set render units
595  symbol->setOutputUnit( context.targetUnit() );
596  lineSymbol->setOutputUnit( context.targetUnit() );
597  lineSymbol->setPenCapStyle( penCapStyle );
598  lineSymbol->setPenJoinStyle( penJoinStyle );
599  lineSymbol->setDataDefinedProperties( ddProperties );
600  lineSymbol->setOffset( lineOffset );
601  lineSymbol->setOffsetUnit( context.targetUnit() );
602 
603  if ( lineOpacity != -1 )
604  {
605  symbol->setOpacity( lineOpacity );
606  }
607  if ( lineColor.isValid() )
608  {
609  lineSymbol->setColor( lineColor );
610  }
611  if ( lineWidth != -1 )
612  {
613  lineSymbol->setWidth( lineWidth );
614  }
615  if ( !dashVector.empty() )
616  {
617  lineSymbol->setUseCustomDashPattern( true );
618  lineSymbol->setCustomDashVector( dashVector );
619  }
620 
622  style.setSymbol( symbol.release() );
623  return true;
624 }
625 
626 void QgsMapBoxGlStyleConverter::parseSymbolLayer( const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &renderer, bool &hasRenderer, QgsVectorTileBasicLabelingStyle &labelingStyle, bool &hasLabeling, QgsMapBoxGlStyleConversionContext &context )
627 {
628  hasLabeling = false;
629  hasRenderer = false;
630 
631  if ( !jsonLayer.contains( QStringLiteral( "layout" ) ) )
632  {
633  context.pushWarning( QObject::tr( "%1: Style layer has no layout property, skipping" ).arg( context.layerId() ) );
634  return;
635  }
636  const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral( "layout" ) ).toMap();
637  if ( !jsonLayout.contains( QStringLiteral( "text-field" ) ) )
638  {
639  hasRenderer = parseSymbolLayerAsRenderer( jsonLayer, renderer, context );
640  return;
641  }
642 
643  if ( !jsonLayer.contains( QStringLiteral( "paint" ) ) )
644  {
645  context.pushWarning( QObject::tr( "%1: Style layer has no paint property, skipping" ).arg( context.layerId() ) );
646  return;
647  }
648  const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral( "paint" ) ).toMap();
649 
650  QgsPropertyCollection ddLabelProperties;
651 
652  double textSize = 16.0 * context.pixelSizeConversionFactor();
653  QString textSizeProperty;
654  if ( jsonLayout.contains( QStringLiteral( "text-size" ) ) )
655  {
656  const QVariant jsonTextSize = jsonLayout.value( QStringLiteral( "text-size" ) );
657  switch ( jsonTextSize.type() )
658  {
659  case QVariant::Int:
660  case QVariant::Double:
661  textSize = jsonTextSize.toDouble() * context.pixelSizeConversionFactor();
662  break;
663 
664  case QVariant::Map:
665  textSize = -1;
666  textSizeProperty = parseInterpolateByZoom( jsonTextSize.toMap(), context, context.pixelSizeConversionFactor(), &textSize );
667 
668  break;
669 
670  case QVariant::List:
671  case QVariant::StringList:
672  textSize = -1;
673  textSizeProperty = parseValueList( jsonTextSize.toList(), PropertyType::Numeric, context, context.pixelSizeConversionFactor(), 255, nullptr, &textSize );
674  break;
675 
676  default:
677  context.pushWarning( QObject::tr( "%1: Skipping unsupported text-size type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonTextSize.type() ) ) );
678  break;
679  }
680 
681  if ( !textSizeProperty.isEmpty() )
682  {
683  ddLabelProperties.setProperty( QgsPalLayerSettings::Size, textSizeProperty );
684  }
685  }
686 
687  // a rough average of ems to character count conversion for a variety of fonts
688  constexpr double EM_TO_CHARS = 2.0;
689 
690  double textMaxWidth = -1;
691  if ( jsonLayout.contains( QStringLiteral( "text-max-width" ) ) )
692  {
693  const QVariant jsonTextMaxWidth = jsonLayout.value( QStringLiteral( "text-max-width" ) );
694  switch ( jsonTextMaxWidth.type() )
695  {
696  case QVariant::Int:
697  case QVariant::Double:
698  textMaxWidth = jsonTextMaxWidth.toDouble() * EM_TO_CHARS;
699  break;
700 
701  case QVariant::Map:
702  ddLabelProperties.setProperty( QgsPalLayerSettings::AutoWrapLength, parseInterpolateByZoom( jsonTextMaxWidth.toMap(), context, EM_TO_CHARS, &textMaxWidth ) );
703  break;
704 
705  case QVariant::List:
706  case QVariant::StringList:
707  ddLabelProperties.setProperty( QgsPalLayerSettings::AutoWrapLength, parseValueList( jsonTextMaxWidth.toList(), PropertyType::Numeric, context, EM_TO_CHARS, 255, nullptr, &textMaxWidth ) );
708  break;
709 
710  default:
711  context.pushWarning( QObject::tr( "%1: Skipping unsupported text-max-width type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonTextMaxWidth.type() ) ) );
712  break;
713  }
714  }
715  else
716  {
717  // defaults to 10
718  textMaxWidth = 10 * EM_TO_CHARS;
719  }
720 
721  double textLetterSpacing = -1;
722  if ( jsonLayout.contains( QStringLiteral( "text-letter-spacing" ) ) )
723  {
724  const QVariant jsonTextLetterSpacing = jsonLayout.value( QStringLiteral( "text-letter-spacing" ) );
725  switch ( jsonTextLetterSpacing.type() )
726  {
727  case QVariant::Int:
728  case QVariant::Double:
729  textLetterSpacing = jsonTextLetterSpacing.toDouble();
730  break;
731 
732  case QVariant::Map:
733  ddLabelProperties.setProperty( QgsPalLayerSettings::FontLetterSpacing, parseInterpolateByZoom( jsonTextLetterSpacing.toMap(), context, 1, &textLetterSpacing ) );
734  break;
735 
736  case QVariant::List:
737  case QVariant::StringList:
738  ddLabelProperties.setProperty( QgsPalLayerSettings::FontLetterSpacing, parseValueList( jsonTextLetterSpacing.toList(), PropertyType::Numeric, context, 1, 255, nullptr, &textLetterSpacing ) );
739  break;
740 
741  default:
742  context.pushWarning( QObject::tr( "%1: Skipping unsupported text-letter-spacing type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonTextLetterSpacing.type() ) ) );
743  break;
744  }
745  }
746 
747  QFont textFont;
748  bool foundFont = false;
749  QString fontName;
750  if ( jsonLayout.contains( QStringLiteral( "text-font" ) ) )
751  {
752  auto splitFontFamily = []( const QString & fontName, QString & family, QString & style ) -> bool
753  {
754  const QStringList textFontParts = fontName.split( ' ' );
755  for ( int i = 1; i < textFontParts.size(); ++i )
756  {
757  const QString candidateFontName = textFontParts.mid( 0, i ).join( ' ' );
758  const QString candidateFontStyle = textFontParts.mid( i ).join( ' ' );
759  if ( QgsFontUtils::fontFamilyHasStyle( candidateFontName, candidateFontStyle ) )
760  {
761  family = candidateFontName;
762  style = candidateFontStyle;
763  return true;
764  }
765  }
766 
767  if ( QFontDatabase().hasFamily( fontName ) )
768  {
769  // the json isn't following the spec correctly!!
770  family = fontName;
771  style.clear();
772  return true;
773  }
774  return false;
775  };
776 
777  const QVariant jsonTextFont = jsonLayout.value( QStringLiteral( "text-font" ) );
778  if ( jsonTextFont.type() != QVariant::List && jsonTextFont.type() != QVariant::StringList && jsonTextFont.type() != QVariant::String
779  && jsonTextFont.type() != QVariant::Map )
780  {
781  context.pushWarning( QObject::tr( "%1: Skipping unsupported text-font type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonTextFont.type() ) ) );
782  }
783  else
784  {
785  switch ( jsonTextFont.type() )
786  {
787  case QVariant::List:
788  case QVariant::StringList:
789  fontName = jsonTextFont.toList().value( 0 ).toString();
790  break;
791 
792  case QVariant::String:
793  fontName = jsonTextFont.toString();
794  break;
795 
796  case QVariant::Map:
797  {
798  QString familyCaseString = QStringLiteral( "CASE " );
799  QString styleCaseString = QStringLiteral( "CASE " );
800  QString fontFamily;
801  QString fontStyle;
802  const QVariantList stops = jsonTextFont.toMap().value( QStringLiteral( "stops" ) ).toList();
803 
804  bool error = false;
805  for ( int i = 0; i < stops.length() - 1; ++i )
806  {
807  // bottom zoom and value
808  const QVariant bz = stops.value( i ).toList().value( 0 );
809  const QString bv = stops.value( i ).toList().value( 1 ).type() == QVariant::String ? stops.value( i ).toList().value( 1 ).toString() : stops.value( i ).toList().value( 1 ).toList().value( 0 ).toString();
810  if ( bz.type() == QVariant::List || bz.type() == QVariant::StringList )
811  {
812  context.pushWarning( QObject::tr( "%1: Expressions in interpolation function are not supported, skipping." ).arg( context.layerId() ) );
813  error = true;
814  break;
815  }
816 
817  // top zoom
818  const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
819  if ( tz.type() == QVariant::List || tz.type() == QVariant::StringList )
820  {
821  context.pushWarning( QObject::tr( "%1: Expressions in interpolation function are not supported, skipping." ).arg( context.layerId() ) );
822  error = true;
823  break;
824  }
825 
826  if ( splitFontFamily( bv, fontFamily, fontStyle ) )
827  {
828  familyCaseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
829  "THEN %3 " ).arg( bz.toString(),
830  tz.toString(),
831  QgsExpression::quotedValue( fontFamily ) );
832  styleCaseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
833  "THEN %3 " ).arg( bz.toString(),
834  tz.toString(),
835  QgsExpression::quotedValue( fontStyle ) );
836  }
837  else
838  {
839  context.pushWarning( QObject::tr( "%1: Referenced font %2 is not available on system" ).arg( context.layerId(), bv ) );
840  }
841  }
842  if ( error )
843  break;
844 
845  const QString bv = stops.constLast().toList().value( 1 ).type() == QVariant::String ? stops.constLast().toList().value( 1 ).toString() : stops.constLast().toList().value( 1 ).toList().value( 0 ).toString();
846  if ( splitFontFamily( bv, fontFamily, fontStyle ) )
847  {
848  familyCaseString += QStringLiteral( "ELSE %1 END" ).arg( QgsExpression::quotedValue( fontFamily ) );
849  styleCaseString += QStringLiteral( "ELSE %1 END" ).arg( QgsExpression::quotedValue( fontStyle ) );
850  }
851  else
852  {
853  context.pushWarning( QObject::tr( "%1: Referenced font %2 is not available on system" ).arg( context.layerId(), bv ) );
854  }
855 
856  ddLabelProperties.setProperty( QgsPalLayerSettings::Family, QgsProperty::fromExpression( familyCaseString ) );
857  ddLabelProperties.setProperty( QgsPalLayerSettings::FontStyle, QgsProperty::fromExpression( styleCaseString ) );
858 
859  foundFont = true;
860  fontName = fontFamily;
861 
862  break;
863  }
864 
865  default:
866  break;
867  }
868 
869  QString fontFamily;
870  QString fontStyle;
871  if ( splitFontFamily( fontName, fontFamily, fontStyle ) )
872  {
873  textFont = QFont( fontFamily );
874  if ( !fontStyle.isEmpty() )
875  textFont.setStyleName( fontStyle );
876  foundFont = true;
877  }
878  }
879  }
880  else
881  {
882  // Defaults to ["Open Sans Regular","Arial Unicode MS Regular"].
883  if ( QgsFontUtils::fontFamilyHasStyle( QStringLiteral( "Open Sans" ), QStringLiteral( "Regular" ) ) )
884  {
885  fontName = QStringLiteral( "Open Sans" );
886  textFont = QFont( fontName );
887  textFont.setStyleName( QStringLiteral( "Regular" ) );
888  foundFont = true;
889  }
890  else if ( QgsFontUtils::fontFamilyHasStyle( QStringLiteral( "Arial Unicode MS" ), QStringLiteral( "Regular" ) ) )
891  {
892  fontName = QStringLiteral( "Arial Unicode MS" );
893  textFont = QFont( fontName );
894  textFont.setStyleName( QStringLiteral( "Regular" ) );
895  foundFont = true;
896  }
897  else
898  {
899  fontName = QStringLiteral( "Open Sans, Arial Unicode MS" );
900  }
901  }
902  if ( !foundFont && !fontName.isEmpty() )
903  {
904  context.pushWarning( QObject::tr( "%1: Referenced font %2 is not available on system" ).arg( context.layerId(), fontName ) );
905  }
906 
907  // text color
908  QColor textColor;
909  if ( jsonPaint.contains( QStringLiteral( "text-color" ) ) )
910  {
911  const QVariant jsonTextColor = jsonPaint.value( QStringLiteral( "text-color" ) );
912  switch ( jsonTextColor.type() )
913  {
914  case QVariant::Map:
915  ddLabelProperties.setProperty( QgsPalLayerSettings::Color, parseInterpolateColorByZoom( jsonTextColor.toMap(), context, &textColor ) );
916  break;
917 
918  case QVariant::List:
919  case QVariant::StringList:
920  ddLabelProperties.setProperty( QgsPalLayerSettings::Color, parseValueList( jsonTextColor.toList(), PropertyType::Color, context, 1, 255, &textColor ) );
921  break;
922 
923  case QVariant::String:
924  textColor = parseColor( jsonTextColor.toString(), context );
925  break;
926 
927  default:
928  context.pushWarning( QObject::tr( "%1: Skipping unsupported text-color type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonTextColor.type() ) ) );
929  break;
930  }
931  }
932  else
933  {
934  // defaults to #000000
935  textColor = QColor( 0, 0, 0 );
936  }
937 
938  // buffer color
939  QColor bufferColor;
940  if ( jsonPaint.contains( QStringLiteral( "text-halo-color" ) ) )
941  {
942  const QVariant jsonBufferColor = jsonPaint.value( QStringLiteral( "text-halo-color" ) );
943  switch ( jsonBufferColor.type() )
944  {
945  case QVariant::Map:
946  ddLabelProperties.setProperty( QgsPalLayerSettings::BufferColor, parseInterpolateColorByZoom( jsonBufferColor.toMap(), context, &bufferColor ) );
947  break;
948 
949  case QVariant::List:
950  case QVariant::StringList:
951  ddLabelProperties.setProperty( QgsPalLayerSettings::BufferColor, parseValueList( jsonBufferColor.toList(), PropertyType::Color, context, 1, 255, &bufferColor ) );
952  break;
953 
954  case QVariant::String:
955  bufferColor = parseColor( jsonBufferColor.toString(), context );
956  break;
957 
958  default:
959  context.pushWarning( QObject::tr( "%1: Skipping unsupported text-halo-color type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonBufferColor.type() ) ) );
960  break;
961  }
962  }
963 
964  double bufferSize = 0.0;
965  // the pixel based text buffers appear larger when rendered on the web - so automatically scale
966  // them up when converting to a QGIS style
967  // (this number is based on trial-and-error comparisons only!)
968  constexpr double BUFFER_SIZE_SCALE = 2.0;
969  if ( jsonPaint.contains( QStringLiteral( "text-halo-width" ) ) )
970  {
971  const QVariant jsonHaloWidth = jsonPaint.value( QStringLiteral( "text-halo-width" ) );
972  switch ( jsonHaloWidth.type() )
973  {
974  case QVariant::Int:
975  case QVariant::Double:
976  bufferSize = jsonHaloWidth.toDouble() * context.pixelSizeConversionFactor() * BUFFER_SIZE_SCALE;
977  break;
978 
979  case QVariant::Map:
980  bufferSize = 1;
981  ddLabelProperties.setProperty( QgsPalLayerSettings::BufferSize, parseInterpolateByZoom( jsonHaloWidth.toMap(), context, context.pixelSizeConversionFactor() * BUFFER_SIZE_SCALE, &bufferSize ) );
982  break;
983 
984  case QVariant::List:
985  case QVariant::StringList:
986  bufferSize = 1;
987  ddLabelProperties.setProperty( QgsPalLayerSettings::BufferSize, parseValueList( jsonHaloWidth.toList(), PropertyType::Numeric, context, context.pixelSizeConversionFactor() * BUFFER_SIZE_SCALE, 255, nullptr, &bufferSize ) );
988  break;
989 
990  default:
991  context.pushWarning( QObject::tr( "%1: Skipping unsupported text-halo-width type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonHaloWidth.type() ) ) );
992  break;
993  }
994  }
995 
996  double haloBlurSize = 0;
997  if ( jsonPaint.contains( QStringLiteral( "text-halo-blur" ) ) )
998  {
999  const QVariant jsonTextHaloBlur = jsonPaint.value( QStringLiteral( "text-halo-blur" ) );
1000  switch ( jsonTextHaloBlur.type() )
1001  {
1002  case QVariant::Int:
1003  case QVariant::Double:
1004  {
1005  haloBlurSize = jsonTextHaloBlur.toDouble() * context.pixelSizeConversionFactor();
1006  break;
1007  }
1008 
1009  default:
1010  context.pushWarning( QObject::tr( "%1: Skipping unsupported text-halo-blur type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonTextHaloBlur.type() ) ) );
1011  break;
1012  }
1013  }
1014 
1015  QgsTextFormat format;
1016  format.setSizeUnit( context.targetUnit() );
1017  if ( textColor.isValid() )
1018  format.setColor( textColor );
1019  if ( textSize >= 0 )
1020  format.setSize( textSize );
1021  if ( foundFont )
1022  format.setFont( textFont );
1023  if ( textLetterSpacing > 0 )
1024  {
1025  QFont f = format.font();
1026  f.setLetterSpacing( QFont::AbsoluteSpacing, textLetterSpacing );
1027  format.setFont( f );
1028  }
1029 
1030  if ( bufferSize > 0 )
1031  {
1032  format.buffer().setEnabled( true );
1033  format.buffer().setSize( bufferSize );
1034  format.buffer().setSizeUnit( context.targetUnit() );
1035  format.buffer().setColor( bufferColor );
1036 
1037  if ( haloBlurSize > 0 )
1038  {
1039  QgsEffectStack *stack = new QgsEffectStack();
1040  QgsBlurEffect *blur = new QgsBlurEffect() ;
1041  blur->setEnabled( true );
1042  blur->setBlurUnit( context.targetUnit() );
1043  blur->setBlurLevel( haloBlurSize );
1045  stack->appendEffect( blur );
1046  stack->setEnabled( true );
1047  format.buffer().setPaintEffect( stack );
1048  }
1049  }
1050 
1051  QgsPalLayerSettings labelSettings;
1052 
1053  if ( textMaxWidth > 0 )
1054  {
1055  labelSettings.autoWrapLength = textMaxWidth;
1056  }
1057 
1058  // convert field name
1059 
1060  auto processLabelField = []( const QString & string, bool & isExpression )->QString
1061  {
1062  // {field_name} is permitted in string -- if multiple fields are present, convert them to an expression
1063  // but if single field is covered in {}, return it directly
1064  const QRegularExpression singleFieldRx( QStringLiteral( "^{([^}]+)}$" ) );
1065  QRegularExpressionMatch match = singleFieldRx.match( string );
1066  if ( match.hasMatch() )
1067  {
1068  isExpression = false;
1069  return match.captured( 1 );
1070  }
1071 
1072  const QRegularExpression multiFieldRx( QStringLiteral( "(?={[^}]+})" ) );
1073  const QStringList parts = string.split( multiFieldRx );
1074  if ( parts.size() > 1 )
1075  {
1076  isExpression = true;
1077 
1078  QStringList res;
1079  for ( const QString &part : parts )
1080  {
1081  if ( part.isEmpty() )
1082  continue;
1083 
1084  // part will start at a {field} reference
1085  const QStringList split = part.split( '}' );
1086  res << QgsExpression::quotedColumnRef( split.at( 0 ).mid( 1 ) );
1087  if ( !split.at( 1 ).isEmpty() )
1088  res << QgsExpression::quotedValue( split.at( 1 ) );
1089  }
1090  return QStringLiteral( "concat(%1)" ).arg( res.join( ',' ) );
1091  }
1092  else
1093  {
1094  isExpression = false;
1095  return string;
1096  }
1097  };
1098 
1099  if ( jsonLayout.contains( QStringLiteral( "text-field" ) ) )
1100  {
1101  const QVariant jsonTextField = jsonLayout.value( QStringLiteral( "text-field" ) );
1102  switch ( jsonTextField.type() )
1103  {
1104  case QVariant::String:
1105  {
1106  labelSettings.fieldName = processLabelField( jsonTextField.toString(), labelSettings.isExpression );
1107  break;
1108  }
1109 
1110  case QVariant::List:
1111  case QVariant::StringList:
1112  {
1113  const QVariantList textFieldList = jsonTextField.toList();
1114  /*
1115  * e.g.
1116  * "text-field": ["format",
1117  * "foo", { "font-scale": 1.2 },
1118  * "bar", { "font-scale": 0.8 }
1119  * ]
1120  */
1121  if ( textFieldList.size() > 2 && textFieldList.at( 0 ).toString() == QLatin1String( "format" ) )
1122  {
1123  QStringList parts;
1124  for ( int i = 1; i < textFieldList.size(); ++i )
1125  {
1126  bool isExpression = false;
1127  const QString part = processLabelField( textFieldList.at( i ).toString(), isExpression );
1128  if ( !isExpression )
1129  parts << QgsExpression::quotedColumnRef( part );
1130  else
1131  parts << part;
1132  // TODO -- we could also translate font color, underline, overline, strikethrough to HTML tags!
1133  i += 1;
1134  }
1135  labelSettings.fieldName = QStringLiteral( "concat(%1)" ).arg( parts.join( ',' ) );
1136  labelSettings.isExpression = true;
1137  }
1138  else
1139  {
1140  /*
1141  * e.g.
1142  * "text-field": ["to-string", ["get", "name"]]
1143  */
1144  labelSettings.fieldName = parseExpression( textFieldList, context );
1145  labelSettings.isExpression = true;
1146  }
1147  break;
1148  }
1149 
1150  default:
1151  context.pushWarning( QObject::tr( "%1: Skipping unsupported text-field type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonTextField.type() ) ) );
1152  break;
1153  }
1154  }
1155 
1156  if ( jsonLayout.contains( QStringLiteral( "text-transform" ) ) )
1157  {
1158  const QString textTransform = jsonLayout.value( QStringLiteral( "text-transform" ) ).toString();
1159  if ( textTransform == QLatin1String( "uppercase" ) )
1160  {
1161  labelSettings.fieldName = QStringLiteral( "upper(%1)" ).arg( labelSettings.isExpression ? labelSettings.fieldName : QgsExpression::quotedColumnRef( labelSettings.fieldName ) );
1162  }
1163  else if ( textTransform == QLatin1String( "lowercase" ) )
1164  {
1165  labelSettings.fieldName = QStringLiteral( "lower(%1)" ).arg( labelSettings.isExpression ? labelSettings.fieldName : QgsExpression::quotedColumnRef( labelSettings.fieldName ) );
1166  }
1167  labelSettings.isExpression = true;
1168  }
1169 
1170  labelSettings.placement = QgsPalLayerSettings::OverPoint;
1172  if ( jsonLayout.contains( QStringLiteral( "symbol-placement" ) ) )
1173  {
1174  const QString symbolPlacement = jsonLayout.value( QStringLiteral( "symbol-placement" ) ).toString();
1175  if ( symbolPlacement == QLatin1String( "line" ) )
1176  {
1177  labelSettings.placement = QgsPalLayerSettings::Curved;
1179  geometryType = QgsWkbTypes::LineGeometry;
1180 
1181  if ( jsonLayout.contains( QStringLiteral( "text-rotation-alignment" ) ) )
1182  {
1183  const QString textRotationAlignment = jsonLayout.value( QStringLiteral( "text-rotation-alignment" ) ).toString();
1184  if ( textRotationAlignment == QLatin1String( "viewport" ) )
1185  {
1187  }
1188  }
1189 
1190  if ( labelSettings.placement == QgsPalLayerSettings::Curved )
1191  {
1192  QPointF textOffset;
1193  QString textOffsetProperty;
1194  if ( jsonLayout.contains( QStringLiteral( "text-offset" ) ) )
1195  {
1196  const QVariant jsonTextOffset = jsonLayout.value( QStringLiteral( "text-offset" ) );
1197 
1198  // units are ems!
1199  switch ( jsonTextOffset.type() )
1200  {
1201  case QVariant::Map:
1202  textOffsetProperty = parseInterpolatePointByZoom( jsonTextOffset.toMap(), context, textSizeProperty.isEmpty() ? textSize : 1.0, &textOffset );
1203  if ( textSizeProperty.isEmpty() )
1204  {
1205  ddLabelProperties.setProperty( QgsPalLayerSettings::LabelDistance, QStringLiteral( "abs(array_get(%1,1))-%2" ).arg( textOffsetProperty ).arg( textSize ) );
1206  }
1207  else
1208  {
1209  ddLabelProperties.setProperty( QgsPalLayerSettings::LabelDistance, QStringLiteral( "with_variable('text_size',%2,abs(array_get(%1,1))*@text_size-@text_size)" ).arg( textOffsetProperty ).arg( textSizeProperty ) );
1210  }
1211  ddLabelProperties.setProperty( QgsPalLayerSettings::LinePlacementOptions, QStringLiteral( "if(array_get(%1,1)>0,'BL','AL')" ).arg( textOffsetProperty ) );
1212  break;
1213 
1214  case QVariant::List:
1215  case QVariant::StringList:
1216  textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1217  jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1218  break;
1219 
1220  default:
1221  context.pushWarning( QObject::tr( "%1: Skipping unsupported text-offset type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonTextOffset.type() ) ) );
1222  break;
1223  }
1224 
1225  if ( !textOffset.isNull() )
1226  {
1227  labelSettings.distUnits = context.targetUnit();
1228  labelSettings.dist = std::abs( textOffset.y() ) - textSize;
1229  labelSettings.lineSettings().setPlacementFlags( textOffset.y() > 0.0 ? QgsLabeling::BelowLine : QgsLabeling::AboveLine );
1230  if ( !textSizeProperty.isEmpty() && textOffsetProperty.isEmpty() )
1231  {
1232  ddLabelProperties.setProperty( QgsPalLayerSettings::LabelDistance, QStringLiteral( "with_variable('text_size',%2,%1*@text_size-@text_size)" ).arg( std::abs( textOffset.y() / textSize ) ).arg( ( textSizeProperty ) ) );
1233  }
1234  }
1235  }
1236 
1237  if ( textOffset.isNull() )
1238  {
1240  }
1241  }
1242  }
1243  }
1244 
1245  if ( jsonLayout.contains( QStringLiteral( "text-justify" ) ) )
1246  {
1247  const QVariant jsonTextJustify = jsonLayout.value( QStringLiteral( "text-justify" ) );
1248 
1249  // default is center
1250  QString textAlign = QStringLiteral( "center" );
1251 
1252  const QVariantMap conversionMap
1253  {
1254  { QStringLiteral( "left" ), QStringLiteral( "left" ) },
1255  { QStringLiteral( "center" ), QStringLiteral( "center" ) },
1256  { QStringLiteral( "right" ), QStringLiteral( "right" ) },
1257  { QStringLiteral( "auto" ), QStringLiteral( "follow" ) }
1258  };
1259 
1260  switch ( jsonTextJustify.type() )
1261  {
1262  case QVariant::String:
1263  textAlign = jsonTextJustify.toString();
1264  break;
1265 
1266  case QVariant::List:
1267  ddLabelProperties.setProperty( QgsPalLayerSettings::OffsetQuad, QgsProperty::fromExpression( parseStringStops( jsonTextJustify.toList(), context, conversionMap, &textAlign ) ) );
1268  break;
1269 
1270  case QVariant::Map:
1271  ddLabelProperties.setProperty( QgsPalLayerSettings::OffsetQuad, parseInterpolateStringByZoom( jsonTextJustify.toMap(), context, conversionMap, &textAlign ) );
1272  break;
1273 
1274  default:
1275  context.pushWarning( QObject::tr( "%1: Skipping unsupported text-justify type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonTextJustify.type() ) ) );
1276  break;
1277  }
1278 
1279  if ( textAlign == QLatin1String( "left" ) )
1281  else if ( textAlign == QLatin1String( "right" ) )
1283  else if ( textAlign == QLatin1String( "center" ) )
1285  else if ( textAlign == QLatin1String( "follow" ) )
1287  }
1288  else
1289  {
1291  }
1292 
1293  if ( labelSettings.placement == QgsPalLayerSettings::OverPoint )
1294  {
1295  if ( jsonLayout.contains( QStringLiteral( "text-anchor" ) ) )
1296  {
1297  const QVariant jsonTextAnchor = jsonLayout.value( QStringLiteral( "text-anchor" ) );
1298  QString textAnchor;
1299 
1300  const QVariantMap conversionMap
1301  {
1302  { QStringLiteral( "center" ), 4 },
1303  { QStringLiteral( "left" ), 5 },
1304  { QStringLiteral( "right" ), 3 },
1305  { QStringLiteral( "top" ), 7 },
1306  { QStringLiteral( "bottom" ), 1 },
1307  { QStringLiteral( "top-left" ), 8 },
1308  { QStringLiteral( "top-right" ), 6 },
1309  { QStringLiteral( "bottom-left" ), 2 },
1310  { QStringLiteral( "bottom-right" ), 0 },
1311  };
1312 
1313  switch ( jsonTextAnchor.type() )
1314  {
1315  case QVariant::String:
1316  textAnchor = jsonTextAnchor.toString();
1317  break;
1318 
1319  case QVariant::List:
1320  ddLabelProperties.setProperty( QgsPalLayerSettings::OffsetQuad, QgsProperty::fromExpression( parseStringStops( jsonTextAnchor.toList(), context, conversionMap, &textAnchor ) ) );
1321  break;
1322 
1323  case QVariant::Map:
1324  ddLabelProperties.setProperty( QgsPalLayerSettings::OffsetQuad, parseInterpolateStringByZoom( jsonTextAnchor.toMap(), context, conversionMap, &textAnchor ) );
1325  break;
1326 
1327  default:
1328  context.pushWarning( QObject::tr( "%1: Skipping unsupported text-anchor type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonTextAnchor.type() ) ) );
1329  break;
1330  }
1331 
1332  if ( textAnchor == QLatin1String( "center" ) )
1334  else if ( textAnchor == QLatin1String( "left" ) )
1336  else if ( textAnchor == QLatin1String( "right" ) )
1338  else if ( textAnchor == QLatin1String( "top" ) )
1340  else if ( textAnchor == QLatin1String( "bottom" ) )
1342  else if ( textAnchor == QLatin1String( "top-left" ) )
1344  else if ( textAnchor == QLatin1String( "top-right" ) )
1346  else if ( textAnchor == QLatin1String( "bottom-left" ) )
1348  else if ( textAnchor == QLatin1String( "bottom-right" ) )
1350  }
1351 
1352  QPointF textOffset;
1353  if ( jsonLayout.contains( QStringLiteral( "text-offset" ) ) )
1354  {
1355  const QVariant jsonTextOffset = jsonLayout.value( QStringLiteral( "text-offset" ) );
1356 
1357  // units are ems!
1358  switch ( jsonTextOffset.type() )
1359  {
1360  case QVariant::Map:
1361  ddLabelProperties.setProperty( QgsPalLayerSettings::OffsetXY, parseInterpolatePointByZoom( jsonTextOffset.toMap(), context, textSize, &textOffset ) );
1362  break;
1363 
1364  case QVariant::List:
1365  case QVariant::StringList:
1366  textOffset = QPointF( jsonTextOffset.toList().value( 0 ).toDouble() * textSize,
1367  jsonTextOffset.toList().value( 1 ).toDouble() * textSize );
1368  break;
1369 
1370  default:
1371  context.pushWarning( QObject::tr( "%1: Skipping unsupported text-offset type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonTextOffset.type() ) ) );
1372  break;
1373  }
1374 
1375  if ( !textOffset.isNull() )
1376  {
1377  labelSettings.offsetUnits = context.targetUnit();
1378  labelSettings.xOffset = textOffset.x();
1379  labelSettings.yOffset = textOffset.y();
1380  }
1381  }
1382  }
1383 
1384  if ( jsonLayout.contains( QStringLiteral( "icon-image" ) ) &&
1385  ( labelSettings.placement == QgsPalLayerSettings::Horizontal || labelSettings.placement == QgsPalLayerSettings::Curved ) )
1386  {
1387  QSize spriteSize;
1388  QString spriteProperty, spriteSizeProperty;
1389  const QString sprite = retrieveSpriteAsBase64( jsonLayout.value( QStringLiteral( "icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
1390  if ( !sprite.isEmpty() )
1391  {
1393  markerLayer->setPath( sprite );
1394  markerLayer->setSize( spriteSize.width() );
1395  markerLayer->setSizeUnit( context.targetUnit() );
1396 
1397  if ( !spriteProperty.isEmpty() )
1398  {
1399  QgsPropertyCollection markerDdProperties;
1400  markerDdProperties.setProperty( QgsSymbolLayer::PropertyName, QgsProperty::fromExpression( spriteProperty ) );
1401  markerLayer->setDataDefinedProperties( markerDdProperties );
1402 
1403  ddLabelProperties.setProperty( QgsPalLayerSettings::ShapeSizeX, QgsProperty::fromExpression( spriteSizeProperty ) );
1404  }
1405 
1406  QgsTextBackgroundSettings backgroundSettings;
1407  backgroundSettings.setEnabled( true );
1409  backgroundSettings.setSize( spriteSize );
1410  backgroundSettings.setSizeUnit( context.targetUnit() );
1411  backgroundSettings.setSizeType( QgsTextBackgroundSettings::SizeFixed );
1412  backgroundSettings.setMarkerSymbol( new QgsMarkerSymbol( QgsSymbolLayerList() << markerLayer ) );
1413  format.setBackground( backgroundSettings );
1414  }
1415  }
1416 
1417  if ( textSize >= 0 )
1418  {
1419  // TODO -- this probably needs revisiting -- it was copied from the MapTiler code, but may be wrong...
1420  labelSettings.priority = std::min( textSize / ( context.pixelSizeConversionFactor() * 3 ), 10.0 );
1421  }
1422 
1423  labelSettings.setFormat( format );
1424 
1425  // use a low obstacle weight for layers by default -- we'd rather have more labels for these layers, even if placement isn't ideal
1426  labelSettings.obstacleSettings().setFactor( 0.1 );
1427 
1428  labelSettings.setDataDefinedProperties( ddLabelProperties );
1429 
1430  labelingStyle.setGeometryType( geometryType );
1431  labelingStyle.setLabelSettings( labelSettings );
1432 
1433  hasLabeling = true;
1434 
1435  hasRenderer = parseSymbolLayerAsRenderer( jsonLayer, renderer, context );
1436 }
1437 
1439 {
1440  if ( !jsonLayer.contains( QStringLiteral( "layout" ) ) )
1441  {
1442  context.pushWarning( QObject::tr( "%1: Style layer has no layout property, skipping" ).arg( context.layerId() ) );
1443  return false;
1444  }
1445  const QVariantMap jsonLayout = jsonLayer.value( QStringLiteral( "layout" ) ).toMap();
1446 
1447  if ( jsonLayout.value( QStringLiteral( "symbol-placement" ) ).toString() == QLatin1String( "line" ) && !jsonLayout.contains( QStringLiteral( "text-field" ) ) )
1448  {
1449  QgsPropertyCollection ddProperties;
1450 
1451  double spacing = -1.0;
1452  if ( jsonLayout.contains( QStringLiteral( "symbol-spacing" ) ) )
1453  {
1454  const QVariant jsonSpacing = jsonLayout.value( QStringLiteral( "symbol-spacing" ) );
1455  switch ( jsonSpacing.type() )
1456  {
1457  case QVariant::Int:
1458  case QVariant::Double:
1459  spacing = jsonSpacing.toDouble() * context.pixelSizeConversionFactor();
1460  break;
1461 
1462  case QVariant::Map:
1463  ddProperties.setProperty( QgsSymbolLayer::PropertyInterval, parseInterpolateByZoom( jsonSpacing.toMap(), context, context.pixelSizeConversionFactor(), &spacing ) );
1464  break;
1465 
1466  case QVariant::List:
1467  case QVariant::StringList:
1468  ddProperties.setProperty( QgsSymbolLayer::PropertyInterval, parseValueList( jsonSpacing.toList(), PropertyType::Numeric, context, context.pixelSizeConversionFactor(), 255, nullptr, &spacing ) );
1469  break;
1470 
1471  default:
1472  context.pushWarning( QObject::tr( "%1: Skipping unsupported symbol-spacing type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonSpacing.type() ) ) );
1473  break;
1474  }
1475  }
1476  else
1477  {
1478  // defaults to 250
1479  spacing = 250 * context.pixelSizeConversionFactor();
1480  }
1481 
1482  bool rotateMarkers = true;
1483  if ( jsonLayout.contains( QStringLiteral( "icon-rotation-alignment" ) ) )
1484  {
1485  const QString alignment = jsonLayout.value( QStringLiteral( "icon-rotation-alignment" ) ).toString();
1486  if ( alignment == QLatin1String( "map" ) || alignment == QLatin1String( "auto" ) )
1487  {
1488  rotateMarkers = true;
1489  }
1490  else if ( alignment == QLatin1String( "viewport" ) )
1491  {
1492  rotateMarkers = false;
1493  }
1494  }
1495 
1496  QgsPropertyCollection markerDdProperties;
1497  double rotation = 0.0;
1498  if ( jsonLayout.contains( QStringLiteral( "icon-rotate" ) ) )
1499  {
1500  const QVariant jsonIconRotate = jsonLayout.value( QStringLiteral( "icon-rotate" ) );
1501  switch ( jsonIconRotate.type() )
1502  {
1503  case QVariant::Int:
1504  case QVariant::Double:
1505  rotation = jsonIconRotate.toDouble();
1506  break;
1507 
1508  case QVariant::Map:
1509  markerDdProperties.setProperty( QgsSymbolLayer::PropertyAngle, parseInterpolateByZoom( jsonIconRotate.toMap(), context, context.pixelSizeConversionFactor(), &rotation ) );
1510  break;
1511 
1512  case QVariant::List:
1513  case QVariant::StringList:
1514  markerDdProperties.setProperty( QgsSymbolLayer::PropertyAngle, parseValueList( jsonIconRotate.toList(), PropertyType::Numeric, context, context.pixelSizeConversionFactor(), 255, nullptr, &rotation ) );
1515  break;
1516 
1517  default:
1518  context.pushWarning( QObject::tr( "%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonIconRotate.type() ) ) );
1519  break;
1520  }
1521  }
1522 
1523  QgsMarkerLineSymbolLayer *lineSymbol = new QgsMarkerLineSymbolLayer( rotateMarkers, spacing > 0 ? spacing : 1 );
1524  lineSymbol->setOutputUnit( context.targetUnit() );
1525  lineSymbol->setDataDefinedProperties( ddProperties );
1526  if ( spacing < 1 )
1527  {
1528  // if spacing isn't specified, it's a central point marker only
1530  }
1531 
1533  QSize spriteSize;
1534  QString spriteProperty, spriteSizeProperty;
1535  const QString sprite = retrieveSpriteAsBase64( jsonLayout.value( QStringLiteral( "icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
1536  if ( !sprite.isNull() )
1537  {
1538  markerLayer->setPath( sprite );
1539  markerLayer->setSize( spriteSize.width() );
1540  markerLayer->setSizeUnit( context.targetUnit() );
1541 
1542  if ( !spriteProperty.isEmpty() )
1543  {
1544  markerDdProperties.setProperty( QgsSymbolLayer::PropertyName, QgsProperty::fromExpression( spriteProperty ) );
1545  markerDdProperties.setProperty( QgsSymbolLayer::PropertyWidth, QgsProperty::fromExpression( spriteSizeProperty ) );
1546  }
1547  }
1548 
1549  if ( jsonLayout.contains( QStringLiteral( "icon-size" ) ) )
1550  {
1551  const QVariant jsonIconSize = jsonLayout.value( QStringLiteral( "icon-size" ) );
1552  double size = 1.0;
1553  QgsProperty property;
1554  switch ( jsonIconSize.type() )
1555  {
1556  case QVariant::Int:
1557  case QVariant::Double:
1558  {
1559  size = jsonIconSize.toDouble();
1560  if ( !spriteSizeProperty.isEmpty() )
1561  {
1562  markerDdProperties.setProperty( QgsSymbolLayer::PropertyWidth,
1563  QgsProperty::fromExpression( QStringLiteral( "with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
1564  }
1565  break;
1566  }
1567 
1568  case QVariant::Map:
1569  property = parseInterpolateByZoom( jsonIconSize.toMap(), context, 1, &size );
1570  break;
1571 
1572  case QVariant::List:
1573  case QVariant::StringList:
1574  default:
1575  context.pushWarning( QObject::tr( "%1: Skipping non-implemented icon-size type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonIconSize.type() ) ) );
1576  break;
1577  }
1578  markerLayer->setSize( size * spriteSize.width() );
1579  if ( !property.expressionString().isEmpty() )
1580  {
1581  if ( !spriteSizeProperty.isEmpty() )
1582  {
1583  markerDdProperties.setProperty( QgsSymbolLayer::PropertyWidth,
1584  QgsProperty::fromExpression( QStringLiteral( "with_variable('marker_size',%1,(%2)*@marker_size)" ).arg( spriteSizeProperty ).arg( property.expressionString() ) ) );
1585  }
1586  else
1587  {
1588  markerDdProperties.setProperty( QgsSymbolLayer::PropertyWidth,
1589  QgsProperty::fromExpression( QStringLiteral( "(%2)*%1" ).arg( spriteSize.width() ).arg( property.expressionString() ) ) );
1590  }
1591  }
1592  }
1593 
1594  markerLayer->setDataDefinedProperties( markerDdProperties );
1595  markerLayer->setAngle( rotation );
1596  lineSymbol->setSubSymbol( new QgsMarkerSymbol( QgsSymbolLayerList() << markerLayer ) );
1597 
1598  std::unique_ptr< QgsSymbol > symbol = qgis::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << lineSymbol );
1599 
1600  // set render units
1601  symbol->setOutputUnit( context.targetUnit() );
1602  lineSymbol->setOutputUnit( context.targetUnit() );
1603 
1604  rendererStyle.setGeometryType( QgsWkbTypes::LineGeometry );
1605  rendererStyle.setSymbol( symbol.release() );
1606  return true;
1607  }
1608  else if ( jsonLayout.contains( QStringLiteral( "icon-image" ) ) )
1609  {
1610  const QVariantMap jsonPaint = jsonLayer.value( QStringLiteral( "paint" ) ).toMap();
1611 
1612  QSize spriteSize;
1613  QString spriteProperty, spriteSizeProperty;
1614  const QString sprite = retrieveSpriteAsBase64( jsonLayout.value( QStringLiteral( "icon-image" ) ), context, spriteSize, spriteProperty, spriteSizeProperty );
1615  if ( !sprite.isEmpty() )
1616  {
1618  rasterMarker->setPath( sprite );
1619  rasterMarker->setSize( spriteSize.width() );
1620  rasterMarker->setSizeUnit( context.targetUnit() );
1621 
1622  QgsPropertyCollection markerDdProperties;
1623  if ( !spriteProperty.isEmpty() )
1624  {
1625  markerDdProperties.setProperty( QgsSymbolLayer::PropertyName, QgsProperty::fromExpression( spriteProperty ) );
1626  markerDdProperties.setProperty( QgsSymbolLayer::PropertyWidth, QgsProperty::fromExpression( spriteSizeProperty ) );
1627  }
1628 
1629  if ( jsonLayout.contains( QStringLiteral( "icon-size" ) ) )
1630  {
1631  const QVariant jsonIconSize = jsonLayout.value( QStringLiteral( "icon-size" ) );
1632  double size = 1.0;
1633  QgsProperty property;
1634  switch ( jsonIconSize.type() )
1635  {
1636  case QVariant::Int:
1637  case QVariant::Double:
1638  {
1639  size = jsonIconSize.toDouble();
1640  if ( !spriteSizeProperty.isEmpty() )
1641  {
1642  markerDdProperties.setProperty( QgsSymbolLayer::PropertyWidth,
1643  QgsProperty::fromExpression( QStringLiteral( "with_variable('marker_size',%1,%2*@marker_size)" ).arg( spriteSizeProperty ).arg( size ) ) );
1644  }
1645  break;
1646  }
1647 
1648  case QVariant::Map:
1649  property = parseInterpolateByZoom( jsonIconSize.toMap(), context, 1, &size );
1650  break;
1651 
1652  case QVariant::List:
1653  case QVariant::StringList:
1654  default:
1655  context.pushWarning( QObject::tr( "%1: Skipping non-implemented icon-size type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonIconSize.type() ) ) );
1656  break;
1657  }
1658  rasterMarker->setSize( size * spriteSize.width() );
1659  if ( !property.expressionString().isEmpty() )
1660  {
1661  if ( !spriteSizeProperty.isEmpty() )
1662  {
1663  markerDdProperties.setProperty( QgsSymbolLayer::PropertyWidth,
1664  QgsProperty::fromExpression( QStringLiteral( "with_variable('marker_size',%1,(%2)*@marker_size)" ).arg( spriteSizeProperty ).arg( property.expressionString() ) ) );
1665  }
1666  else
1667  {
1668  markerDdProperties.setProperty( QgsSymbolLayer::PropertyWidth,
1669  QgsProperty::fromExpression( QStringLiteral( "(%2)*%1" ).arg( spriteSize.width() ).arg( property.expressionString() ) ) );
1670  }
1671  }
1672  }
1673 
1674  double rotation = 0.0;
1675  if ( jsonLayout.contains( QStringLiteral( "icon-rotate" ) ) )
1676  {
1677  const QVariant jsonIconRotate = jsonLayout.value( QStringLiteral( "icon-rotate" ) );
1678  switch ( jsonIconRotate.type() )
1679  {
1680  case QVariant::Int:
1681  case QVariant::Double:
1682  rotation = jsonIconRotate.toDouble();
1683  break;
1684 
1685  case QVariant::Map:
1686  markerDdProperties.setProperty( QgsSymbolLayer::PropertyAngle, parseInterpolateByZoom( jsonIconRotate.toMap(), context, context.pixelSizeConversionFactor(), &rotation ) );
1687  break;
1688 
1689  case QVariant::List:
1690  case QVariant::StringList:
1691  markerDdProperties.setProperty( QgsSymbolLayer::PropertyAngle, parseValueList( jsonIconRotate.toList(), PropertyType::Numeric, context, context.pixelSizeConversionFactor(), 255, nullptr, &rotation ) );
1692  break;
1693 
1694  default:
1695  context.pushWarning( QObject::tr( "%1: Skipping unsupported icon-rotate type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonIconRotate.type() ) ) );
1696  break;
1697  }
1698  }
1699 
1700  double iconOpacity = -1.0;
1701  if ( jsonPaint.contains( QStringLiteral( "icon-opacity" ) ) )
1702  {
1703  const QVariant jsonIconOpacity = jsonPaint.value( QStringLiteral( "icon-opacity" ) );
1704  switch ( jsonIconOpacity.type() )
1705  {
1706  case QVariant::Int:
1707  case QVariant::Double:
1708  iconOpacity = jsonIconOpacity.toDouble();
1709  break;
1710 
1711  case QVariant::Map:
1712  markerDdProperties.setProperty( QgsSymbolLayer::PropertyOpacity, parseInterpolateByZoom( jsonIconOpacity.toMap(), context, 100, &iconOpacity ) );
1713  break;
1714 
1715  case QVariant::List:
1716  case QVariant::StringList:
1717  markerDdProperties.setProperty( QgsSymbolLayer::PropertyOpacity, parseValueList( jsonIconOpacity.toList(), PropertyType::Numeric, context, 100, 255, nullptr, &iconOpacity ) );
1718  break;
1719 
1720  default:
1721  context.pushWarning( QObject::tr( "%1: Skipping unsupported icon-opacity type (%2)" ).arg( context.layerId(), QMetaType::typeName( jsonIconOpacity.type() ) ) );
1722  break;
1723  }
1724  }
1725 
1726  rasterMarker->setDataDefinedProperties( markerDdProperties );
1727  rasterMarker->setAngle( rotation );
1728  if ( iconOpacity >= 0 )
1729  rasterMarker->setOpacity( iconOpacity );
1730 
1731  QgsMarkerSymbol *markerSymbol = new QgsMarkerSymbol( QgsSymbolLayerList() << rasterMarker );
1732  rendererStyle.setSymbol( markerSymbol );
1734  return true;
1735  }
1736  }
1737 
1738  return false;
1739 }
1740 
1742 {
1743  const double base = json.value( QStringLiteral( "base" ), QStringLiteral( "1" ) ).toDouble();
1744  const QVariantList stops = json.value( QStringLiteral( "stops" ) ).toList();
1745  if ( stops.empty() )
1746  return QgsProperty();
1747 
1748  QString caseString = QStringLiteral( "CASE " );
1749 
1750  for ( int i = 0; i < stops.length() - 1; ++i )
1751  {
1752  // step bottom zoom
1753  const QString bz = stops.at( i ).toList().value( 0 ).toString();
1754  // step top zoom
1755  const QString tz = stops.at( i + 1 ).toList().value( 0 ).toString();
1756 
1757  const QColor bottomColor = parseColor( stops.at( i ).toList().value( 1 ), context );
1758  const QColor topColor = parseColor( stops.at( i + 1 ).toList().value( 1 ), context );
1759 
1760  int bcHue;
1761  int bcSat;
1762  int bcLight;
1763  int bcAlpha;
1764  colorAsHslaComponents( bottomColor, bcHue, bcSat, bcLight, bcAlpha );
1765  int tcHue;
1766  int tcSat;
1767  int tcLight;
1768  int tcAlpha;
1769  colorAsHslaComponents( topColor, tcHue, tcSat, tcLight, tcAlpha );
1770 
1771  caseString += QStringLiteral( "WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 THEN color_hsla("
1772  "%3, %4, %5, %6) " ).arg( bz, tz,
1773  interpolateExpression( bz.toDouble(), tz.toDouble(), bcHue, tcHue, base ),
1774  interpolateExpression( bz.toDouble(), tz.toDouble(), bcSat, tcSat, base ),
1775  interpolateExpression( bz.toDouble(), tz.toDouble(), bcLight, tcLight, base ),
1776  interpolateExpression( bz.toDouble(), tz.toDouble(), bcAlpha, tcAlpha, base ) );
1777  }
1778 
1779  // top color
1780  const QString tz = stops.last().toList().value( 0 ).toString();
1781  const QColor topColor = parseColor( stops.last().toList().value( 1 ), context );
1782  int tcHue;
1783  int tcSat;
1784  int tcLight;
1785  int tcAlpha;
1786  colorAsHslaComponents( topColor, tcHue, tcSat, tcLight, tcAlpha );
1787 
1788  caseString += QStringLiteral( "WHEN @vector_tile_zoom >= %1 THEN color_hsla(%2, %3, %4, %5) "
1789  "ELSE color_hsla(%2, %3, %4, %5) END" ).arg( tz )
1790  .arg( tcHue ).arg( tcSat ).arg( tcLight ).arg( tcAlpha );
1791 
1792 
1793  if ( !stops.empty() && defaultColor )
1794  *defaultColor = parseColor( stops.value( 0 ).toList().value( 1 ).toString(), context );
1795 
1796  return QgsProperty::fromExpression( caseString );
1797 }
1798 
1799 QgsProperty QgsMapBoxGlStyleConverter::parseInterpolateByZoom( const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier, double *defaultNumber )
1800 {
1801  const double base = json.value( QStringLiteral( "base" ), QStringLiteral( "1" ) ).toDouble();
1802  const QVariantList stops = json.value( QStringLiteral( "stops" ) ).toList();
1803  if ( stops.empty() )
1804  return QgsProperty();
1805 
1806  QString scaleExpression;
1807  if ( stops.size() <= 2 )
1808  {
1809  scaleExpression = interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(),
1810  stops.last().toList().value( 0 ).toDouble(),
1811  stops.value( 0 ).toList().value( 1 ).toDouble(),
1812  stops.last().toList().value( 1 ).toDouble(), base, multiplier );
1813  }
1814  else
1815  {
1816  scaleExpression = parseStops( base, stops, multiplier, context );
1817  }
1818 
1819  if ( !stops.empty() && defaultNumber )
1820  *defaultNumber = stops.value( 0 ).toList().value( 1 ).toDouble() * multiplier;
1821 
1822  return QgsProperty::fromExpression( scaleExpression );
1823 }
1824 
1826 {
1827  const double base = json.value( QStringLiteral( "base" ), QStringLiteral( "1" ) ).toDouble();
1828  const QVariantList stops = json.value( QStringLiteral( "stops" ) ).toList();
1829  if ( stops.empty() )
1830  return QgsProperty();
1831 
1832  QString scaleExpression;
1833  if ( stops.length() <= 2 )
1834  {
1835  scaleExpression = QStringLiteral( "set_color_part(@symbol_color, 'alpha', %1)" )
1836  .arg( interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(),
1837  stops.last().toList().value( 0 ).toDouble(),
1838  stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity,
1839  stops.last().toList().value( 1 ).toDouble() * maxOpacity, base ) );
1840  }
1841  else
1842  {
1843  scaleExpression = parseOpacityStops( base, stops, maxOpacity );
1844  }
1845  return QgsProperty::fromExpression( scaleExpression );
1846 }
1847 
1848 QString QgsMapBoxGlStyleConverter::parseOpacityStops( double base, const QVariantList &stops, int maxOpacity )
1849 {
1850  QString caseString = QStringLiteral( "CASE WHEN @vector_tile_zoom < %1 THEN set_color_part(@symbol_color, 'alpha', %2)" )
1851  .arg( stops.value( 0 ).toList().value( 0 ).toString() )
1852  .arg( stops.value( 0 ).toList().value( 1 ).toDouble() * maxOpacity );
1853 
1854  for ( int i = 0; i < stops.size() - 1; ++i )
1855  {
1856  caseString += QStringLiteral( " WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
1857  "THEN set_color_part(@symbol_color, 'alpha', %3)" )
1858  .arg( stops.value( i ).toList().value( 0 ).toString(),
1859  stops.value( i + 1 ).toList().value( 0 ).toString(),
1860  interpolateExpression( stops.value( i ).toList().value( 0 ).toDouble(),
1861  stops.value( i + 1 ).toList().value( 0 ).toDouble(),
1862  stops.value( i ).toList().value( 1 ).toDouble() * maxOpacity,
1863  stops.value( i + 1 ).toList().value( 1 ).toDouble() * maxOpacity, base ) );
1864  }
1865 
1866  caseString += QStringLiteral( " WHEN @vector_tile_zoom >= %1 "
1867  "THEN set_color_part(@symbol_color, 'alpha', %2) END" )
1868  .arg( stops.last().toList().value( 0 ).toString() )
1869  .arg( stops.last().toList().value( 1 ).toDouble() * maxOpacity );
1870  return caseString;
1871 }
1872 
1873 QgsProperty QgsMapBoxGlStyleConverter::parseInterpolatePointByZoom( const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier, QPointF *defaultPoint )
1874 {
1875  const double base = json.value( QStringLiteral( "base" ), QStringLiteral( "1" ) ).toDouble();
1876  const QVariantList stops = json.value( QStringLiteral( "stops" ) ).toList();
1877  if ( stops.empty() )
1878  return QgsProperty();
1879 
1880  QString scaleExpression;
1881  if ( stops.size() <= 2 )
1882  {
1883  scaleExpression = QStringLiteral( "array(%1,%2)" ).arg( interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(),
1884  stops.last().toList().value( 0 ).toDouble(),
1885  stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble(),
1886  stops.last().toList().value( 1 ).toList().value( 0 ).toDouble(), base, multiplier ),
1887  interpolateExpression( stops.value( 0 ).toList().value( 0 ).toDouble(),
1888  stops.last().toList().value( 0 ).toDouble(),
1889  stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble(),
1890  stops.last().toList().value( 1 ).toList().value( 1 ).toDouble(), base, multiplier )
1891  );
1892  }
1893  else
1894  {
1895  scaleExpression = parsePointStops( base, stops, context, multiplier );
1896  }
1897 
1898  if ( !stops.empty() && defaultPoint )
1899  *defaultPoint = QPointF( stops.value( 0 ).toList().value( 1 ).toList().value( 0 ).toDouble() * multiplier,
1900  stops.value( 0 ).toList().value( 1 ).toList().value( 1 ).toDouble() * multiplier );
1901 
1902  return QgsProperty::fromExpression( scaleExpression );
1903 }
1904 
1906  const QVariantMap &conversionMap, QString *defaultString )
1907 {
1908  const QVariantList stops = json.value( QStringLiteral( "stops" ) ).toList();
1909  if ( stops.empty() )
1910  return QgsProperty();
1911 
1912  QString scaleExpression = parseStringStops( stops, context, conversionMap, defaultString );
1913 
1914  return QgsProperty::fromExpression( scaleExpression );
1915 }
1916 
1917 QString QgsMapBoxGlStyleConverter::parsePointStops( double base, const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, double multiplier )
1918 {
1919  QString caseString = QStringLiteral( "CASE " );
1920 
1921  for ( int i = 0; i < stops.length() - 1; ++i )
1922  {
1923  // bottom zoom and value
1924  const QVariant bz = stops.value( i ).toList().value( 0 );
1925  const QVariant bv = stops.value( i ).toList().value( 1 );
1926  if ( bv.type() != QVariant::List && bv.type() != QVariant::StringList )
1927  {
1928  context.pushWarning( QObject::tr( "%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.layerId(), QMetaType::typeName( bz.type() ) ) );
1929  return QString();
1930  }
1931 
1932  // top zoom and value
1933  const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
1934  const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
1935  if ( tv.type() != QVariant::List && tv.type() != QVariant::StringList )
1936  {
1937  context.pushWarning( QObject::tr( "%1: Skipping unsupported offset interpolation type (%2)." ).arg( context.layerId(), QMetaType::typeName( tz.type() ) ) );
1938  return QString();
1939  }
1940 
1941  caseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1942  "THEN array(%3,%4)" ).arg( bz.toString(),
1943  tz.toString(),
1944  interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 0 ).toDouble(), tv.toList().value( 0 ).toDouble(), base, multiplier ),
1945  interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toList().value( 1 ).toDouble(), tv.toList().value( 1 ).toDouble(), base, multiplier ) );
1946  }
1947  caseString += QLatin1String( "END" );
1948  return caseString;
1949 }
1950 
1951 QString QgsMapBoxGlStyleConverter::parseStops( double base, const QVariantList &stops, double multiplier, QgsMapBoxGlStyleConversionContext &context )
1952 {
1953  QString caseString = QStringLiteral( "CASE " );
1954 
1955  for ( int i = 0; i < stops.length() - 1; ++i )
1956  {
1957  // bottom zoom and value
1958  const QVariant bz = stops.value( i ).toList().value( 0 );
1959  const QVariant bv = stops.value( i ).toList().value( 1 );
1960  if ( bz.type() == QVariant::List || bz.type() == QVariant::StringList )
1961  {
1962  context.pushWarning( QObject::tr( "%1: Expressions in interpolation function are not supported, skipping." ).arg( context.layerId() ) );
1963  return QString();
1964  }
1965 
1966  // top zoom and value
1967  const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
1968  const QVariant tv = stops.value( i + 1 ).toList().value( 1 );
1969  if ( tz.type() == QVariant::List || tz.type() == QVariant::StringList )
1970  {
1971  context.pushWarning( QObject::tr( "%1: Expressions in interpolation function are not supported, skipping." ).arg( context.layerId() ) );
1972  return QString();
1973  }
1974 
1975  caseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
1976  "THEN %3 " ).arg( bz.toString(),
1977  tz.toString(),
1978  interpolateExpression( bz.toDouble(), tz.toDouble(), bv.toDouble(), tv.toDouble(), base, multiplier ) );
1979  }
1980 
1981  const QVariant z = stops.last().toList().value( 0 );
1982  const QVariant v = stops.last().toList().value( 1 );
1983  caseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 "
1984  "THEN %2 END" ).arg( z.toString() ).arg( v.toDouble() * multiplier );
1985  return caseString;
1986 }
1987 
1988 QString QgsMapBoxGlStyleConverter::parseStringStops( const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, const QVariantMap &conversionMap, QString *defaultString )
1989 {
1990  QString caseString = QStringLiteral( "CASE " );
1991 
1992  for ( int i = 0; i < stops.length() - 1; ++i )
1993  {
1994  // bottom zoom and value
1995  const QVariant bz = stops.value( i ).toList().value( 0 );
1996  const QString bv = stops.value( i ).toList().value( 1 ).toString();
1997  if ( bz.type() == QVariant::List || bz.type() == QVariant::StringList )
1998  {
1999  context.pushWarning( QObject::tr( "%1: Expressions in interpolation function are not supported, skipping." ).arg( context.layerId() ) );
2000  return QString();
2001  }
2002 
2003  // top zoom
2004  const QVariant tz = stops.value( i + 1 ).toList().value( 0 );
2005  if ( tz.type() == QVariant::List || tz.type() == QVariant::StringList )
2006  {
2007  context.pushWarning( QObject::tr( "%1: Expressions in interpolation function are not supported, skipping." ).arg( context.layerId() ) );
2008  return QString();
2009  }
2010 
2011  caseString += QStringLiteral( "WHEN @vector_tile_zoom > %1 AND @vector_tile_zoom <= %2 "
2012  "THEN %3 " ).arg( bz.toString(),
2013  tz.toString(),
2014  QgsExpression::quotedValue( conversionMap.value( bv, bv ) ) );
2015  }
2016  caseString += QStringLiteral( "ELSE %1 END" ).arg( QgsExpression::quotedValue( conversionMap.value( stops.constLast().toList().value( 1 ).toString(),
2017  stops.constLast().toList().value( 1 ) ) ) );
2018  if ( defaultString )
2019  *defaultString = stops.constLast().toList().value( 1 ).toString();
2020  return caseString;
2021 }
2022 
2023 QgsProperty QgsMapBoxGlStyleConverter::parseValueList( const QVariantList &json, QgsMapBoxGlStyleConverter::PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier, int maxOpacity, QColor *defaultColor, double *defaultNumber )
2024 {
2025  const QString method = json.value( 0 ).toString();
2026  if ( method == QLatin1String( "interpolate" ) )
2027  {
2028  return parseInterpolateListByZoom( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
2029  }
2030  else if ( method == QLatin1String( "match" ) )
2031  {
2032  return parseMatchList( json, type, context, multiplier, maxOpacity, defaultColor, defaultNumber );
2033  }
2034  else
2035  {
2036  context.pushWarning( QObject::tr( "%1: Could not interpret value list with method %2" ).arg( context.layerId(), method ) );
2037  return QgsProperty();
2038  }
2039 }
2040 
2041 QgsProperty QgsMapBoxGlStyleConverter::parseMatchList( const QVariantList &json, QgsMapBoxGlStyleConverter::PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier, int maxOpacity, QColor *defaultColor, double *defaultNumber )
2042 {
2043  const QString attribute = parseExpression( json.value( 1 ).toList(), context );
2044  if ( attribute.isEmpty() )
2045  {
2046  context.pushWarning( QObject::tr( "%1: Could not interpret match list" ).arg( context.layerId() ) );
2047  return QgsProperty();
2048  }
2049 
2050  QString caseString = QStringLiteral( "CASE " );
2051 
2052  for ( int i = 2; i < json.length() - 1; i += 2 )
2053  {
2054  const QVariantList keys = json.value( i ).toList();
2055 
2056  QStringList matchString;
2057  for ( const QVariant &key : keys )
2058  {
2059  matchString << QgsExpression::quotedValue( key );
2060  }
2061 
2062  const QVariant value = json.value( i + 1 );
2063 
2064  QString valueString;
2065  switch ( type )
2066  {
2067  case Color:
2068  {
2069  const QColor color = parseColor( value, context );
2070  valueString = QgsExpression::quotedString( color.name() );
2071  break;
2072  }
2073 
2074  case Numeric:
2075  {
2076  const double v = value.toDouble() * multiplier;
2077  valueString = QString::number( v );
2078  break;
2079  }
2080 
2081  case Opacity:
2082  {
2083  const double v = value.toDouble() * maxOpacity;
2084  valueString = QString::number( v );
2085  break;
2086  }
2087 
2088  case Point:
2089  {
2090  valueString = QStringLiteral( "array(%1,%2)" ).arg( value.toList().value( 0 ).toDouble() * multiplier,
2091  value.toList().value( 0 ).toDouble() * multiplier );
2092  break;
2093  }
2094 
2095  }
2096 
2097  caseString += QStringLiteral( "WHEN %1 IN (%2) THEN %3 " ).arg( attribute,
2098  matchString.join( ',' ), valueString );
2099  }
2100 
2101 
2102  QString elseValue;
2103  switch ( type )
2104  {
2105  case Color:
2106  {
2107  const QColor color = parseColor( json.constLast(), context );
2108  if ( defaultColor )
2109  *defaultColor = color;
2110 
2111  elseValue = QgsExpression::quotedString( color.name() );
2112  break;
2113  }
2114 
2115  case Numeric:
2116  {
2117  const double v = json.constLast().toDouble() * multiplier;
2118  if ( defaultNumber )
2119  *defaultNumber = v;
2120  elseValue = QString::number( v );
2121  break;
2122  }
2123 
2124  case Opacity:
2125  {
2126  const double v = json.constLast().toDouble() * maxOpacity;
2127  if ( defaultNumber )
2128  *defaultNumber = v;
2129  elseValue = QString::number( v );
2130  break;
2131  }
2132 
2133  case Point:
2134  {
2135  elseValue = QStringLiteral( "array(%1,%2)" ).arg( json.constLast().toList().value( 0 ).toDouble() * multiplier,
2136  json.constLast().toList().value( 0 ).toDouble() * multiplier );
2137  break;
2138  }
2139 
2140  }
2141 
2142  caseString += QStringLiteral( "ELSE %1 END" ).arg( elseValue );
2143  return QgsProperty::fromExpression( caseString );
2144 }
2145 
2146 QgsProperty QgsMapBoxGlStyleConverter::parseInterpolateListByZoom( const QVariantList &json, PropertyType type, QgsMapBoxGlStyleConversionContext &context, double multiplier, int maxOpacity, QColor *defaultColor, double *defaultNumber )
2147 {
2148  if ( json.value( 0 ).toString() != QLatin1String( "interpolate" ) )
2149  {
2150  context.pushWarning( QObject::tr( "%1: Could not interpret value list" ).arg( context.layerId() ) );
2151  return QgsProperty();
2152  }
2153 
2154  double base = 1;
2155  const QString technique = json.value( 1 ).toList().value( 0 ).toString();
2156  if ( technique == QLatin1String( "linear" ) )
2157  base = 1;
2158  else if ( technique == QLatin1String( "exponential" ) )
2159  base = json.value( 1 ).toList(). value( 1 ).toDouble();
2160  else if ( technique == QLatin1String( "cubic-bezier" ) )
2161  {
2162  context.pushWarning( QObject::tr( "%1: Cubic-bezier interpolation is not supported, linear used instead." ).arg( context.layerId() ) );
2163  base = 1;
2164  }
2165  else
2166  {
2167  context.pushWarning( QObject::tr( "%1: Skipping not implemented interpolation method %2" ).arg( context.layerId(), technique ) );
2168  return QgsProperty();
2169  }
2170 
2171  if ( json.value( 2 ).toList().value( 0 ).toString() != QLatin1String( "zoom" ) )
2172  {
2173  context.pushWarning( QObject::tr( "%1: Skipping not implemented interpolation input %2" ).arg( context.layerId(), json.value( 2 ).toString() ) );
2174  return QgsProperty();
2175  }
2176 
2177  // Convert stops into list of lists
2178  QVariantList stops;
2179  for ( int i = 3; i < json.length(); i += 2 )
2180  {
2181  stops.push_back( QVariantList() << json.value( i ).toString() << json.value( i + 1 ).toString() );
2182  }
2183 
2184  QVariantMap props;
2185  props.insert( QStringLiteral( "stops" ), stops );
2186  props.insert( QStringLiteral( "base" ), base );
2187  switch ( type )
2188  {
2189  case PropertyType::Color:
2190  return parseInterpolateColorByZoom( props, context, defaultColor );
2191 
2192  case PropertyType::Numeric:
2193  return parseInterpolateByZoom( props, context, multiplier, defaultNumber );
2194 
2195  case PropertyType::Opacity:
2196  return parseInterpolateOpacityByZoom( props, maxOpacity );
2197 
2198  case PropertyType::Point:
2199  return parseInterpolatePointByZoom( props, context, multiplier );
2200  }
2201  return QgsProperty();
2202 }
2203 
2205 {
2206  if ( color.type() != QVariant::String )
2207  {
2208  context.pushWarning( QObject::tr( "%1: Could not parse non-string color %2, skipping" ).arg( context.layerId(), color.toString() ) );
2209  return QColor();
2210  }
2211 
2212  return QgsSymbolLayerUtils::parseColor( color.toString() );
2213 }
2214 
2215 void QgsMapBoxGlStyleConverter::colorAsHslaComponents( const QColor &color, int &hue, int &saturation, int &lightness, int &alpha )
2216 {
2217  hue = std::max( 0, color.hslHue() );
2218  saturation = color.hslSaturation() / 255.0 * 100;
2219  lightness = color.lightness() / 255.0 * 100;
2220  alpha = color.alpha();
2221 }
2222 
2223 QString QgsMapBoxGlStyleConverter::interpolateExpression( double zoomMin, double zoomMax, double valueMin, double valueMax, double base, double multiplier )
2224 {
2225  // special case!
2226  if ( qgsDoubleNear( valueMin, valueMax ) )
2227  return QString::number( valueMin * multiplier );
2228 
2229  QString expression;
2230  if ( base == 1 )
2231  {
2232  expression = QStringLiteral( "scale_linear(@vector_tile_zoom,%1,%2,%3,%4)" ).arg( zoomMin )
2233  .arg( zoomMax )
2234  .arg( valueMin )
2235  .arg( valueMax );
2236  }
2237  else
2238  {
2239  expression = QStringLiteral( "scale_exp(@vector_tile_zoom,%1,%2,%3,%4,%5)" ).arg( zoomMin )
2240  .arg( zoomMax )
2241  .arg( valueMin )
2242  .arg( valueMax )
2243  .arg( base );
2244  }
2245 
2246  if ( multiplier != 1 )
2247  return QStringLiteral( "%1 * %2" ).arg( expression ).arg( multiplier );
2248  else
2249  return expression;
2250 }
2251 
2252 Qt::PenCapStyle QgsMapBoxGlStyleConverter::parseCapStyle( const QString &style )
2253 {
2254  if ( style == QLatin1String( "round" ) )
2255  return Qt::RoundCap;
2256  else if ( style == QLatin1String( "square" ) )
2257  return Qt::SquareCap;
2258  else
2259  return Qt::FlatCap; // "butt" is default
2260 }
2261 
2262 Qt::PenJoinStyle QgsMapBoxGlStyleConverter::parseJoinStyle( const QString &style )
2263 {
2264  if ( style == QLatin1String( "bevel" ) )
2265  return Qt::BevelJoin;
2266  else if ( style == QLatin1String( "round" ) )
2267  return Qt::RoundJoin;
2268  else
2269  return Qt::MiterJoin; // "miter" is default
2270 }
2271 
2272 QString QgsMapBoxGlStyleConverter::parseExpression( const QVariantList &expression, QgsMapBoxGlStyleConversionContext &context )
2273 {
2274  QString op = expression.value( 0 ).toString();
2275  if ( op == QLatin1String( "all" )
2276  || op == QLatin1String( "any" )
2277  || op == QLatin1String( "none" ) )
2278  {
2279  QStringList parts;
2280  for ( int i = 1; i < expression.size(); ++i )
2281  {
2282  QString part = parseValue( expression.at( i ), context );
2283  if ( part.isEmpty() )
2284  {
2285  context.pushWarning( QObject::tr( "%1: Skipping unsupported expression" ).arg( context.layerId() ) );
2286  return QString();
2287  }
2288  parts << part;
2289  }
2290 
2291  if ( op == QLatin1String( "none" ) )
2292  return QStringLiteral( "NOT (%1)" ).arg( parts.join( QLatin1String( ") AND NOT (" ) ) );
2293 
2294  QString operatorString;
2295  if ( op == QLatin1String( "all" ) )
2296  operatorString = QStringLiteral( ") AND (" );
2297  else if ( op == QLatin1String( "any" ) )
2298  operatorString = QStringLiteral( ") OR (" );
2299 
2300  return QStringLiteral( "(%1)" ).arg( parts.join( operatorString ) );
2301  }
2302  else if ( op == '!' )
2303  {
2304  // ! inverts next expression's meaning
2305  QVariantList contraJsonExpr = expression.value( 1 ).toList();
2306  contraJsonExpr[0] = QString( op + contraJsonExpr[0].toString() );
2307  // ['!', ['has', 'level']] -> ['!has', 'level']
2308  return parseKey( contraJsonExpr );
2309  }
2310  else if ( op == QLatin1String( "==" )
2311  || op == QLatin1String( "!=" )
2312  || op == QLatin1String( ">=" )
2313  || op == '>'
2314  || op == QLatin1String( "<=" )
2315  || op == '<' )
2316  {
2317  // use IS and NOT IS instead of = and != because they can deal with NULL values
2318  if ( op == QLatin1String( "==" ) )
2319  op = QStringLiteral( "IS" );
2320  else if ( op == QLatin1String( "!=" ) )
2321  op = QStringLiteral( "IS NOT" );
2322  return QStringLiteral( "%1 %2 %3" ).arg( parseKey( expression.value( 1 ) ),
2323  op, parseValue( expression.value( 2 ), context ) );
2324  }
2325  else if ( op == QLatin1String( "has" ) )
2326  {
2327  return parseKey( expression.value( 1 ) ) + QStringLiteral( " IS NOT NULL" );
2328  }
2329  else if ( op == QLatin1String( "!has" ) )
2330  {
2331  return parseKey( expression.value( 1 ) ) + QStringLiteral( " IS NULL" );
2332  }
2333  else if ( op == QLatin1String( "in" ) || op == QLatin1String( "!in" ) )
2334  {
2335  const QString key = parseKey( expression.value( 1 ) );
2336  QStringList parts;
2337  for ( int i = 2; i < expression.size(); ++i )
2338  {
2339  QString part = parseValue( expression.at( i ), context );
2340  if ( part.isEmpty() )
2341  {
2342  context.pushWarning( QObject::tr( "%1: Skipping unsupported expression" ).arg( context.layerId() ) );
2343  return QString();
2344  }
2345  parts << part;
2346  }
2347  if ( op == QLatin1String( "in" ) )
2348  return QStringLiteral( "%1 IN (%2)" ).arg( key, parts.join( QLatin1String( ", " ) ) );
2349  else
2350  return QStringLiteral( "(%1 IS NULL OR %1 NOT IN (%2))" ).arg( key, parts.join( QLatin1String( ", " ) ) );
2351  }
2352  else if ( op == QLatin1String( "get" ) )
2353  {
2354  return parseKey( expression.value( 1 ) );
2355  }
2356  else if ( op == QLatin1String( "match" ) )
2357  {
2358  const QString attribute = expression.value( 1 ).toList().value( 1 ).toString();
2359 
2360  if ( expression.size() == 5
2361  && expression.at( 3 ).type() == QVariant::Bool && expression.at( 3 ).toBool() == true
2362  && expression.at( 4 ).type() == QVariant::Bool && expression.at( 4 ).toBool() == false )
2363  {
2364  // simple case, make a nice simple expression instead of a CASE statement
2365  if ( expression.at( 2 ).type() == QVariant::List || expression.at( 2 ).type() == QVariant::StringList )
2366  {
2367  QStringList parts;
2368  for ( const QVariant &p : expression.at( 2 ).toList() )
2369  {
2370  parts << QgsExpression::quotedValue( p );
2371  }
2372 
2373  if ( parts.size() > 1 )
2374  return QStringLiteral( "%1 IN (%2)" ).arg( QgsExpression::quotedColumnRef( attribute ), parts.join( ", " ) );
2375  else
2376  return QgsExpression::createFieldEqualityExpression( attribute, expression.at( 2 ).toList().value( 0 ) );
2377  }
2378  else if ( expression.at( 2 ).type() == QVariant::String || expression.at( 2 ).type() == QVariant::Int
2379  || expression.at( 2 ).type() == QVariant::Double )
2380  {
2381  return QgsExpression::createFieldEqualityExpression( attribute, expression.at( 2 ) );
2382  }
2383  else
2384  {
2385  context.pushWarning( QObject::tr( "%1: Skipping unsupported expression" ).arg( context.layerId() ) );
2386  return QString();
2387  }
2388  }
2389  else
2390  {
2391  QString caseString = QStringLiteral( "CASE " );
2392  for ( int i = 2; i < expression.size() - 2; i += 2 )
2393  {
2394  if ( expression.at( i ).type() == QVariant::List || expression.at( i ).type() == QVariant::StringList )
2395  {
2396  QStringList parts;
2397  for ( const QVariant &p : expression.at( i ).toList() )
2398  {
2399  parts << QgsExpression::quotedValue( p );
2400  }
2401 
2402  if ( parts.size() > 1 )
2403  caseString += QStringLiteral( "WHEN %1 IN (%2) " ).arg( QgsExpression::quotedColumnRef( attribute ), parts.join( ", " ) );
2404  else
2405  caseString += QStringLiteral( "WHEN %1 " ).arg( QgsExpression::createFieldEqualityExpression( attribute, expression.at( i ).toList().value( 0 ) ) );
2406  }
2407  else if ( expression.at( i ).type() == QVariant::String || expression.at( i ).type() == QVariant::Int
2408  || expression.at( i ).type() == QVariant::Double )
2409  {
2410  caseString += QStringLiteral( "WHEN (%1) " ).arg( QgsExpression::createFieldEqualityExpression( attribute, expression.at( i ) ) );
2411  }
2412 
2413  caseString += QStringLiteral( "THEN %1 " ).arg( QgsExpression::quotedValue( expression.at( i + 1 ) ) );
2414  }
2415  caseString += QStringLiteral( "ELSE %1 END" ).arg( QgsExpression::quotedValue( expression.last() ) );
2416  return caseString;
2417  }
2418  }
2419  else if ( op == QLatin1String( "to-string" ) )
2420  {
2421  return QStringLiteral( "to_string(%1)" ).arg( parseExpression( expression.value( 1 ).toList(), context ) );
2422  }
2423  else
2424  {
2425  context.pushWarning( QObject::tr( "%1: Skipping unsupported expression" ).arg( context.layerId() ) );
2426  return QString();
2427  }
2428 }
2429 
2430 QImage QgsMapBoxGlStyleConverter::retrieveSprite( const QString &name, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize )
2431 {
2432  if ( context.spriteImage().isNull() )
2433  {
2434  context.pushWarning( QObject::tr( "%1: Could not retrieve sprite '%2'" ).arg( context.layerId(), name ) );
2435  return QImage();
2436  }
2437 
2438  const QVariantMap spriteDefinition = context.spriteDefinitions().value( name ).toMap();
2439  if ( spriteDefinition.size() == 0 )
2440  {
2441  context.pushWarning( QObject::tr( "%1: Could not retrieve sprite '%2'" ).arg( context.layerId(), name ) );
2442  return QImage();
2443  }
2444 
2445  const QImage sprite = context.spriteImage().copy( spriteDefinition.value( QStringLiteral( "x" ) ).toInt(),
2446  spriteDefinition.value( QStringLiteral( "y" ) ).toInt(),
2447  spriteDefinition.value( QStringLiteral( "width" ) ).toInt(),
2448  spriteDefinition.value( QStringLiteral( "height" ) ).toInt() );
2449  if ( sprite.isNull() )
2450  {
2451  context.pushWarning( QObject::tr( "%1: Could not retrieve sprite '%2'" ).arg( context.layerId(), name ) );
2452  return QImage();
2453  }
2454 
2455  spriteSize = sprite.size() / spriteDefinition.value( QStringLiteral( "pixelRatio" ) ).toDouble() * context.pixelSizeConversionFactor();
2456  return sprite;
2457 }
2458 
2459 QString QgsMapBoxGlStyleConverter::retrieveSpriteAsBase64( const QVariant &value, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize, QString &spriteProperty, QString &spriteSizeProperty )
2460 {
2461  QString spritePath;
2462 
2463  auto prepareBase64 = []( const QImage & sprite )
2464  {
2465  QString path;
2466  if ( !sprite.isNull() )
2467  {
2468  QByteArray blob;
2469  QBuffer buffer( &blob );
2470  buffer.open( QIODevice::WriteOnly );
2471  sprite.save( &buffer, "PNG" );
2472  buffer.close();
2473  QByteArray encoded = blob.toBase64();
2474  path = QString( encoded );
2475  path.prepend( QLatin1String( "base64:" ) );
2476  }
2477  return path;
2478  };
2479 
2480  switch ( value.type() )
2481  {
2482  case QVariant::String:
2483  {
2484  QString spriteName = value.toString();
2485  QRegularExpression fieldNameMatch( QStringLiteral( "{([^}]+)}" ) );
2486  QRegularExpressionMatch match = fieldNameMatch.match( spriteName );
2487  if ( match.hasMatch() )
2488  {
2489  const QString fieldName = match.captured( 1 );
2490  spriteProperty = QStringLiteral( "CASE" );
2491  spriteSizeProperty = QStringLiteral( "CASE" );
2492 
2493  spriteName.replace( "(", QLatin1String( "\\(" ) );
2494  spriteName.replace( ")", QLatin1String( "\\)" ) );
2495  spriteName.replace( fieldNameMatch, QStringLiteral( "([^\\/\\\\]+)" ) );
2496  QRegularExpression fieldValueMatch( spriteName );
2497  const QStringList spriteNames = context.spriteDefinitions().keys();
2498  for ( const QString &name : spriteNames )
2499  {
2500  match = fieldValueMatch.match( name );
2501  if ( match.hasMatch() )
2502  {
2503  QSize size;
2504  QString path;
2505  const QString fieldValue = match.captured( 1 );
2506  const QImage sprite = retrieveSprite( name, context, size );
2507  path = prepareBase64( sprite );
2508  if ( spritePath.isEmpty() && !path.isEmpty() )
2509  {
2510  spritePath = path;
2511  spriteSize = size;
2512  }
2513 
2514  spriteProperty += QStringLiteral( " WHEN \"%1\" = '%2' THEN '%3'" )
2515  .arg( fieldName, fieldValue, path );
2516  spriteSizeProperty += QStringLiteral( " WHEN \"%1\" = '%2' THEN %3" )
2517  .arg( fieldName ).arg( fieldValue ).arg( size.width() );
2518  }
2519  }
2520 
2521  spriteProperty += QLatin1String( " END" );
2522  spriteSizeProperty += QLatin1String( " END" );
2523  }
2524  else
2525  {
2526  spriteProperty.clear();
2527  spriteSizeProperty.clear();
2528  const QImage sprite = retrieveSprite( spriteName, context, spriteSize );
2529  spritePath = prepareBase64( sprite );
2530  }
2531  break;
2532  }
2533 
2534  case QVariant::Map:
2535  {
2536  const QVariantList stops = value.toMap().value( QStringLiteral( "stops" ) ).toList();
2537  if ( stops.size() == 0 )
2538  break;
2539 
2540  QString path;
2541  QSize size;
2542  QImage sprite;
2543 
2544  sprite = retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, spriteSize );
2545  spritePath = prepareBase64( sprite );
2546 
2547  spriteProperty = QStringLiteral( "CASE WHEN @vector_tile_zoom < %1 THEN '%2'" )
2548  .arg( stops.value( 0 ).toList().value( 0 ).toString() )
2549  .arg( spritePath );
2550  spriteSizeProperty = QStringLiteral( "CASE WHEN @vector_tile_zoom < %1 THEN %2" )
2551  .arg( stops.value( 0 ).toList().value( 0 ).toString() )
2552  .arg( spriteSize.width() );
2553 
2554  for ( int i = 0; i < stops.size() - 1; ++i )
2555  {
2556  ;
2557  sprite = retrieveSprite( stops.value( 0 ).toList().value( 1 ).toString(), context, size );
2558  path = prepareBase64( sprite );
2559 
2560  spriteProperty += QStringLiteral( " WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
2561  "THEN '%3'" )
2562  .arg( stops.value( i ).toList().value( 0 ).toString(),
2563  stops.value( i + 1 ).toList().value( 0 ).toString(),
2564  path );
2565  spriteSizeProperty += QStringLiteral( " WHEN @vector_tile_zoom >= %1 AND @vector_tile_zoom < %2 "
2566  "THEN %3" )
2567  .arg( stops.value( i ).toList().value( 0 ).toString(),
2568  stops.value( i + 1 ).toList().value( 0 ).toString() )
2569  .arg( size.width() );
2570  }
2571  sprite = retrieveSprite( stops.last().toList().value( 1 ).toString(), context, size );
2572  path = prepareBase64( sprite );
2573 
2574  spriteProperty += QStringLiteral( " WHEN @vector_tile_zoom >= %1 "
2575  "THEN '%2' END" )
2576  .arg( stops.last().toList().value( 0 ).toString() )
2577  .arg( path );
2578  spriteSizeProperty += QStringLiteral( " WHEN @vector_tile_zoom >= %1 "
2579  "THEN %2 END" )
2580  .arg( stops.last().toList().value( 0 ).toString() )
2581  .arg( size.width() );
2582  break;
2583  }
2584 
2585  case QVariant::List:
2586  {
2587  const QVariantList json = value.toList();
2588  const QString method = json.value( 0 ).toString();
2589  if ( method != QLatin1String( "match" ) )
2590  {
2591  context.pushWarning( QObject::tr( "%1: Could not interpret sprite value list with method %2" ).arg( context.layerId(), method ) );
2592  break;
2593  }
2594 
2595  const QString attribute = parseExpression( json.value( 1 ).toList(), context );
2596  if ( attribute.isEmpty() )
2597  {
2598  context.pushWarning( QObject::tr( "%1: Could not interpret match list" ).arg( context.layerId() ) );
2599  break;
2600  }
2601 
2602  spriteProperty = QStringLiteral( "CASE " );
2603  spriteSizeProperty = QStringLiteral( "CASE " );
2604 
2605  for ( int i = 2; i < json.length() - 1; i += 2 )
2606  {
2607  const QVariantList keys = json.value( i ).toList();
2608 
2609  QStringList matchString;
2610  for ( const QVariant &key : keys )
2611  {
2612  matchString << QgsExpression::quotedValue( key );
2613  }
2614 
2615  const QVariant value = json.value( i + 1 );
2616 
2617  const QImage sprite = retrieveSprite( value.toString(), context, spriteSize );
2618  spritePath = prepareBase64( sprite );
2619 
2620  spriteProperty += QStringLiteral( " WHEN %1 IN (%2) "
2621  "THEN '%3' " ).arg( attribute,
2622  matchString.join( ',' ),
2623  spritePath );
2624 
2625  spriteSizeProperty += QStringLiteral( " WHEN %1 IN (%2) "
2626  "THEN %3 " ).arg( attribute,
2627  matchString.join( ',' ) ).arg( spriteSize.width() );
2628  }
2629 
2630  const QImage sprite = retrieveSprite( json.constLast().toString(), context, spriteSize );
2631  spritePath = prepareBase64( sprite );
2632 
2633  spriteProperty += QStringLiteral( "ELSE %1 END" ).arg( spritePath );
2634  spriteSizeProperty += QStringLiteral( "ELSE %3 END" ).arg( spriteSize.width() );
2635  break;
2636  }
2637 
2638  default:
2639  context.pushWarning( QObject::tr( "%1: Skipping unsupported sprite type (%2)." ).arg( context.layerId(), QMetaType::typeName( value.type() ) ) );
2640  break;
2641  }
2642 
2643  return spritePath;
2644 }
2645 
2646 QString QgsMapBoxGlStyleConverter::parseValue( const QVariant &value, QgsMapBoxGlStyleConversionContext &context )
2647 {
2648  switch ( value.type() )
2649  {
2650  case QVariant::List:
2651  case QVariant::StringList:
2652  return parseExpression( value.toList(), context );
2653 
2654  case QVariant::String:
2655  return QgsExpression::quotedValue( value.toString() );
2656 
2657  case QVariant::Int:
2658  case QVariant::Double:
2659  return value.toString();
2660 
2661  default:
2662  context.pushWarning( QObject::tr( "%1: Skipping unsupported expression part" ).arg( context.layerId() ) );
2663  break;
2664  }
2665  return QString();
2666 }
2667 
2668 QString QgsMapBoxGlStyleConverter::parseKey( const QVariant &value )
2669 {
2670  if ( value.toString() == QLatin1String( "$type" ) )
2671  return QStringLiteral( "_geom_type" );
2672  else if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
2673  {
2674  if ( value.toList().size() > 1 )
2675  return value.toList().at( 1 ).toString();
2676  else
2677  return value.toList().value( 0 ).toString();
2678  }
2679  return QgsExpression::quotedColumnRef( value.toString() );
2680 }
2681 
2683 {
2684  return mRenderer ? mRenderer->clone() : nullptr;
2685 }
2686 
2688 {
2689  return mLabeling ? mLabeling->clone() : nullptr;
2690 }
2691 
2692 //
2693 // QgsMapBoxGlStyleConversionContext
2694 //
2696 {
2697  QgsDebugMsg( warning );
2698  mWarnings << warning;
2699 }
2700 
2702 {
2703  return mTargetUnit;
2704 }
2705 
2707 {
2708  mTargetUnit = targetUnit;
2709 }
2710 
2712 {
2713  return mSizeConversionFactor;
2714 }
2715 
2717 {
2718  mSizeConversionFactor = sizeConversionFactor;
2719 }
2720 
2722 {
2723  return mSpriteImage;
2724 }
2725 
2727 {
2728  return mSpriteDefinitions;
2729 }
2730 
2731 void QgsMapBoxGlStyleConversionContext::setSprites( const QImage &image, const QVariantMap &definitions )
2732 {
2733  mSpriteImage = image;
2734  mSpriteDefinitions = definitions;
2735 }
2736 
2737 void QgsMapBoxGlStyleConversionContext::setSprites( const QImage &image, const QString &definitions )
2738 {
2739  setSprites( image, QgsJsonUtils::parseJson( definitions ).toMap() );
2740 }
2741 
2743 {
2744  return mLayerId;
2745 }
2746 
2748 {
2749  mLayerId = value;
2750 }
QgsSymbolLayerUtils::parseColor
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,...
Definition: qgssymbollayerutils.cpp:3581
QgsTextBufferSettings::setSizeUnit
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the buffer size.
Definition: qgstextbuffersettings.cpp:97
QgsMapBoxGlStyleConversionContext::setTargetUnit
void setTargetUnit(QgsUnitTypes::RenderUnit targetUnit)
Sets the target unit type.
Definition: qgsmapboxglstyleconverter.cpp:2706
QgsVectorTileBasicLabelingStyle::setGeometryType
void setGeometryType(QgsWkbTypes::GeometryType geomType)
Sets type of the geometry that will be used (point / line / polygon)
Definition: qgsvectortilebasiclabeling.h:51
QgsMarkerSymbolLayer::setSizeUnit
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol's size.
Definition: qgssymbollayer.h:670
QgsRasterFillSymbolLayer::setWidthUnit
void setWidthUnit(const QgsUnitTypes::RenderUnit unit)
Sets the units for the image's width.
Definition: qgsfillsymbollayer.h:967
QgsMapBoxGlStyleConverter::renderer
QgsVectorTileRenderer * renderer() const
Returns a new instance of a vector tile renderer representing the converted style,...
Definition: qgsmapboxglstyleconverter.cpp:2682
QgsPalLayerSettings::distUnits
QgsUnitTypes::RenderUnit distUnits
Units the distance from feature to the label.
Definition: qgspallabeling.h:674
QgsExpression::quotedString
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
Definition: qgsexpression.cpp:70
QgsMapBoxGlStyleConverter::parseStringStops
static QString parseStringStops(const QVariantList &stops, QgsMapBoxGlStyleConversionContext &context, const QVariantMap &conversionMap, QString *defaultString=nullptr)
Parses a list of interpolation stops containing string values.
Definition: qgsmapboxglstyleconverter.cpp:1988
QgsMapBoxGlStyleConverter::parseInterpolateOpacityByZoom
static QgsProperty parseInterpolateOpacityByZoom(const QVariantMap &json, int maxOpacity)
Interpolates opacity with either scale_linear() or scale_exp() (depending on base value).
Definition: qgsmapboxglstyleconverter.cpp:1825
QgsTextFormat::setFont
void setFont(const QFont &font)
Sets the font used for rendering text.
Definition: qgstextformat.cpp:177
QgsProperty
A store for object properties.
Definition: qgsproperty.h:232
QgsPalLayerSettings::OffsetXY
@ OffsetXY
Definition: qgspallabeling.h:435
QgsUnitTypes::RenderUnit
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:167
QgsMapBoxGlStyleConverter::parseSymbolLayerAsRenderer
static bool parseSymbolLayerAsRenderer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as a renderer.
Definition: qgsmapboxglstyleconverter.cpp:1438
QgsBlurEffect
A paint effect which blurs a source picture, using a number of different blur methods.
Definition: qgsblureffect.h:35
QgsSymbol::defaultSymbol
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
Definition: qgssymbol.cpp:320
QgsMapBoxGlStyleConversionContext::setLayerId
void setLayerId(const QString &value)
Sets the layer ID of the layer currently being converted.
Definition: qgsmapboxglstyleconverter.cpp:2747
QgsLabeling::OnLine
@ OnLine
Labels can be placed directly over a line feature.
Definition: qgslabeling.h:40
QgsMapBoxGlStyleConverter::~QgsMapBoxGlStyleConverter
~QgsMapBoxGlStyleConverter()
QgsPalLayerSettings::BufferSize
@ BufferSize
Definition: qgspallabeling.h:380
QgsTextBackgroundSettings::setSizeType
void setSizeType(SizeType type)
Sets the method used to determine the size of the background shape (e.g., fixed size or buffer around...
Definition: qgstextbackgroundsettings.cpp:135
QgsMapBoxGlStyleConverter::QgsMapBoxGlStyleConverter
QgsMapBoxGlStyleConverter()
Constructor for QgsMapBoxGlStyleConverter.
Definition: qgsmapboxglstyleconverter.cpp:38
QgsVectorTileBasicLabelingStyle::setLabelSettings
void setLabelSettings(const QgsPalLayerSettings &settings)
Sets labeling configuration of this style.
Definition: qgsvectortilebasiclabeling.h:36
QgsPalLayerSettings::Color
@ Color
Text color.
Definition: qgspallabeling.h:351
QgsPalLayerSettings
Definition: qgspallabeling.h:207
QgsVectorTileBasicRendererStyle::setEnabled
void setEnabled(bool enabled)
Sets whether this style is enabled (used for rendering)
Definition: qgsvectortilebasicrenderer.h:83
QgsTextBufferSettings::setEnabled
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
Definition: qgstextbuffersettings.cpp:77
QgsTextFormat::buffer
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
Definition: qgstextformat.cpp:103
QgsBlurEffect::setBlurUnit
void setBlurUnit(const QgsUnitTypes::RenderUnit unit)
Sets the units used for the blur level (radius).
Definition: qgsblureffect.h:93
QgsMapBoxGlStyleConverter::parseExpression
static QString parseExpression(const QVariantList &expression, QgsMapBoxGlStyleConversionContext &context)
Converts a MapBox GL expression to a QGIS expression.
Definition: qgsmapboxglstyleconverter.cpp:2272
QgsSimpleLineSymbolLayer::setPenJoinStyle
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the pen join style used to render the line (e.g.
Definition: qgslinesymbollayer.h:113
QgsSymbolLayer::setColor
virtual void setColor(const QColor &color)
The fill color.
Definition: qgssymbollayer.h:232
QgsMapBoxGlStyleConverter::retrieveSprite
static QImage retrieveSprite(const QString &name, QgsMapBoxGlStyleConversionContext &context, QSize &spriteSize)
Retrieves the sprite image with the specified name, taken from the specified context.
Definition: qgsmapboxglstyleconverter.cpp:2430
QgsSymbolLayer::PropertyFillColor
@ PropertyFillColor
Fill color.
Definition: qgssymbollayer.h:135
QgsVectorTileBasicLabelingStyle
Configuration of a single style within QgsVectorTileBasicLabeling.
Definition: qgsvectortilebasiclabeling.h:32
QgsLineSymbolLayer::setWidth
virtual void setWidth(double width)
Sets the width of the line symbol layer.
Definition: qgssymbollayer.h:954
QgsMapBoxGlStyleConverter::parseInterpolateListByZoom
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.
Definition: qgsmapboxglstyleconverter.cpp:2146
QgsMarkerLineSymbolLayer::setOutputUnit
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Definition: qgslinesymbollayer.cpp:2372
qgssymbollayerutils.h
QgsVectorTileBasicRendererStyle::setSymbol
void setSymbol(QgsSymbol *sym)
Sets symbol for rendering. Takes ownership of the symbol.
Definition: qgsvectortilebasicrenderer.cpp:54
QgsMarkerSymbolLayer::setAngle
void setAngle(double angle)
Sets the rotation angle for the marker.
Definition: qgssymbollayer.h:627
QgsMapBoxGlStyleConverter::parseLineLayer
static bool parseLineLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context)
Parses a line layer.
Definition: qgsmapboxglstyleconverter.cpp:397
QgsTextBackgroundSettings::setMarkerSymbol
void setMarkerSymbol(QgsMarkerSymbol *symbol)
Sets the current marker symbol for the background shape.
Definition: qgstextbackgroundsettings.cpp:125
QgsSimpleFillSymbolLayer::setBrushStyle
void setBrushStyle(Qt::BrushStyle style)
Definition: qgsfillsymbollayer.h:79
QgsPalLayerSettings::obstacleSettings
const QgsLabelObstacleSettings & obstacleSettings() const
Returns the label obstacle settings.
Definition: qgspallabeling.h:1048
QgsMapBoxGlStyleConverter::parseColor
static QColor parseColor(const QVariant &color, QgsMapBoxGlStyleConversionContext &context)
Parses a color in one of these supported formats:
Definition: qgsmapboxglstyleconverter.cpp:2204
QgsPalLayerSettings::yOffset
double yOffset
Vertical offset of label.
Definition: qgspallabeling.h:726
QgsRasterFillSymbolLayer::setOpacity
void setOpacity(double opacity)
Sets the opacity for the raster image used in the fill.
Definition: qgsfillsymbollayer.cpp:4182
qgsmarkersymbollayer.h
QgsVectorTileBasicRenderer
The default vector tile renderer implementation.
Definition: qgsvectortilebasicrenderer.h:128
QgsTextBackgroundSettings
Container for settings relating to a text background object.
Definition: qgstextbackgroundsettings.h:46
QgsMapBoxGlStyleConverter::NoLayerList
@ NoLayerList
No layer list was found in JSON input.
Definition: qgsmapboxglstyleconverter.h:191
qgstextbackgroundsettings.h
QgsRasterFillSymbolLayer
A class for filling symbols with a repeated raster image.
Definition: qgsfillsymbollayer.h:800
QgsVectorTileBasicLabeling
Basic labeling configuration for vector tile layers.
Definition: qgsvectortilebasiclabeling.h:108
QgsMapBoxGlStyleConverter::interpolateExpression
static QString interpolateExpression(double zoomMin, double zoomMax, double valueMin, double valueMax, double base, double multiplier=1)
Generates an interpolation for values between valueMin and valueMax, scaled between the ranges zoomMi...
Definition: qgsmapboxglstyleconverter.cpp:2223
QgsMapBoxGlStyleConverter::parseJoinStyle
static Qt::PenJoinStyle parseJoinStyle(const QString &style)
Converts a value to Qt::PenJoinStyle enum from JSON value.
Definition: qgsmapboxglstyleconverter.cpp:2262
QgsSymbolLayer::PropertyInterval
@ PropertyInterval
Line marker interval.
Definition: qgssymbollayer.h:171
QgsRasterMarkerSymbolLayer::setOpacity
void setOpacity(double opacity)
Set the marker opacity.
Definition: qgsmarkersymbollayer.h:719
QgsProperty::fromExpression
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
Definition: qgsproperty.cpp:212
QgsSimpleLineSymbolLayer::setUseCustomDashPattern
void setUseCustomDashPattern(bool b)
Sets whether the line uses a custom dash pattern.
Definition: qgslinesymbollayer.h:143
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsMarkerLineSymbolLayer
Line symbol layer type which draws repeating marker symbols along a line feature.
Definition: qgslinesymbollayer.h:694
qgsfontutils.h
QgsVectorTileBasicLabelingStyle::setMaxZoomLevel
void setMaxZoomLevel(int maxZoom)
Sets maximum zoom level index (negative number means no limit)
Definition: qgsvectortilebasiclabeling.h:71
qgspainteffect.h
QgsMarkerLineSymbolLayer::setSubSymbol
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
Definition: qgslinesymbollayer.cpp:2087
QgsSymbolLayer::PropertyStrokeColor
@ PropertyStrokeColor
Stroke color.
Definition: qgssymbollayer.h:136
QgsPalLayerSettings::LabelDistance
@ LabelDistance
Definition: qgspallabeling.h:437
QgsMapBoxGlStyleConverter::parseInterpolateColorByZoom
static QgsProperty parseInterpolateColorByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, QColor *defaultColor=nullptr)
Parses a color value which is interpolated by zoom range.
Definition: qgsmapboxglstyleconverter.cpp:1741
QgsSimpleLineSymbolLayer::setOutputUnit
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Definition: qgslinesymbollayer.cpp:45
QgsLineSymbolLayer::setOffset
void setOffset(double offset)
Sets the line's offset.
Definition: qgssymbollayer.h:999
QgsPalLayerSettings::dist
double dist
Distance from feature to the label.
Definition: qgspallabeling.h:667
QgsTemplatedLineSymbolLayerBase::setPlacement
void setPlacement(Placement placement)
Sets the placement of the symbols.
Definition: qgslinesymbollayer.h:476
QgsPalLayerSettings::OverPoint
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
Definition: qgspallabeling.h:224
QgsWkbTypes::PolygonGeometry
@ PolygonGeometry
Definition: qgswkbtypes.h:144
QgsMapBoxGlStyleConverter::retrieveSpriteAsBase64
static QString retrieveSpriteAsBase64(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...
Definition: qgsmapboxglstyleconverter.cpp:2459
QgsProperty::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...
Definition: qgsproperty.cpp:519
QgsMapBoxGlStyleConversionContext::layerId
QString layerId() const
Returns the layer ID of the layer currently being converted.
Definition: qgsmapboxglstyleconverter.cpp:2742
qgsmapboxglstyleconverter.h
QgsPropertyCollection::property
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
Definition: qgspropertycollection.cpp:214
QgsPalLayerSettings::QuadrantAboveRight
@ QuadrantAboveRight
Definition: qgspallabeling.h:289
QgsProperty::expressionString
QString expressionString() const
Returns the expression used for the property value.
Definition: qgsproperty.cpp:323
QgsMapBoxGlStyleConverter::convert
Result convert(const QVariantMap &style, QgsMapBoxGlStyleConversionContext *context=nullptr)
Converts a JSON style map, and returns the resultant status of the conversion.
Definition: qgsmapboxglstyleconverter.cpp:42
QgsMapBoxGlStyleConverter::Numeric
@ Numeric
Numeric property (e.g. line width, text size)
Definition: qgsmapboxglstyleconverter.h:257
QgsSimpleLineSymbolLayer::setCustomDashVector
void setCustomDashVector(const QVector< qreal > &vector)
Sets the custom dash vector, which is the pattern of alternating drawn/skipped lengths used while ren...
Definition: qgslinesymbollayer.h:195
QgsMapBoxGlStyleConverter::Success
@ Success
Conversion was successful.
Definition: qgsmapboxglstyleconverter.h:190
QgsExpression::quotedValue
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
Definition: qgsexpression.cpp:79
QgsMapBoxGlStyleConverter::PropertyType
PropertyType
Property types, for interpolated value conversion.
Definition: qgsmapboxglstyleconverter.h:255
QgsPalLayerSettings::QuadrantBelowLeft
@ QuadrantBelowLeft
Definition: qgspallabeling.h:293
QgsMapBoxGlStyleConversionContext
Context for a MapBox GL style conversion operation.
Definition: qgsmapboxglstyleconverter.h:37
QgsTextFormat::setBackground
void setBackground(const QgsTextBackgroundSettings &backgroundSettings)
Sets the text's background settings.q.
Definition: qgstextformat.cpp:121
QgsPalLayerSettings::xOffset
double xOffset
Horizontal offset of label.
Definition: qgspallabeling.h:718
QgsVectorTileBasicRendererStyle
Definition of map rendering of a subset of vector tile data.
Definition: qgsvectortilebasicrenderer.h:48
QgsTextFormat
Container for all settings relating to text rendering.
Definition: qgstextformat.h:40
QgsTextFormat::setColor
void setColor(const QColor &color)
Sets the color that text will be rendered in.
Definition: qgstextformat.cpp:237
QgsLabelObstacleSettings::setFactor
void setFactor(double factor)
Sets the obstacle factor, where 1.0 = default, < 1.0 more likely to be covered by labels,...
Definition: qgslabelobstaclesettings.h:97
QgsSymbolLayer::PropertyOffset
@ PropertyOffset
Symbol offset.
Definition: qgssymbollayer.h:139
QgsPalLayerSettings::Curved
@ Curved
Arranges candidates following the curvature of a line feature. Applies to line layers only.
Definition: qgspallabeling.h:226
QgsVectorTileBasicLabelingStyle::setEnabled
void setEnabled(bool enabled)
Sets whether this style is enabled (used for rendering)
Definition: qgsvectortilebasiclabeling.h:61
QgsVectorTileBasicLabelingStyle::setLayerName
void setLayerName(const QString &name)
Sets name of the sub-layer to render (empty layer means that all layers match)
Definition: qgsvectortilebasiclabeling.h:46
QgsMapBoxGlStyleConverter::parseLayers
void parseLayers(const QVariantList &layers, QgsMapBoxGlStyleConversionContext *context=nullptr)
Parse list of layers from JSON.
Definition: qgsmapboxglstyleconverter.cpp:65
QgsPalLayerSettings::FontLetterSpacing
@ FontLetterSpacing
Letter spacing.
Definition: qgspallabeling.h:359
QgsMapBoxGlStyleConversionContext::warnings
QStringList warnings() const
Returns a list of warning messages generated during the conversion.
Definition: qgsmapboxglstyleconverter.h:48
QgsPalLayerSettings::QuadrantLeft
@ QuadrantLeft
Definition: qgspallabeling.h:290
QgsPalLayerSettings::setDataDefinedProperties
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the label's property collection, used for data defined overrides.
Definition: qgspallabeling.h:977
QgsVectorTileRenderer
Abstract base class for all vector tile renderer implementations.
Definition: qgsvectortilerenderer.h:89
QgsMapBoxGlStyleConversionContext::clearWarnings
void clearWarnings()
Clears the list of warning messages.
Definition: qgsmapboxglstyleconverter.h:53
QgsMarkerSymbol
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:931
QgsMapBoxGlStyleConverter::parseStops
static QString parseStops(double base, const QVariantList &stops, double multiplier, QgsMapBoxGlStyleConversionContext &context)
Parses a list of interpolation stops.
Definition: qgsmapboxglstyleconverter.cpp:1951
QgsBlurEffect::StackBlur
@ StackBlur
Stack blur, a fast but low quality blur. Valid blur level values are between 0 - 16.
Definition: qgsblureffect.h:42
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
QgsPalLayerSettings::BufferColor
@ BufferColor
Definition: qgspallabeling.h:382
QgsPalLayerSettings::autoWrapLength
int autoWrapLength
If non-zero, indicates that label text should be automatically wrapped to (ideally) the specified num...
Definition: qgspallabeling.h:577
QgsLineSymbolLayer::setOffsetUnit
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit for the line's offset.
Definition: qgssymbollayer.h:1007
QgsPalLayerSettings::multilineAlign
MultiLineAlign multilineAlign
Horizontal alignment of multi-line labels.
Definition: qgspallabeling.h:592
QgsMapBoxGlStyleConversionContext::targetUnit
QgsUnitTypes::RenderUnit targetUnit() const
Returns the target unit type.
Definition: qgsmapboxglstyleconverter.cpp:2701
QgsSimpleLineSymbolLayer
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
Definition: qgslinesymbollayer.h:40
QgsTextBufferSettings::setPaintEffect
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the buffer.
Definition: qgstextbuffersettings.cpp:167
QgsMapBoxGlStyleConverter::Result
Result
Result of conversion.
Definition: qgsmapboxglstyleconverter.h:189
QgsMapBoxGlStyleConverter::parseSymbolLayer
static void parseSymbolLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &rendererStyle, bool &hasRenderer, QgsVectorTileBasicLabelingStyle &labelingStyle, bool &hasLabeling, QgsMapBoxGlStyleConversionContext &context)
Parses a symbol layer as renderer or labeling.
Definition: qgsmapboxglstyleconverter.cpp:626
QgsMapBoxGlStyleConversionContext::setSprites
void setSprites(const QImage &image, const QVariantMap &definitions)
Sets the sprite image and definitions JSON to use during conversion.
Definition: qgsmapboxglstyleconverter.cpp:2731
QgsVectorTileBasicRendererStyle::setFilterExpression
void setFilterExpression(const QString &expr)
Sets filter expression (empty filter means that all features match)
Definition: qgsvectortilebasicrenderer.h:73
QgsSimpleFillSymbolLayer::setOutputUnit
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Definition: qgsfillsymbollayer.cpp:60
QgsRasterFillSymbolLayer::Viewport
@ Viewport
Tiling is based on complete map viewport.
Definition: qgsfillsymbollayer.h:807
QgsPalLayerSettings::setFormat
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
Definition: qgspallabeling.h:992
QgsPalLayerSettings::placement
Placement placement
Definition: qgspallabeling.h:618
QgsMapBoxGlStyleConversionContext::pixelSizeConversionFactor
double pixelSizeConversionFactor() const
Returns the pixel size conversion factor, used to scale the original pixel sizes when converting styl...
Definition: qgsmapboxglstyleconverter.cpp:2711
QgsMapBoxGlStyleConverter::colorAsHslaComponents
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...
Definition: qgsmapboxglstyleconverter.cpp:2215
QgsRasterMarkerSymbolLayer::setPath
void setPath(const QString &path)
Set the marker raster image path.
Definition: qgsmarkersymbollayer.cpp:2627
QgsSimpleLineSymbolLayer::setPenCapStyle
void setPenCapStyle(Qt::PenCapStyle style)
Sets the pen cap style used to render the line (e.g.
Definition: qgslinesymbollayer.h:127
QgsRasterMarkerSymbolLayer
Raster marker symbol layer class.
Definition: qgsmarkersymbollayer.h:653
QgsPalLayerSettings::lineSettings
const QgsLabelLineSettings & lineSettings() const
Returns the label line settings, which contain settings related to how the label engine places and fo...
Definition: qgspallabeling.h:1022
qgssymbollayer.h
QgsMapBoxGlStyleConversionContext::spriteDefinitions
QVariantMap spriteDefinitions() const
Returns the sprite definitions to use during conversion.
Definition: qgsmapboxglstyleconverter.cpp:2726
QgsPalLayerSettings::AutoWrapLength
@ AutoWrapLength
Definition: qgspallabeling.h:365
QgsSimpleFillSymbolLayer::setOffsetUnit
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit for the fill's offset.
Definition: qgsfillsymbollayer.h:137
QgsSymbolLayer::PropertyName
@ PropertyName
Name, eg shape name for simple markers.
Definition: qgssymbollayer.h:134
typeName
const QString & typeName
Definition: qgswfsgetfeature.cpp:55
qgseffectstack.h
QgsTextBackgroundSettings::setSizeUnit
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the shape's size.
Definition: qgstextbackgroundsettings.cpp:155
qgsfillsymbollayer.h
QgsPaintEffect::setEnabled
void setEnabled(bool enabled)
Sets whether the effect is enabled.
Definition: qgspainteffect.cpp:44
QgsSymbolLayer::PropertyStrokeWidth
@ PropertyStrokeWidth
Stroke width.
Definition: qgssymbollayer.h:137
QgsMapBoxGlStyleConverter::labeling
QgsVectorTileLabeling * labeling() const
Returns a new instance of a vector tile labeling representing the converted style,...
Definition: qgsmapboxglstyleconverter.cpp:2687
QgsMapBoxGlStyleConverter::parseValueList
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.
Definition: qgsmapboxglstyleconverter.cpp:2023
QgsVectorTileBasicRendererStyle::setLayerName
void setLayerName(const QString &name)
Sets name of the sub-layer to render (empty layer means that all layers match)
Definition: qgsvectortilebasicrenderer.h:63
QgsPalLayerSettings::Horizontal
@ Horizontal
Arranges horizontal candidates scattered throughout a polygon feature. Applies to polygon layers only...
Definition: qgspallabeling.h:227
QgsMapBoxGlStyleConversionContext::pushWarning
void pushWarning(const QString &warning)
Pushes a warning message generated during the conversion.
Definition: qgsmapboxglstyleconverter.cpp:2695
QgsPalLayerSettings::MultiLeft
@ MultiLeft
Definition: qgspallabeling.h:317
QgsMapBoxGlStyleConverter::parseInterpolatePointByZoom
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).
Definition: qgsmapboxglstyleconverter.cpp:1873
QgsPalLayerSettings::QuadrantBelowRight
@ QuadrantBelowRight
Definition: qgspallabeling.h:295
QgsPalLayerSettings::QuadrantBelow
@ QuadrantBelow
Definition: qgspallabeling.h:294
QgsVectorTileBasicLabelingStyle::setStyleName
void setStyleName(const QString &name)
Sets human readable name of this style.
Definition: qgsvectortilebasiclabeling.h:41
QgsMapBoxGlStyleConverter::Opacity
@ Opacity
Opacity property.
Definition: qgsmapboxglstyleconverter.h:258
QgsMapBoxGlStyleConversionContext::setPixelSizeConversionFactor
void setPixelSizeConversionFactor(double sizeConversionFactor)
Sets the pixel size conversion factor, used to scale the original pixel sizes when converting styles.
Definition: qgsmapboxglstyleconverter.cpp:2716
QgsPropertyCollection
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
Definition: qgspropertycollection.h:319
QgsPalLayerSettings::Size
@ Size
Label size.
Definition: qgspallabeling.h:347
QgsMapBoxGlStyleConverter::Color
@ Color
Color property.
Definition: qgsmapboxglstyleconverter.h:256
QgsPalLayerSettings::priority
int priority
Label priority.
Definition: qgspallabeling.h:766
QgsWkbTypes::LineGeometry
@ LineGeometry
Definition: qgswkbtypes.h:143
QgsWkbTypes::PointGeometry
@ PointGeometry
Definition: qgswkbtypes.h:142
QgsPalLayerSettings::fieldName
QString fieldName
Name of field (or an expression) to use for label text.
Definition: qgspallabeling.h:539
QgsSymbolLayer::setDataDefinedProperties
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the symbol layer's property collection, used for data defined overrides.
Definition: qgssymbollayer.h:501
QgsTextBackgroundSettings::setType
void setType(ShapeType type)
Sets the type of background shape to draw (e.g., square, ellipse, SVG).
Definition: qgstextbackgroundsettings.cpp:105
QgsPalLayerSettings::ShapeSizeX
@ ShapeSizeX
Definition: qgspallabeling.h:400
qgslinesymbollayer.h
QgsExpression::createFieldEqualityExpression
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
Definition: qgsexpression.cpp:1079
QgsVectorTileBasicLabelingStyle::setFilterExpression
void setFilterExpression(const QString &expr)
Sets filter expression (empty filter means that all features match)
Definition: qgsvectortilebasiclabeling.h:56
QgsWkbTypes::GeometryType
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
QgsPropertyCollection::value
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection.
Definition: qgspropertycollection.cpp:228
QgsMapBoxGlStyleConverter::parseCapStyle
static Qt::PenCapStyle parseCapStyle(const QString &style)
Converts a value to Qt::PenCapStyle enum from JSON value.
Definition: qgsmapboxglstyleconverter.cpp:2252
QgsTextBackgroundSettings::SizeFixed
@ SizeFixed
Fixed size.
Definition: qgstextbackgroundsettings.h:68
QgsMapBoxGlStyleConverter::parseInterpolateByZoom
static QgsProperty parseInterpolateByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, double multiplier=1, double *defaultNumber=nullptr)
Parses a numeric value which is interpolated by zoom range.
Definition: qgsmapboxglstyleconverter.cpp:1799
QgsPalLayerSettings::Family
@ Family
Font family.
Definition: qgspallabeling.h:353
QgsSymbolLayerList
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:53
QgsMapBoxGlStyleConversionContext::spriteImage
QImage spriteImage() const
Returns the sprite image to use during conversion, or an invalid image if this is not set.
Definition: qgsmapboxglstyleconverter.cpp:2721
QgsPalLayerSettings::QuadrantRight
@ QuadrantRight
Definition: qgspallabeling.h:292
QgsRasterFillSymbolLayer::setWidth
void setWidth(const double width)
Sets the width for scaling the image used in the fill.
Definition: qgsfillsymbollayer.h:948
QgsEffectStack::appendEffect
void appendEffect(QgsPaintEffect *effect)
Appends an effect to the end of the stack.
Definition: qgseffectstack.cpp:215
QgsPalLayerSettings::quadOffset
QuadrantPosition quadOffset
Sets the quadrant in which to offset labels from feature.
Definition: qgspallabeling.h:710
QgsMapBoxGlStyleConverter::Point
@ Point
Point/offset property.
Definition: qgsmapboxglstyleconverter.h:259
QgsVectorTileBasicRendererStyle::setMinZoomLevel
void setMinZoomLevel(int minZoom)
Sets minimum zoom level index (negative number means no limit)
Definition: qgsvectortilebasicrenderer.h:88
QgsSymbolLayer::PropertyFile
@ PropertyFile
Filename, eg for svg files.
Definition: qgssymbollayer.h:162
QgsSimpleFillSymbolLayer
Definition: qgsfillsymbollayer.h:40
QgsSimpleFillSymbolLayer::setOffset
void setOffset(QPointF offset)
Sets an offset by which polygons will be translated during rendering.
Definition: qgsfillsymbollayer.h:104
QgsPalLayerSettings::FontStyle
@ FontStyle
Font style name.
Definition: qgspallabeling.h:354
QgsPalLayerSettings::MultiFollowPlacement
@ MultiFollowPlacement
Definition: qgspallabeling.h:320
QgsSymbolLayer::PropertyAngle
@ PropertyAngle
Symbol angle.
Definition: qgssymbollayer.h:133
QgsTextBufferSettings::setSize
void setSize(double size)
Sets the size of the buffer.
Definition: qgstextbuffersettings.cpp:87
qgsvectortilebasicrenderer.h
QgsExpression::quotedColumnRef
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
Definition: qgsexpression.cpp:65
QgsPalLayerSettings::isExpression
bool isExpression
true if this label is made from a expression string, e.g., FieldName || 'mm'
Definition: qgspallabeling.h:545
QgsLabeling::AboveLine
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
Definition: qgslabeling.h:41
QgsTextBufferSettings::setColor
void setColor(const QColor &color)
Sets the color for the buffer.
Definition: qgstextbuffersettings.cpp:117
QgsVectorTileBasicRendererStyle::setMaxZoomLevel
void setMaxZoomLevel(int maxZoom)
Sets maximum zoom level index (negative number means no limit)
Definition: qgsvectortilebasicrenderer.h:93
QgsPalLayerSettings::OffsetQuad
@ OffsetQuad
Definition: qgspallabeling.h:434
QgsTextFormat::font
QFont font() const
Returns the font used for rendering text.
Definition: qgstextformat.cpp:151
QgsPalLayerSettings::QuadrantOver
@ QuadrantOver
Definition: qgspallabeling.h:291
QgsPalLayerSettings::offsetUnits
QgsUnitTypes::RenderUnit offsetUnits
Units for offsets of label.
Definition: qgspallabeling.h:734
QgsPalLayerSettings::LinePlacementOptions
@ LinePlacementOptions
Line placement flags.
Definition: qgspallabeling.h:452
QgsTextFormat::setSizeUnit
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the size of rendered text.
Definition: qgstextformat.cpp:204
QgsMarkerSymbolLayer::setSize
virtual void setSize(double size)
Sets the symbol size.
Definition: qgssymbollayer.h:653
QgsPalLayerSettings::MultiRight
@ MultiRight
Definition: qgspallabeling.h:319
QgsVectorTileLabeling
Base class for labeling configuration classes for vector tile layers.
Definition: qgsvectortilelabeling.h:71
QgsVectorTileBasicRendererStyle::setStyleName
void setStyleName(const QString &name)
Sets human readable name of this style.
Definition: qgsvectortilebasicrenderer.h:58
QgsSymbolLayer::PropertyOpacity
@ PropertyOpacity
Opacity.
Definition: qgssymbollayer.h:167
QgsPropertyCollection::setProperty
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
Definition: qgspropertycollection.cpp:187
QgsTextBackgroundSettings::setSize
void setSize(QSizeF size)
Sets the size of the background shape.
Definition: qgstextbackgroundsettings.cpp:145
QgsRasterFillSymbolLayer::setImageFilePath
void setImageFilePath(const QString &imagePath)
Sets the path to the raster image used for the fill.
Definition: qgsfillsymbollayer.cpp:4172
QgsBlurEffect::setBlurLevel
void setBlurLevel(const double level)
Sets blur level (radius)
Definition: qgsblureffect.h:72
QgsRasterFillSymbolLayer::setCoordinateMode
void setCoordinateMode(FillCoordinateMode mode)
Set the coordinate mode for fill.
Definition: qgsfillsymbollayer.cpp:4177
QgsMapBoxGlStyleConverter::parseOpacityStops
static QString parseOpacityStops(double base, const QVariantList &stops, int maxOpacity)
Takes values from stops and uses either scale_linear() or scale_exp() functions to interpolate alpha ...
Definition: qgsmapboxglstyleconverter.cpp:1848
qgsjsonutils.h
qgslogger.h
QgsVectorTileBasicLabelingStyle::setMinZoomLevel
void setMinZoomLevel(int minZoom)
Sets minimum zoom level index (negative number means no limit)
Definition: qgsvectortilebasiclabeling.h:66
QgsMapBoxGlStyleConverter::parseFillLayer
static bool parseFillLayer(const QVariantMap &jsonLayer, QgsVectorTileBasicRendererStyle &style, QgsMapBoxGlStyleConversionContext &context)
Parses a fill layer.
Definition: qgsmapboxglstyleconverter.cpp:159
QgsTextFormat::setSize
void setSize(double size)
Sets the size for rendered text.
Definition: qgstextformat.cpp:226
QgsPalLayerSettings::QuadrantAboveLeft
@ QuadrantAboveLeft
Definition: qgspallabeling.h:287
QgsTextBackgroundSettings::ShapeMarkerSymbol
@ ShapeMarkerSymbol
Marker symbol.
Definition: qgstextbackgroundsettings.h:59
QgsPropertyCollection::isActive
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
Definition: qgspropertycollection.cpp:268
qgsblureffect.h
qgsvectortilebasiclabeling.h
QgsSimpleFillSymbolLayer::setStrokeColor
void setStrokeColor(const QColor &strokeColor) override
Set stroke color.
Definition: qgsfillsymbollayer.h:82
QgsLabelLineSettings::setPlacementFlags
void setPlacementFlags(QgsLabeling::LinePlacementFlags flags)
Returns the line placement flags, which dictate how line labels can be placed above or below the line...
Definition: qgslabellinesettings.h:76
QgsTemplatedLineSymbolLayerBase::CentralPoint
@ CentralPoint
Place symbols at the mid point of the line.
Definition: qgslinesymbollayer.h:393
QgsMapBoxGlStyleConverter::parseMatchList
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.
Definition: qgsmapboxglstyleconverter.cpp:2041
QgsLabeling::BelowLine
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
Definition: qgslabeling.h:42
QgsTextBackgroundSettings::setEnabled
void setEnabled(bool enabled)
Sets whether the text background will be drawn.
Definition: qgstextbackgroundsettings.cpp:95
QgsMapBoxGlStyleConverter::parsePointStops
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/...
Definition: qgsmapboxglstyleconverter.cpp:1917
QgsPalLayerSettings::QuadrantAbove
@ QuadrantAbove
Definition: qgspallabeling.h:288
QgsPalLayerSettings::MultiCenter
@ MultiCenter
Definition: qgspallabeling.h:318
QgsSimpleFillSymbolLayer::setFillColor
void setFillColor(const QColor &color) override
Set fill color.
Definition: qgsfillsymbollayer.h:85
QgsSimpleFillSymbolLayer::setStrokeStyle
void setStrokeStyle(Qt::PenStyle strokeStyle)
Definition: qgsfillsymbollayer.h:88
QgsSymbolLayer::PropertyWidth
@ PropertyWidth
Symbol width.
Definition: qgssymbollayer.h:141
QgsVectorTileBasicRendererStyle::setGeometryType
void setGeometryType(QgsWkbTypes::GeometryType geomType)
Sets type of the geometry that will be used (point / line / polygon)
Definition: qgsvectortilebasicrenderer.h:68
QgsBlurEffect::setBlurMethod
void setBlurMethod(const BlurMethod method)
Sets the blur method (algorithm) to use for performing the blur.
Definition: qgsblureffect.h:130
QgsJsonUtils::parseJson
static QVariant parseJson(const std::string &jsonString)
Converts JSON jsonString to a QVariant, in case of parsing error an invalid QVariant is returned.
Definition: qgsjsonutils.cpp:455
QgsEffectStack
A paint effect which consists of a stack of other chained paint effects.
Definition: qgseffectstack.h:45
QgsFontUtils::fontFamilyHasStyle
static bool fontFamilyHasStyle(const QString &family, const QString &style)
Check whether font family on system has specific style.
Definition: qgsfontutils.cpp:45
QgsMapBoxGlStyleConverter::parseInterpolateStringByZoom
static QgsProperty parseInterpolateStringByZoom(const QVariantMap &json, QgsMapBoxGlStyleConversionContext &context, const QVariantMap &conversionMap, QString *defaultString=nullptr)
Interpolates a string by zoom.
Definition: qgsmapboxglstyleconverter.cpp:1905