QGIS API Documentation  3.24.2-Tisler (13c1a02865)
qgslayertreemodellegendnode.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayertreemodellegendnode.cpp
3  --------------------------------------
4  Date : August 2014
5  Copyright : (C) 2014 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7 
8  QgsWMSLegendNode : Sandro Santilli < strk at keybit dot net >
9 
10  ***************************************************************************
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * *
17  ***************************************************************************/
18 
20 
22 #include "qgslayertree.h"
23 #include "qgslayertreemodel.h"
24 #include "qgslegendsettings.h"
25 #include "qgsrasterlayer.h"
26 #include "qgsrenderer.h"
27 #include "qgssymbollayerutils.h"
28 #include "qgsimageoperation.h"
29 #include "qgsvectorlayer.h"
30 #include "qgspointcloudlayer.h"
31 #include "qgspointcloudrenderer.h"
32 #include "qgsrasterrenderer.h"
34 #include "qgsfeatureid.h"
35 #include "qgslayoutitem.h"
37 #include "qgsexpression.h"
38 #include "qgstextrenderer.h"
39 #include "qgssettings.h"
40 #include "qgsfileutils.h"
41 #include "qgsmarkersymbol.h"
42 
43 #include <QBuffer>
44 
46  : QObject( parent )
47  , mLayerNode( nodeL )
48  , mEmbeddedInParent( false )
49 {
50 }
51 
53 {
54  return qobject_cast<QgsLayerTreeModel *>( parent() );
55 }
56 
58 {
59  return Qt::ItemIsEnabled;
60 }
61 
62 bool QgsLayerTreeModelLegendNode::setData( const QVariant &value, int role )
63 {
64  Q_UNUSED( value )
65  Q_UNUSED( role )
66  return false;
67 }
68 
70 {
71  if ( mEmbeddedInParent )
72  return mLayerNode->patchSize();
73 
74  return mUserSize;
75 }
76 
78 {
79  if ( mUserSize == size )
80  return;
81 
82  mUserSize = size;
83  emit sizeChanged();
84 }
85 
87 {
88  const QFont symbolLabelFont = settings.style( QgsLegendStyle::SymbolLabel ).font();
89 
90  const double textHeight = settings.fontHeightCharacterMM( symbolLabelFont, QChar( '0' ) );
91  // itemHeight here is not really item height, it is only for symbol
92  // vertical alignment purpose, i.e. OK take single line height
93  // if there are more lines, those run under the symbol
94  const double itemHeight = std::max( static_cast< double >( ctx && ctx->patchSize.height() > 0 ? ctx->patchSize.height() : settings.symbolSize().height() ), textHeight );
95 
96  ItemMetrics im;
97  im.symbolSize = drawSymbol( settings, ctx, itemHeight );
98  im.labelSize = drawSymbolText( settings, ctx, im.symbolSize );
99  return im;
100 }
101 
103 {
104  QJsonObject json = exportSymbolToJson( settings, context );
105  const QString text = data( Qt::DisplayRole ).toString();
106  json[ QStringLiteral( "title" ) ] = text;
107  return json;
108 }
109 
110 QSizeF QgsLayerTreeModelLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
111 {
112  const QIcon symbolIcon = data( Qt::DecorationRole ).value<QIcon>();
113  if ( symbolIcon.isNull() )
114  return QSizeF();
115 
116  QSizeF size = settings.symbolSize();
117  if ( ctx )
118  {
119  if ( ctx->patchSize.width() > 0 )
120  size.setWidth( ctx->patchSize.width( ) );
121  if ( ctx->patchSize.height() > 0 )
122  size.setHeight( ctx->patchSize.height( ) );
123  }
124 
125  if ( ctx && ctx->painter )
126  {
127  switch ( settings.symbolAlignment() )
128  {
129  case Qt::AlignLeft:
130  default:
131  symbolIcon.paint( ctx->painter,
132  static_cast< int >( ctx->columnLeft ),
133  static_cast< int >( ctx->top + ( itemHeight - size.height() ) / 2 ),
134  static_cast< int >( size.width() ),
135  static_cast< int >( size.height() ) );
136  break;
137 
138  case Qt::AlignRight:
139  symbolIcon.paint( ctx->painter,
140  static_cast< int >( ctx->columnRight - size.width() ),
141  static_cast< int >( ctx->top + ( itemHeight - size.height() ) / 2 ),
142  static_cast< int >( size.width() ),
143  static_cast< int >( size.height() ) );
144  break;
145  }
146  }
147  return size;
148 }
149 
151 {
152  const QIcon icon = data( Qt::DecorationRole ).value<QIcon>();
153  if ( icon.isNull() )
154  return QJsonObject();
155 
156  const QImage image( icon.pixmap( settings.symbolSize().width(), settings.symbolSize().height() ).toImage() );
157  QByteArray byteArray;
158  QBuffer buffer( &byteArray );
159  image.save( &buffer, "PNG" );
160  const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
161 
162  QJsonObject json;
163  json[ QStringLiteral( "icon" ) ] = base64;
164  return json;
165 }
166 
167 QSizeF QgsLayerTreeModelLegendNode::drawSymbolText( const QgsLegendSettings &settings, ItemContext *ctx, QSizeF symbolSize ) const
168 {
169  QSizeF labelSize( 0, 0 );
170 
171  const QFont symbolLabelFont = settings.style( QgsLegendStyle::SymbolLabel ).font();
172  const double textHeight = settings.fontHeightCharacterMM( symbolLabelFont, QChar( '0' ) );
173  const double textDescent = settings.fontDescentMillimeters( symbolLabelFont );
174 
175  const QgsExpressionContext tempContext;
176 
177  const QStringList lines = settings.evaluateItemText( data( Qt::DisplayRole ).toString(), ctx && ctx->context ? ctx->context->expressionContext() : tempContext );
178 
179  labelSize.rheight() = lines.count() * textHeight + ( lines.count() - 1 ) * ( settings.lineSpacing() + textDescent );
180 
181  double labelXMin = 0.0;
182  double labelXMax = 0.0;
183  double labelY = 0.0;
184  if ( ctx && ctx->painter )
185  {
186  ctx->painter->setPen( settings.fontColor() );
187  switch ( settings.symbolAlignment() )
188  {
189  case Qt::AlignLeft:
190  default:
191  labelXMin = ctx->columnLeft + std::max( static_cast< double >( symbolSize.width() ), ctx->maxSiblingSymbolWidth )
194  labelXMax = ctx->columnRight;
195  break;
196 
197  case Qt::AlignRight:
198  labelXMin = ctx->columnLeft;
199  // NOTE -- while the below calculations use the flipped margins from the style, that's only done because
200  // those are the only margins we expose and use for now! (and we expose them as generic margins, not side-specific
201  // ones) TODO when/if we expose other margin settings, these should be reversed...
202  labelXMax = ctx->columnRight - std::max( static_cast< double >( symbolSize.width() ), ctx->maxSiblingSymbolWidth )
205  break;
206  }
207 
208  labelY = ctx->top;
209 
210  // Vertical alignment of label with symbol
211  if ( labelSize.height() < symbolSize.height() )
212  labelY += symbolSize.height() / 2 - labelSize.height() / 2; // label centered with symbol
213 
214  labelY += textHeight;
215  }
216 
217  for ( QStringList::ConstIterator itemPart = lines.constBegin(); itemPart != lines.constEnd(); ++itemPart )
218  {
219  const double lineWidth = settings.textWidthMillimeters( symbolLabelFont, *itemPart );
220  labelSize.rwidth() = std::max( lineWidth, double( labelSize.width() ) );
221 
222  if ( ctx && ctx->painter )
223  {
224  switch ( settings.style( QgsLegendStyle::SymbolLabel ).alignment() )
225  {
226  case Qt::AlignLeft:
227  default:
228  settings.drawText( ctx->painter, labelXMin, labelY, *itemPart, symbolLabelFont );
229  break;
230 
231  case Qt::AlignRight:
232  settings.drawText( ctx->painter, labelXMax - lineWidth, labelY, *itemPart, symbolLabelFont );
233  break;
234 
235  case Qt::AlignHCenter:
236  settings.drawText( ctx->painter, labelXMin + ( labelXMax - labelXMin - lineWidth ) / 2.0, labelY, *itemPart, symbolLabelFont );
237  break;
238  }
239 
240  if ( itemPart != ( lines.end() - 1 ) )
241  labelY += textDescent + settings.lineSpacing() + textHeight;
242  }
243  }
244 
245  return labelSize;
246 }
247 
249 {
250  checkAll( true );
251 }
252 
254 {
255  checkAll( false );
256 }
257 
259 {
260  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ) )
261  {
262  if ( !vlayer->renderer() )
263  return;
264 
265  const QgsLegendSymbolList symbolList = vlayer->renderer()->legendSymbolItems();
266  for ( const auto &item : symbolList )
267  {
268  vlayer->renderer()->checkLegendSymbolItem( item.ruleKey(), ! vlayer->renderer()->legendSymbolItemChecked( item.ruleKey() ) );
269  }
270 
271  emit dataChanged();
272  vlayer->emitStyleChanged();
273  vlayer->triggerRepaint();
274  }
275  else if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
276  {
277  if ( !pclayer->renderer() )
278  return;
279 
280  const QStringList ruleKeys = pclayer->renderer()->legendRuleKeys();
281  for ( const QString &rule : ruleKeys )
282  {
283  pclayer->renderer()->checkLegendItem( rule, !pclayer->renderer()->legendItemChecked( rule ) );
284  }
285 
286  emit dataChanged();
287  pclayer->emitStyleChanged();
288  pclayer->triggerRepaint();
289  }
290 }
291 
292 // -------------------------------------------------------------------------
293 
296 
298  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
299  , mItem( item )
300  , mSymbolUsesMapUnits( false )
301 {
302  const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 );
303  mIconSize = QSize( iconSize, iconSize );
304 
305  if ( MINIMUM_SIZE < 0 )
306  {
307  // it's FAR too expensive to construct a QgsSettings object for every symbol node, especially for complex
308  // projects. So only read the valid size ranges once, and store them for subsequent use
309  const QgsSettings settings;
310  MINIMUM_SIZE = settings.value( "/qgis/legendsymbolMinimumSize", 0.5 ).toDouble();
311  MAXIMUM_SIZE = settings.value( "/qgis/legendsymbolMaximumSize", 20.0 ).toDouble();
312  }
313 
314  updateLabel();
315  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() ) )
316  connect( vl, &QgsVectorLayer::symbolFeatureCountMapChanged, this, &QgsSymbolLegendNode::updateLabel );
317 
318  connect( nodeLayer, &QObject::destroyed, this, [ = ]() { mLayerNode = nullptr; } );
319 
320  if ( const QgsSymbol *symbol = mItem.symbol() )
321  {
322  mSymbolUsesMapUnits = symbol->usesMapUnits();
323  }
324 }
325 
326 Qt::ItemFlags QgsSymbolLegendNode::flags() const
327 {
328  if ( mItem.isCheckable() )
329  return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
330  else
331  return Qt::ItemIsEnabled;
332 }
333 
334 
336 {
337  const std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
338  return minimumIconSize( context.get() );
339 }
340 
342 {
343  const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 );
344  const int largeIconSize = QgsLayerTreeModel::scaleIconSize( 512 );
345  QSize minSz( iconSize, iconSize );
346  if ( mItem.symbol() && mItem.symbol()->type() == Qgis::SymbolType::Marker )
347  {
348  // unusued width, height variables
349  double width = 0.0;
350  double height = 0.0;
351  const std::unique_ptr<QgsSymbol> symbol( QgsSymbolLayerUtils::restrictedSizeSymbol( mItem.symbol(), MINIMUM_SIZE, MAXIMUM_SIZE, context, width, height ) );
353  QgsSymbolLayerUtils::symbolPreviewPixmap( symbol ? symbol.get() : mItem.symbol(), QSize( largeIconSize, largeIconSize ), 0,
354  context ).toImage(),
355  minSz,
356  true ).size();
357  }
358  else if ( mItem.symbol() && mItem.symbol()->type() == Qgis::SymbolType::Line )
359  {
360  double width = 0.0;
361  double height = 0.0;
362  const std::unique_ptr<QgsSymbol> symbol( QgsSymbolLayerUtils::restrictedSizeSymbol( mItem.symbol(), MINIMUM_SIZE, MAXIMUM_SIZE, context, width, height ) );
364  QgsSymbolLayerUtils::symbolPreviewPixmap( symbol ? symbol.get() : mItem.symbol(), QSize( minSz.width(), largeIconSize ), 0,
365  context ).toImage(),
366  minSz,
367  true ).size();
368  }
369 
370  if ( !mTextOnSymbolLabel.isEmpty() && context )
371  {
372  const double w = QgsTextRenderer::textWidth( *context, mTextOnSymbolTextFormat, QStringList() << mTextOnSymbolLabel );
373  const double h = QgsTextRenderer::textHeight( *context, mTextOnSymbolTextFormat, QStringList() << mTextOnSymbolLabel, QgsTextRenderer::Point );
374  int wInt = ceil( w ), hInt = ceil( h );
375  if ( wInt > minSz.width() ) minSz.setWidth( wInt );
376  if ( hInt > minSz.height() ) minSz.setHeight( hInt );
377  }
378 
379  return minSz;
380 }
381 
383 {
384  return mItem.symbol();
385 }
386 
388 {
389  QString label;
390  if ( mEmbeddedInParent )
391  {
392  const QVariant legendlabel = mLayerNode->customProperty( QStringLiteral( "legend/title-label" ) );
393  const QString layerName = legendlabel.isNull() ? mLayerNode->name() : legendlabel.toString();
394  label = mUserLabel.isEmpty() ? layerName : mUserLabel;
395  }
396  else
397  label = mUserLabel.isEmpty() ? mItem.label() : mUserLabel;
398  return label;
399 }
400 
402 {
403  if ( mEmbeddedInParent )
404  {
405  return mLayerNode->patchShape();
406  }
407  else
408  {
409  return mPatchShape;
410  }
411 }
412 
414 {
415  mPatchShape = shape;
416 }
417 
419 {
420  return mCustomSymbol.get();
421 }
422 
424 {
425  mCustomSymbol.reset( symbol );
426 }
427 
429 {
430  if ( !symbol )
431  return;
432 
433  std::unique_ptr< QgsSymbol > s( symbol ); // this method takes ownership of symbol
434  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
435  if ( !vlayer || !vlayer->renderer() )
436  return;
437 
438  mItem.setSymbol( s.get() ); // doesn't transfer ownership
439  vlayer->renderer()->setLegendSymbolItem( mItem.ruleKey(), s.release() ); // DOES transfer ownership!
440 
441  mPixmap = QPixmap();
442 
443  emit dataChanged();
444  vlayer->triggerRepaint();
445 }
446 
448 {
449  double scale = 0.0;
450  double mupp = 0.0;
451  int dpi = 0;
452  if ( auto *lModel = model() )
453  lModel->legendMapViewData( &mupp, &dpi, &scale );
454 
455  if ( qgsDoubleNear( mupp, 0.0 ) || dpi == 0 || qgsDoubleNear( scale, 0.0 ) )
456  return nullptr;
457 
458  // setup temporary render context
459  std::unique_ptr<QgsRenderContext> context = std::make_unique<QgsRenderContext>( );
460  context->setScaleFactor( dpi / 25.4 );
461  context->setRendererScale( scale );
462  context->setMapToPixel( QgsMapToPixel( mupp ) );
463  context->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
464  context->setFlag( Qgis::RenderContextFlag::RenderSymbolPreview, true );
465  return context.release();
466 }
467 
468 void QgsLayerTreeModelLegendNode::checkAll( bool state )
469 {
470  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ) )
471  {
472  if ( !vlayer->renderer() )
473  return;
474 
475  const QgsLegendSymbolList symbolList = vlayer->renderer()->legendSymbolItems();
476  for ( const auto &item : symbolList )
477  {
478  vlayer->renderer()->checkLegendSymbolItem( item.ruleKey(), state );
479  }
480 
481  emit dataChanged();
482  vlayer->emitStyleChanged();
483  vlayer->triggerRepaint();
484  }
485  else if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
486  {
487  if ( !pclayer->renderer() )
488  return;
489 
490  const QStringList ruleKeys = pclayer->renderer()->legendRuleKeys();
491  for ( const QString &rule : ruleKeys )
492  {
493  pclayer->renderer()->checkLegendItem( rule, state );
494  }
495 
496  emit dataChanged();
497  pclayer->emitStyleChanged();
498  pclayer->triggerRepaint();
499  }
500 }
501 
502 QVariant QgsSymbolLegendNode::data( int role ) const
503 {
504  if ( role == Qt::DisplayRole )
505  {
506  return mLabel;
507  }
508  else if ( role == Qt::EditRole )
509  {
510  return mUserLabel.isEmpty() ? mItem.label() : mUserLabel;
511  }
512  else if ( role == Qt::DecorationRole )
513  {
514  if ( mPixmap.isNull() || mPixmap.size() != mIconSize )
515  {
516  QPixmap pix;
517  if ( mItem.symbol() )
518  {
519  std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
520 
521  // unusued width, height variables
522  double width = 0.0;
523  double height = 0.0;
524  const std::unique_ptr<QgsSymbol> symbol( QgsSymbolLayerUtils::restrictedSizeSymbol( mItem.symbol(), MINIMUM_SIZE, MAXIMUM_SIZE, context.get(), width, height ) );
525  pix = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol ? symbol.get() : mItem.symbol(), mIconSize, 0, context.get() );
526 
527  if ( !mTextOnSymbolLabel.isEmpty() && context )
528  {
529  QPainter painter( &pix );
530  painter.setRenderHint( QPainter::Antialiasing );
531  context->setPainter( &painter );
532  bool isNullSize = false;
533  const QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( *context, 1.0, &isNullSize ) );
534  if ( !isNullSize )
535  {
536  const qreal yBaselineVCenter = ( mIconSize.height() + fm.ascent() - fm.descent() ) / 2;
537  QgsTextRenderer::drawText( QPointF( mIconSize.width() / 2, yBaselineVCenter ), 0, QgsTextRenderer::AlignCenter,
538  QStringList() << mTextOnSymbolLabel, *context, mTextOnSymbolTextFormat );
539  }
540  }
541  }
542  else
543  {
544  pix = QPixmap( mIconSize );
545  pix.fill( Qt::transparent );
546  }
547 
548  mPixmap = pix;
549  }
550  return mPixmap;
551  }
552  else if ( role == Qt::CheckStateRole )
553  {
554  if ( !mItem.isCheckable() )
555  return QVariant();
556 
557  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ) )
558  {
559  if ( !vlayer->renderer() )
560  return QVariant();
561 
562  return vlayer->renderer()->legendSymbolItemChecked( mItem.ruleKey() ) ? Qt::Checked : Qt::Unchecked;
563  }
564  }
565  else if ( role == RuleKeyRole )
566  {
567  return mItem.ruleKey();
568  }
569  else if ( role == ParentRuleKeyRole )
570  {
571  return mItem.parentRuleKey();
572  }
574  {
576  }
577 
578  return QVariant();
579 }
580 
581 bool QgsSymbolLegendNode::setData( const QVariant &value, int role )
582 {
583  if ( role != Qt::CheckStateRole )
584  return false;
585 
586  if ( !mItem.isCheckable() )
587  return false;
588 
589  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
590  if ( !vlayer || !vlayer->renderer() )
591  return false;
592 
593  vlayer->renderer()->checkLegendSymbolItem( mItem.ruleKey(), value == Qt::Checked );
594 
595  emit dataChanged();
596  vlayer->emitStyleChanged();
597 
598  vlayer->triggerRepaint();
599 
600  return true;
601 }
602 
603 
604 
605 QSizeF QgsSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
606 {
607  QgsSymbol *s = mCustomSymbol ? mCustomSymbol.get() : mItem.symbol();
608  if ( !s )
609  {
610  return QSizeF();
611  }
612 
613  // setup temporary render context
614  QgsRenderContext *context = nullptr;
615  std::unique_ptr< QgsRenderContext > tempRenderContext;
617  if ( ctx && ctx->context )
618  context = ctx->context;
619  else
620  {
621  tempRenderContext = std::make_unique< QgsRenderContext >();
622  // QGIS 4.0 - make ItemContext compulsory, so we don't have to construct temporary render contexts here
624  tempRenderContext->setScaleFactor( settings.dpi() / 25.4 );
625  tempRenderContext->setRendererScale( settings.mapScale() );
626  tempRenderContext->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
627  tempRenderContext->setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * tempRenderContext->scaleFactor() ) ) );
629  tempRenderContext->setForceVectorOutput( true );
630  tempRenderContext->setPainter( ctx ? ctx->painter : nullptr );
631 
632  // setup a minimal expression context
633  QgsExpressionContext expContext;
635  tempRenderContext->setExpressionContext( expContext );
636  context = tempRenderContext.get();
637  }
638 
639  //Consider symbol size for point markers
640  const double desiredHeight = ctx && ctx->patchSize.height() > 0 ? ctx->patchSize.height() : settings.symbolSize().height();
641  const double desiredWidth = ctx && ctx->patchSize.width() > 0 ? ctx->patchSize.width() : settings.symbolSize().width();
642  double height = desiredHeight;
643  double width = desiredWidth;
644 
645  //Center small marker symbols
646  double widthOffset = 0;
647  double heightOffset = 0;
648 
649  const double maxSymbolSize = settings.maximumSymbolSize();
650  const double minSymbolSize = settings.minimumSymbolSize();
651 
652  if ( QgsMarkerSymbol *markerSymbol = dynamic_cast<QgsMarkerSymbol *>( s ) )
653  {
654  const double size = markerSymbol->size( *context ) / context->scaleFactor();
655  height = size;
656  width = size;
657  }
658 
659  const std::unique_ptr<QgsSymbol> minMaxSizeSymbol( QgsSymbolLayerUtils::restrictedSizeSymbol( s, minSymbolSize, maxSymbolSize, context, width, height ) );
660  if ( minMaxSizeSymbol )
661  {
662  s = minMaxSizeSymbol.get();
663  }
664 
665  if ( s->type() == Qgis::SymbolType::Marker )
666  {
667  if ( width < desiredWidth )
668  {
669  widthOffset = ( desiredWidth - width ) / 2.0;
670  }
671  if ( height < desiredHeight )
672  {
673  heightOffset = ( desiredHeight - height ) / 2.0;
674  }
675  }
676  if ( ctx && ctx->painter )
677  {
678  const double currentYCoord = ctx->top + ( itemHeight - desiredHeight ) / 2;
679  QPainter *p = ctx->painter;
680 
681  //setup painter scaling to dots so that raster symbology is drawn to scale
682  const double dotsPerMM = context->scaleFactor();
683 
684  int opacity = 255;
685  if ( QgsMapLayer *layer = layerNode()->layer() )
686  opacity = static_cast<int >( std::round( 255 * layer->opacity() ) );
687 
688  const QgsScopedQPainterState painterState( p );
689  context->setPainterFlagsUsingContext( p );
690 
691  switch ( settings.symbolAlignment() )
692  {
693  case Qt::AlignLeft:
694  default:
695  p->translate( ctx->columnLeft + widthOffset, currentYCoord + heightOffset );
696  break;
697  case Qt::AlignRight:
698  p->translate( ctx->columnRight - widthOffset - width, currentYCoord + heightOffset );
699  break;
700  }
701 
702  p->scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
704  // QGIS 4.0 -- ctx->context will be mandatory
705  const bool useAdvancedEffects = ctx->context ? ctx->context->flags() & Qgis::RenderContextFlag::UseAdvancedEffects : settings.useAdvancedEffects();
707  if ( opacity != 255 && useAdvancedEffects )
708  {
709  const int maxBleed = static_cast< int >( std::ceil( QgsSymbolLayerUtils::estimateMaxSymbolBleed( s, *context ) ) );
710 
711  //semi transparent layer, so need to draw symbol to an image (to flatten it first)
712  //create image which is same size as legend rect, in case symbol bleeds outside its allotted space
713  const QSize symbolSize( static_cast< int >( std::round( width * dotsPerMM ) ), static_cast<int >( std::round( height * dotsPerMM ) ) );
714  const QSize tempImageSize( symbolSize.width() + maxBleed * 2, symbolSize.height() + maxBleed * 2 );
715  QImage tempImage = QImage( tempImageSize, QImage::Format_ARGB32 );
716  tempImage.fill( Qt::transparent );
717  QPainter imagePainter( &tempImage );
718  context->setPainterFlagsUsingContext( &imagePainter );
719 
720  context->setPainter( &imagePainter );
721  imagePainter.translate( maxBleed, maxBleed );
722  s->drawPreviewIcon( &imagePainter, symbolSize, context, false, nullptr, &patchShape );
723  imagePainter.translate( -maxBleed, -maxBleed );
724  context->setPainter( ctx->painter );
725  //reduce opacity of image
726  imagePainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
727  imagePainter.fillRect( tempImage.rect(), QColor( 0, 0, 0, opacity ) );
728  imagePainter.end();
729  //draw rendered symbol image
730  p->drawImage( -maxBleed, -maxBleed, tempImage );
731  }
732  else
733  {
734  s->drawPreviewIcon( p, QSize( static_cast< int >( std::round( width * dotsPerMM ) ), static_cast< int >( std::round( height * dotsPerMM ) ) ), context, false, nullptr, &patchShape );
735  }
736 
737  if ( !mTextOnSymbolLabel.isEmpty() )
738  {
739  bool isNullSize = false;
740  const QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( *context, 1.0, &isNullSize ) );
741  if ( !isNullSize )
742  {
743  const qreal yBaselineVCenter = ( height * dotsPerMM + fm.ascent() - fm.descent() ) / 2;
744  QgsTextRenderer::drawText( QPointF( width * dotsPerMM / 2, yBaselineVCenter ), 0, QgsTextRenderer::AlignCenter,
745  QStringList() << mTextOnSymbolLabel, *context, mTextOnSymbolTextFormat );
746  }
747  }
748  }
749 
750  return QSizeF( std::max( width + 2 * widthOffset, static_cast< double >( desiredWidth ) ),
751  std::max( height + 2 * heightOffset, static_cast< double >( desiredHeight ) ) );
752 }
753 
754 QJsonObject QgsSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const
755 {
756  const QgsSymbol *s = mCustomSymbol ? mCustomSymbol.get() : mItem.symbol();
757  if ( !s )
758  {
759  return QJsonObject();
760  }
761 
762 
763  QgsRenderContext ctx;
764  // QGIS 4.0 - use render context directly here, and note in the dox that the context must be correctly setup
766  ctx.setScaleFactor( settings.dpi() / 25.4 );
767  ctx.setRendererScale( settings.mapScale() );
768  ctx.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * ctx.scaleFactor() ) ) );
769  ctx.setForceVectorOutput( true );
772 
774 
775  // ensure that a minimal expression context is available
776  QgsExpressionContext expContext = context.expressionContext();
778  ctx.setExpressionContext( expContext );
779 
780  const QPixmap pix = QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), minimumIconSize(), 0, &ctx );
781  QImage img( pix.toImage().convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
782 
783  int opacity = 255;
784  if ( QgsMapLayer *layer = layerNode()->layer() )
785  opacity = ( 255 * layer->opacity() );
786 
787  if ( opacity != 255 )
788  {
789  QPainter painter;
790  painter.begin( &img );
791  painter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
792  painter.fillRect( pix.rect(), QColor( 0, 0, 0, opacity ) );
793  painter.end();
794  }
795 
796  QByteArray byteArray;
797  QBuffer buffer( &byteArray );
798  img.save( &buffer, "PNG" );
799  const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
800 
801  QJsonObject json;
802  json[ QStringLiteral( "icon" ) ] = base64;
803  if ( mItem.scaleMaxDenom() > 0 )
804  {
805  json[ QStringLiteral( "scaleMaxDenom" ) ] = mItem.scaleMaxDenom();
806  }
807  if ( mItem.scaleMinDenom() > 0 )
808  {
809  json[ QStringLiteral( "scaleMinDenom" ) ] = mItem.scaleMinDenom();
810  }
811  mItem.scaleMaxDenom();
812  return json;
813 }
814 
816 {
818  updateLabel();
819 }
820 
821 
823 {
824  if ( mSymbolUsesMapUnits )
825  {
826  mPixmap = QPixmap();
827  emit dataChanged();
828  }
829 }
830 
831 
832 void QgsSymbolLegendNode::updateLabel()
833 {
834  if ( !mLayerNode )
835  return;
836 
837  const bool showFeatureCount = mLayerNode->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toBool();
838  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
839  mLabel = symbolLabel();
840 
841  if ( showFeatureCount && vl )
842  {
843  const qlonglong count = mEmbeddedInParent ? vl->featureCount() : vl->featureCount( mItem.ruleKey() ) ;
844  mLabel += QStringLiteral( " [%1]" ).arg( count != -1 ? QLocale().toString( count ) : tr( "N/A" ) );
845  }
846 
847  emit dataChanged();
848 }
849 
850 QString QgsSymbolLegendNode::evaluateLabel( const QgsExpressionContext &context, const QString &label )
851 {
852  if ( !mLayerNode )
853  return QString();
854 
855  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
856 
857  if ( vl )
858  {
859  QgsExpressionContext contextCopy = QgsExpressionContext( context );
860  QgsExpressionContextScope *symbolScope = createSymbolScope();
861  contextCopy.appendScope( symbolScope );
862  contextCopy.appendScope( vl->createExpressionContextScope() );
863 
864  if ( label.isEmpty() )
865  {
866  if ( ! mLayerNode->labelExpression().isEmpty() )
867  mLabel = QgsExpression::replaceExpressionText( "[%" + mLayerNode->labelExpression() + "%]", &contextCopy );
868  else if ( mLabel.contains( "[%" ) )
869  {
870  const QString symLabel = symbolLabel();
871  mLabel = QgsExpression::replaceExpressionText( symLabel, &contextCopy );
872  }
873  return mLabel;
874  }
875  else
876  {
877  QString eLabel;
878  if ( ! mLayerNode->labelExpression().isEmpty() )
879  eLabel = QgsExpression::replaceExpressionText( label + "[%" + mLayerNode->labelExpression() + "%]", &contextCopy );
880  else if ( label.contains( "[%" ) )
881  eLabel = QgsExpression::replaceExpressionText( label, &contextCopy );
882  return eLabel;
883  }
884  }
885  return mLabel;
886 }
887 
888 QgsExpressionContextScope *QgsSymbolLegendNode::createSymbolScope() const
889 {
890  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
891 
892  QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Symbol scope" ) );
893  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_label" ), symbolLabel().remove( "[%" ).remove( "%]" ), true ) );
894  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_id" ), mItem.ruleKey(), true ) );
895  if ( vl )
896  {
897  scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_count" ), QVariant::fromValue( vl->featureCount( mItem.ruleKey() ) ), true ) );
898  }
899  return scope;
900 }
901 
902 // -------------------------------------------------------------------------
903 
904 
905 QgsSimpleLegendNode::QgsSimpleLegendNode( QgsLayerTreeLayer *nodeLayer, const QString &label, const QIcon &icon, QObject *parent, const QString &key )
906  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
907  , mLabel( label )
908  , mIcon( icon )
909  , mKey( key )
910 {
911 }
912 
913 QVariant QgsSimpleLegendNode::data( int role ) const
914 {
915  if ( role == Qt::DisplayRole || role == Qt::EditRole )
916  return mUserLabel.isEmpty() ? mLabel : mUserLabel;
917  else if ( role == Qt::DecorationRole )
918  return mIcon;
919  else if ( role == RuleKeyRole && !mKey.isEmpty() )
920  return mKey;
923  else
924  return QVariant();
925 }
926 
927 
928 // -------------------------------------------------------------------------
929 
930 QgsImageLegendNode::QgsImageLegendNode( QgsLayerTreeLayer *nodeLayer, const QImage &img, QObject *parent )
931  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
932  , mImage( img )
933 {
934 }
935 
936 QVariant QgsImageLegendNode::data( int role ) const
937 {
938  if ( role == Qt::DecorationRole )
939  {
940  return QPixmap::fromImage( mImage );
941  }
942  else if ( role == Qt::SizeHintRole )
943  {
944  return mImage.size();
945  }
947  {
949  }
950  return QVariant();
951 }
952 
953 QSizeF QgsImageLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
954 {
955  Q_UNUSED( itemHeight )
956 
957  if ( ctx && ctx->painter && ctx->context )
958  {
959  const QgsScopedRenderContextScaleToPixels scopedScaleToPixels( *( ctx->context ) );
960  const double scaleFactor = ctx->context->scaleFactor();
961  const double imgWidth = settings.wmsLegendSize().width() * scaleFactor;
962  const double imgHeight = settings.wmsLegendSize().height() * scaleFactor;
963 
964  const QImage scaledImg = mImage.scaled( QSizeF( imgWidth, imgHeight ).toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
965  switch ( settings.symbolAlignment() )
966  {
967  case Qt::AlignLeft:
968  default:
969  ctx->painter->drawImage( QPointF( ctx->columnLeft * scaleFactor, ctx->top * scaleFactor ), scaledImg );
970  break;
971 
972  case Qt::AlignRight:
973  ctx->painter->drawImage( QPointF( ctx->columnRight * scaleFactor - imgWidth, ctx->top * scaleFactor ), scaledImg );
974  break;
975  }
976  }
977  return settings.wmsLegendSize();
978 }
979 
981 {
982  QByteArray byteArray;
983  QBuffer buffer( &byteArray );
984  mImage.save( &buffer, "PNG" );
985  const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
986 
987  QJsonObject json;
988  json[ QStringLiteral( "icon" ) ] = base64;
989  return json;
990 }
991 
992 // -------------------------------------------------------------------------
993 
994 QgsRasterSymbolLegendNode::QgsRasterSymbolLegendNode( QgsLayerTreeLayer *nodeLayer, const QColor &color, const QString &label, QObject *parent, bool isCheckable, const QString &ruleKey )
995  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
996  , mColor( color )
997  , mLabel( label )
998  , mCheckable( isCheckable )
999  , mRuleKey( ruleKey )
1000 {
1001 }
1002 
1004 {
1005  if ( mCheckable )
1006  return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
1007  else
1008  return Qt::ItemIsEnabled;
1009 }
1010 
1011 QVariant QgsRasterSymbolLegendNode::data( int role ) const
1012 {
1013  switch ( role )
1014  {
1015  case Qt::DecorationRole:
1016  {
1017  const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 ); // TODO: configurable?
1018  QPixmap pix( iconSize, iconSize );
1019  pix.fill( mColor );
1020  return QIcon( pix );
1021  }
1022 
1023  case Qt::DisplayRole:
1024  case Qt::EditRole:
1025  return mUserLabel.isEmpty() ? mLabel : mUserLabel;
1026 
1029 
1031  return mRuleKey;
1032 
1033  case Qt::CheckStateRole:
1034  {
1035  if ( !mCheckable )
1036  return QVariant();
1037 
1038  if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
1039  {
1040  if ( !pclayer->renderer() )
1041  return QVariant();
1042 
1043  return pclayer->renderer()->legendItemChecked( mRuleKey ) ? Qt::Checked : Qt::Unchecked;
1044  }
1045 
1046  return QVariant();
1047  }
1048 
1049  default:
1050  return QVariant();
1051  }
1052 }
1053 
1054 bool QgsRasterSymbolLegendNode::setData( const QVariant &value, int role )
1055 {
1056  if ( role != Qt::CheckStateRole )
1057  return false;
1058 
1059  if ( !mCheckable )
1060  return false;
1061 
1062  if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
1063  {
1064  if ( !pclayer->renderer() )
1065  return false;
1066 
1067  pclayer->renderer()->checkLegendItem( mRuleKey, value == Qt::Checked );
1068 
1069  emit dataChanged();
1070  pclayer->emitStyleChanged();
1071 
1072  pclayer->triggerRepaint();
1073  return true;
1074  }
1075  else
1076  {
1077  return false;
1078  }
1079 }
1080 
1081 
1082 QSizeF QgsRasterSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1083 {
1084  QSizeF size = settings.symbolSize();
1085  double offsetX = 0;
1086  if ( ctx )
1087  {
1088  if ( ctx->patchSize.width() > 0 )
1089  {
1090  if ( ctx->patchSize.width() < size.width() )
1091  offsetX = ( size.width() - ctx->patchSize.width() ) / 2.0;
1092  size.setWidth( ctx->patchSize.width() );
1093  }
1094  if ( ctx->patchSize.height() > 0 )
1095  {
1096  size.setHeight( ctx->patchSize.height() );
1097  }
1098  }
1099 
1100  if ( ctx && ctx->painter )
1101  {
1102  QColor itemColor = mColor;
1103  if ( QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layerNode()->layer() ) )
1104  {
1105  if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
1106  itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
1107  }
1108  ctx->painter->setBrush( itemColor );
1109 
1110  if ( settings.drawRasterStroke() )
1111  {
1112  QPen pen;
1113  pen.setColor( settings.rasterStrokeColor() );
1114  pen.setWidthF( settings.rasterStrokeWidth() );
1115  pen.setJoinStyle( Qt::MiterJoin );
1116  ctx->painter->setPen( pen );
1117  }
1118  else
1119  {
1120  ctx->painter->setPen( Qt::NoPen );
1121  }
1122 
1123  switch ( settings.symbolAlignment() )
1124  {
1125  case Qt::AlignLeft:
1126  default:
1127  ctx->painter->drawRect( QRectF( ctx->columnLeft + offsetX, ctx->top + ( itemHeight - size.height() ) / 2,
1128  size.width(), size.height() ) );
1129  break;
1130 
1131  case Qt::AlignRight:
1132  ctx->painter->drawRect( QRectF( ctx->columnRight - size.width() - offsetX, ctx->top + ( itemHeight - size.height() ) / 2,
1133  size.width(), size.height() ) );
1134  break;
1135  }
1136  }
1137  return size;
1138 }
1139 
1141 {
1142  QImage img = QImage( settings.symbolSize().toSize(), QImage::Format_ARGB32 );
1143  img.fill( Qt::transparent );
1144 
1145  QPainter painter( &img );
1146  painter.setRenderHint( QPainter::Antialiasing );
1147 
1148  QColor itemColor = mColor;
1149  if ( QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layerNode()->layer() ) )
1150  {
1151  if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
1152  itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
1153  }
1154  painter.setBrush( itemColor );
1155 
1156  if ( settings.drawRasterStroke() )
1157  {
1158  QPen pen;
1159  pen.setColor( settings.rasterStrokeColor() );
1160  pen.setWidthF( settings.rasterStrokeWidth() );
1161  pen.setJoinStyle( Qt::MiterJoin );
1162  painter.setPen( pen );
1163  }
1164  else
1165  {
1166  painter.setPen( Qt::NoPen );
1167  }
1168 
1169  painter.drawRect( QRectF( 0, 0, settings.symbolSize().width(), settings.symbolSize().height() ) );
1170 
1171  QByteArray byteArray;
1172  QBuffer buffer( &byteArray );
1173  img.save( &buffer, "PNG" );
1174  const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1175 
1176  QJsonObject json;
1177  json[ QStringLiteral( "icon" ) ] = base64;
1178  return json;
1179 }
1180 
1181 // -------------------------------------------------------------------------
1182 
1184  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1185  , mValid( false )
1186 {
1187 }
1188 
1190 
1191 QImage QgsWmsLegendNode::getLegendGraphic() const
1192 {
1193  if ( ! mValid && ! mFetcher )
1194  {
1195  // or maybe in presence of a downloader we should just delete it
1196  // and start a new one ?
1197 
1198  QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( mLayerNode->layer() );
1199 
1200  if ( layer && layer->isValid() )
1201  {
1202  const QgsLayerTreeModel *mod = model();
1203  if ( ! mod )
1204  return mImage;
1205  const QgsMapSettings *ms = mod->legendFilterMapSettings();
1206 
1207  QgsRasterDataProvider *prov = layer->dataProvider();
1208  if ( ! prov )
1209  return mImage;
1210 
1211  Q_ASSERT( ! mFetcher );
1212  mFetcher.reset( prov->getLegendGraphicFetcher( ms ) );
1213  if ( mFetcher )
1214  {
1215  connect( mFetcher.get(), &QgsImageFetcher::finish, this, &QgsWmsLegendNode::getLegendGraphicFinished );
1216  connect( mFetcher.get(), &QgsImageFetcher::error, this, &QgsWmsLegendNode::getLegendGraphicErrored );
1217  connect( mFetcher.get(), &QgsImageFetcher::progress, this, &QgsWmsLegendNode::getLegendGraphicProgress );
1218  mFetcher->start();
1219  }
1220  }
1221  else
1222  {
1223  QgsDebugMsg( tr( "Failed to download legend graphics: layer is not valid." ) );
1224  }
1225  }
1226 
1227  return mImage;
1228 }
1229 
1230 QVariant QgsWmsLegendNode::data( int role ) const
1231 {
1232  if ( role == Qt::DecorationRole )
1233  {
1234  return QPixmap::fromImage( getLegendGraphic() );
1235  }
1236  else if ( role == Qt::SizeHintRole )
1237  {
1238  return getLegendGraphic().size();
1239  }
1240  else if ( role == QgsLayerTreeModelLegendNode::NodeTypeRole )
1241  {
1243  }
1244  return QVariant();
1245 }
1246 
1247 QSizeF QgsWmsLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1248 {
1249  Q_UNUSED( itemHeight )
1250 
1251  const QImage image = getLegendGraphic();
1252 
1253  double px2mm = 1000. / image.dotsPerMeterX();
1254  double mmWidth = image.width() * px2mm;
1255  double mmHeight = image.height() * px2mm;
1256 
1257  QSize targetSize = QSize( mmWidth, mmHeight );
1258  if ( settings.wmsLegendSize().width() < mmWidth )
1259  {
1260  double targetHeight = mmHeight * settings.wmsLegendSize().width() / mmWidth;
1261  targetSize = QSize( settings.wmsLegendSize().width(), targetHeight );
1262  }
1263  else if ( settings.wmsLegendSize().height() < mmHeight )
1264  {
1265  double targetWidth = mmWidth * settings.wmsLegendSize().height() / mmHeight;
1266  targetSize = QSize( targetWidth, settings.wmsLegendSize().height() );
1267  }
1268 
1269  if ( ctx && ctx->painter )
1270  {
1271  QImage smoothImage = image.scaled( targetSize / px2mm, Qt::KeepAspectRatio, Qt::SmoothTransformation );
1272 
1273  switch ( settings.symbolAlignment() )
1274  {
1275  case Qt::AlignLeft:
1276  default:
1277  ctx->painter->drawImage( QRectF( ctx->columnLeft,
1278  ctx->top,
1279  targetSize.width(),
1280  targetSize.height() ),
1281  smoothImage,
1282  QRectF( QPointF( 0, 0 ), smoothImage.size() ) );
1283  break;
1284 
1285  case Qt::AlignRight:
1286  ctx->painter->drawImage( QRectF( ctx->columnRight - settings.wmsLegendSize().width(),
1287  ctx->top,
1288  targetSize.width(),
1289  targetSize.height() ),
1290  smoothImage,
1291  QRectF( QPointF( 0, 0 ), smoothImage.size() ) );
1292  break;
1293  }
1294  }
1295  return targetSize;
1296 }
1297 
1299 {
1300  QByteArray byteArray;
1301  QBuffer buffer( &byteArray );
1302  mImage.save( &buffer, "PNG" );
1303  const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1304 
1305  QJsonObject json;
1306  json[ QStringLiteral( "icon" ) ] = base64;
1307  return json;
1308 }
1309 
1310 QImage QgsWmsLegendNode::renderMessage( const QString &msg ) const
1311 {
1312  const int fontHeight = 10;
1313  const int margin = fontHeight / 2;
1314  const int nlines = 1;
1315 
1316  const int w = 512, h = fontHeight * nlines + margin * ( nlines + 1 );
1317  QImage image( w, h, QImage::Format_ARGB32_Premultiplied );
1318  QPainter painter;
1319  painter.begin( &image );
1320  painter.setPen( QColor( 255, 0, 0 ) );
1321  painter.setFont( QFont( QStringLiteral( "Chicago" ), fontHeight ) );
1322  painter.fillRect( 0, 0, w, h, QColor( 255, 255, 255 ) );
1323  painter.drawText( 0, margin + fontHeight, msg );
1324  //painter.drawText(0,2*(margin+fontHeight),tr("retrying in 5 seconds…"));
1325  painter.end();
1326 
1327  return image;
1328 }
1329 
1330 void QgsWmsLegendNode::getLegendGraphicProgress( qint64 cur, qint64 tot )
1331 {
1332  const QString msg = tot > 0 ? tr( "Downloading: %1% (%2)" ).arg( static_cast< int >( std::round( 100 * cur / tot ) ) ).arg( QgsFileUtils::representFileSize( tot ) )
1333  : tr( "Downloading: %1" ).arg( QgsFileUtils::representFileSize( cur ) );
1334  mImage = renderMessage( msg );
1335  emit dataChanged();
1336 }
1337 
1338 void QgsWmsLegendNode::getLegendGraphicErrored( const QString & )
1339 {
1340  if ( ! mFetcher )
1341  return; // must be coming after finish
1342 
1343  mImage = QImage();
1344  emit dataChanged();
1345 
1346  mFetcher.reset();
1347 
1348  mValid = true; // we consider it valid anyway
1349 }
1350 
1351 void QgsWmsLegendNode::getLegendGraphicFinished( const QImage &image )
1352 {
1353  if ( ! mFetcher )
1354  return; // must be coming after error
1355 
1356  if ( ! image.isNull() )
1357  {
1358  if ( image != mImage )
1359  {
1360  mImage = image;
1361  setUserPatchSize( mImage.size() );
1362  emit dataChanged();
1363  }
1364  mValid = true; // only if not null I guess
1365  }
1366  mFetcher.reset();
1367 }
1368 
1370 {
1371  // TODO: do this only if this extent != prev extent ?
1372  mValid = false;
1373  emit dataChanged();
1374 }
1375 
1376 // -------------------------------------------------------------------------
1377 
1379  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1380  , mSettings( new QgsDataDefinedSizeLegend( settings ) )
1381 {
1382 }
1383 
1385 {
1386  delete mSettings;
1387 }
1388 
1389 QVariant QgsDataDefinedSizeLegendNode::data( int role ) const
1390 {
1391  if ( role == Qt::DecorationRole )
1392  {
1393  cacheImage();
1394  return QPixmap::fromImage( mImage );
1395  }
1396  else if ( role == Qt::SizeHintRole )
1397  {
1398  cacheImage();
1399  return mImage.size();
1400  }
1401  else if ( role == QgsLayerTreeModelLegendNode::NodeTypeRole )
1402  {
1404  }
1405  return QVariant();
1406 }
1407 
1409 {
1410  // setup temporary render context if none specified
1411  QgsRenderContext *context = nullptr;
1412  std::unique_ptr< QgsRenderContext > tempRenderContext;
1413  if ( ctx && ctx->context )
1414  context = ctx->context;
1415  else
1416  {
1417  tempRenderContext = std::make_unique< QgsRenderContext >();
1418  // QGIS 4.0 - make ItemContext compulsory, so we don't have to construct temporary render contexts here
1420  tempRenderContext->setScaleFactor( settings.dpi() / 25.4 );
1421  tempRenderContext->setRendererScale( settings.mapScale() );
1422  tempRenderContext->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
1423  tempRenderContext->setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * tempRenderContext->scaleFactor() ) ) );
1424  tempRenderContext->setForceVectorOutput( true );
1425  tempRenderContext->setPainter( ctx ? ctx->painter : nullptr );
1426  tempRenderContext->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
1428 
1429  // setup a minimal expression context
1430  QgsExpressionContext expContext;
1432  tempRenderContext->setExpressionContext( expContext );
1433  context = tempRenderContext.get();
1434  }
1435 
1436  if ( context->painter() )
1437  {
1438  context->painter()->save();
1439  context->painter()->translate( ctx->columnLeft, ctx->top );
1440 
1441  // scale to pixels
1442  context->painter()->scale( 1 / context->scaleFactor(), 1 / context->scaleFactor() );
1443  }
1444 
1445  QgsDataDefinedSizeLegend ddsLegend( *mSettings );
1446  ddsLegend.setFont( settings.style( QgsLegendStyle::SymbolLabel ).font() );
1447  ddsLegend.setTextColor( settings.fontColor() );
1448 
1449  QSizeF contentSize;
1450  double labelXOffset;
1451  ddsLegend.drawCollapsedLegend( *context, &contentSize, &labelXOffset );
1452 
1453  if ( context->painter() )
1454  context->painter()->restore();
1455 
1456  ItemMetrics im;
1457  im.symbolSize = QSizeF( ( contentSize.width() - labelXOffset ) / context->scaleFactor(), contentSize.height() / context->scaleFactor() );
1458  im.labelSize = QSizeF( labelXOffset / context->scaleFactor(), contentSize.height() / context->scaleFactor() );
1459  return im;
1460 }
1461 
1462 
1463 void QgsDataDefinedSizeLegendNode::cacheImage() const
1464 {
1465  if ( mImage.isNull() )
1466  {
1467  std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1468  if ( !context )
1469  {
1470  context.reset( new QgsRenderContext );
1471  Q_ASSERT( context ); // to make cppcheck happy
1472  context->setScaleFactor( 96 / 25.4 );
1473  }
1474  mImage = mSettings->collapsedLegendImage( *context );
1475  }
1476 }
1477 
1478 QgsVectorLabelLegendNode::QgsVectorLabelLegendNode( QgsLayerTreeLayer *nodeLayer, const QgsPalLayerSettings &labelSettings, QObject *parent ): QgsLayerTreeModelLegendNode( nodeLayer, parent ), mLabelSettings( labelSettings )
1479 {
1480 }
1481 
1483 {
1484 }
1485 
1486 QVariant QgsVectorLabelLegendNode::data( int role ) const
1487 {
1488  if ( role == Qt::DisplayRole )
1489  {
1490  return mUserLabel;
1491  }
1492  if ( role == Qt::DecorationRole )
1493  {
1494  const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 );
1495  return QgsPalLayerSettings::labelSettingsPreviewPixmap( mLabelSettings, QSize( iconSize, iconSize ), mLabelSettings.legendString() );
1496  }
1497  return QVariant();
1498 }
1499 
1500 QSizeF QgsVectorLabelLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1501 {
1502  Q_UNUSED( itemHeight );
1503  if ( !ctx )
1504  {
1505  return QSizeF( 0, 0 );
1506  }
1507 
1508  const QgsRenderContext *renderContext = ctx->context;
1509  if ( renderContext )
1510  {
1511  return drawSymbol( settings, *renderContext, ctx->columnLeft, ctx->top );
1512  }
1513 
1514  return QSizeF( 0, 0 );
1515 }
1516 
1517 QSizeF QgsVectorLabelLegendNode::drawSymbol( const QgsLegendSettings &settings, const QgsRenderContext &renderContext, double xOffset, double yOffset ) const
1518 {
1519  const QStringList textLines( mLabelSettings.legendString() );
1520  const QgsTextFormat textFormat = mLabelSettings.format();
1521  QgsRenderContext ctx( renderContext );
1522  double textWidth, textHeight;
1523  textWidthHeight( textWidth, textHeight, ctx, textFormat, textLines );
1524  textWidth /= renderContext.scaleFactor();
1525  textHeight /= renderContext.scaleFactor();
1526  const QPointF textPos( renderContext.scaleFactor() * ( xOffset + settings.symbolSize().width() / 2.0 - textWidth / 2.0 ), renderContext.scaleFactor() * ( yOffset + settings.symbolSize().height() / 2.0 + textHeight / 2.0 ) );
1527 
1528  const QgsScopedRenderContextScaleToPixels scopedScaleToPixels( ctx );
1529  QgsTextRenderer::drawText( textPos, 0.0, QgsTextRenderer::AlignLeft, textLines, ctx, textFormat );
1530 
1531  const double symbolWidth = std::max( textWidth, settings.symbolSize().width() );
1532  const double symbolHeight = std::max( textHeight, settings.symbolSize().height() );
1533  return QSizeF( symbolWidth, symbolHeight );
1534 }
1535 
1536 QJsonObject QgsVectorLabelLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const
1537 {
1538  Q_UNUSED( settings );
1539 
1540  const double mmToPixel = 96.0 / 25.4; //settings.dpi() is deprecated
1541 
1542  const QStringList textLines( mLabelSettings.legendString() );
1543  const QgsTextFormat textFormat = mLabelSettings.format();
1544  QgsRenderContext ctx( context );
1545  ctx.setScaleFactor( mmToPixel );
1546 
1547  double textWidth, textHeight;
1548  textWidthHeight( textWidth, textHeight, ctx, textFormat, textLines );
1549  const QPixmap previewPixmap = QgsPalLayerSettings::labelSettingsPreviewPixmap( mLabelSettings, QSize( textWidth, textHeight ), mLabelSettings.legendString() );
1550 
1551  QByteArray byteArray;
1552  QBuffer buffer( &byteArray );
1553  previewPixmap.save( &buffer, "PNG" );
1554  const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1555 
1556  QJsonObject json;
1557  json[ QStringLiteral( "icon" ) ] = base64;
1558  return json;
1559 }
1560 
1561 void QgsVectorLabelLegendNode::textWidthHeight( double &width, double &height, QgsRenderContext &ctx, const QgsTextFormat &textFormat, const QStringList &textLines ) const
1562 {
1563  QFontMetricsF fm = QgsTextRenderer::fontMetrics( ctx, textFormat );
1564  height = QgsTextRenderer::textHeight( ctx, textFormat, 'A', true );
1565  width = QgsTextRenderer::textWidth( ctx, textFormat, textLines, &fm );
1566 }
1567 
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
@ LosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
@ Antialiasing
Use antialiasing while drawing.
@ UseAdvancedEffects
Enable layer opacity and blending effects.
@ Marker
Marker symbol.
@ Line
Line symbol.
QgsDataDefinedSizeLegendNode(QgsLayerTreeLayer *nodeLayer, const QgsDataDefinedSizeLegend &settings, QObject *parent=nullptr)
Construct the node using QgsDataDefinedSizeLegend as definition of the node's appearance.
ItemMetrics draw(const QgsLegendSettings &settings, ItemContext *ctx) override
Entry point called from QgsLegendRenderer to do the rendering.
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
Object that keeps configuration of appearance of marker symbol's data-defined size in legend.
void setFont(const QFont &font)
Sets font used for rendering of labels - only valid for collapsed legend.
void setTextColor(const QColor &color)
Sets text color for rendering of labels - only valid for collapsed legend.
QImage collapsedLegendImage(QgsRenderContext &context, const QColor &backgroundColor=Qt::transparent, double paddingMM=1) const
Returns output image that would be shown in the legend. Returns invalid image if legend is not config...
void drawCollapsedLegend(QgsRenderContext &context, QSizeF *outputSize SIP_OUT=nullptr, double *labelXOffset SIP_OUT=nullptr) const
Draw the legend if using LegendOneNodeForAll and optionally output size of the legend and x offset of...
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
static QString replaceExpressionText(const QString &action, const QgsExpressionContext *context, const QgsDistanceArea *distanceArea=nullptr)
This function replaces each expression between [% and %] in the string with the result of its evaluat...
virtual void setLegendSymbolItem(const QString &key, QgsSymbol *symbol)
Sets the symbol to be used for a legend symbol item.
virtual void checkLegendSymbolItem(const QString &key, bool state=true)
item in symbology was checked
static QString representFileSize(qint64 bytes)
Returns the human size from bytes.
void progress(qint64 received, qint64 total)
Emitted to report progress.
void error(const QString &msg)
Emitted when an error occurs.
void finish(const QImage &legend)
Emitted when the download completes.
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
QgsImageLegendNode(QgsLayerTreeLayer *nodeLayer, const QImage &img, QObject *parent=nullptr)
Constructor for QgsImageLegendNode.
QSizeF drawSymbol(const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight) const override
Draws symbol on the left side of the item.
QJsonObject exportSymbolToJson(const QgsLegendSettings &settings, const QgsRenderContext &context) const override
Adds a symbol in base64 string within a JSON object with the key "icon".
static QRect nonTransparentImageRect(const QImage &image, QSize minSize=QSize(), bool center=false)
Calculates the non-transparent region of an image.
Layer tree node points to a map layer.
QString labelExpression() const
Returns the expression member of the LayerTreeNode.
QgsLegendPatchShape patchShape() const
Returns the symbol patch shape to use when rendering the legend node symbol.
QString name() const override
Returns the layer's name.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
QSizeF patchSize() const
Returns the user (overridden) size for the legend node.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
virtual QVariant data(int role) const =0
Returns data associated with the item. Must be implemented in derived class.
QJsonObject exportToJson(const QgsLegendSettings &settings, const QgsRenderContext &context)
Entry point called from QgsLegendRenderer to do the rendering in a JSON object.
@ SimpleLegend
Simple label with icon legend node type.
@ RasterSymbolLegend
Raster symbol legend node type.
@ ImageLegend
Raster image legend node type.
@ DataDefinedSizeLegend
Marker symbol legend node type.
@ SymbolLegend
Vector symbol legend node type.
void checkAllItems()
Checks all checkable items belonging to the same layer as this node.
void uncheckAllItems()
Unchecks all checkable items belonging to the same layer as this node.
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
QgsLayerTreeModelLegendNode(QgsLayerTreeLayer *nodeL, QObject *parent=nullptr)
Construct the node with pointer to its parent layer node.
virtual void setUserPatchSize(QSizeF size)
Sets the user (overridden) size for the legend node.
@ ParentRuleKeyRole
Rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2....
@ RuleKeyRole
Rule key of the node (QString)
@ NodeTypeRole
Type of node. Added in 3.16.
void sizeChanged()
Emitted when the size of this node changes.
void dataChanged()
Emitted on internal data change so the layer tree model can forward the signal to views.
QgsRenderContext * createTemporaryRenderContext() const
Returns a temporary context or nullptr if legendMapViewData are not valid.
virtual QJsonObject exportSymbolToJson(const QgsLegendSettings &settings, const QgsRenderContext &context) const
Adds a symbol in base64 string within a JSON object with the key "icon".
QgsLayerTreeModel * model() const
Returns pointer to model owning this legend node.
void toggleAllItems()
Toggle all checkable items belonging to the same layer as this node.
virtual QSizeF userPatchSize() const
Returns the user (overridden) size for the legend node.
virtual ItemMetrics draw(const QgsLegendSettings &settings, ItemContext *ctx)
Entry point called from QgsLegendRenderer to do the rendering.
virtual Qt::ItemFlags flags() const
Returns item flags associated with the item. Default implementation returns Qt::ItemIsEnabled.
virtual QSizeF drawSymbol(const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight) const
Draws symbol on the left side of the item.
virtual void setEmbeddedInParent(bool embedded)
virtual bool setData(const QVariant &value, int role)
Sets some data associated with the item. Default implementation does nothing and returns false.
virtual QSizeF drawSymbolText(const QgsLegendSettings &settings, ItemContext *ctx, QSizeF symbolSize) const
Draws label on the right side of the item.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
const QgsMapSettings * legendFilterMapSettings() const
Returns the current map settings used for the current legend filter (or nullptr if none is enabled)
static int scaleIconSize(int standardSize)
Scales an layer tree model icon size to compensate for display pixel density, making the icon size hi...
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file.
Represents a patch shape for use in map legends.
The QgsLegendSettings class stores the appearance and layout settings for legend drawing with QgsLege...
void drawText(QPainter *p, double x, double y, const QString &text, const QFont &font) const
Draws Text.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns the style for a legend component.
bool drawRasterStroke() const
Returns whether a stroke will be drawn around raster symbol items.
QSizeF wmsLegendSize() const
Returns the size (in millimeters) of WMS legend graphics shown in the legend.
double minimumSymbolSize() const
Returns the minimum symbol size (in mm).
double rasterStrokeWidth() const
Returns the stroke width (in millimeters) for the stroke drawn around raster symbol items.
double fontDescentMillimeters(const QFont &font) const
Returns the font descent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCA...
QSizeF symbolSize() const
Returns the default symbol size (in millimeters) used for legend items.
QColor fontColor() const
Returns the font color used for legend items.
double maximumSymbolSize() const
Returns the maximum symbol size (in mm).
QColor rasterStrokeColor() const
Returns the stroke color for the stroke drawn around raster symbol items.
double textWidthMillimeters(const QFont &font, const QString &text) const
Returns the font width in millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE...
Q_DECL_DEPRECATED bool useAdvancedEffects() const
double fontHeightCharacterMM(const QFont &font, QChar c) const
Returns the font height of a character in millimeters.
Q_DECL_DEPRECATED int dpi() const
double lineSpacing() const
Returns the line spacing to use between lines of legend text.
Q_DECL_DEPRECATED double mmPerMapUnit() const
QStringList evaluateItemText(const QString &text, const QgsExpressionContext &context) const
Splits a string using the wrap char taking into account handling empty wrap char which means no wrapp...
Qt::AlignmentFlag symbolAlignment() const
Returns the alignment for placement of legend symbols.
Q_DECL_DEPRECATED double mapScale() const
Returns the legend map scale.
double margin(Side side)
Returns the margin (in mm) for the specified side of the component.
Qt::Alignment alignment() const
Returns the alignment for the legend component.
QFont font() const
Returns the font used for rendering this legend component.
@ Right
Right side.
@ Left
Left side.
@ Symbol
Symbol icon (excluding label)
@ SymbolLabel
Symbol label (excluding icon)
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
QString parentRuleKey() const
Key of the parent legend node.
int scaleMaxDenom() const
Max scale denominator of the scale range.
void setSymbol(QgsSymbol *s)
Sets the symbol of the item.
QgsSymbol * symbol() const
Returns associated symbol. May be nullptr.
int scaleMinDenom() const
Min scale denominator of the scale range.
QString ruleKey() const
Returns unique identifier of the rule for identification of the item within renderer.
bool isCheckable() const
Returns whether the item is user-checkable - whether renderer supports enabling/disabling it.
QString label() const
Returns text label.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted.
void emitStyleChanged()
Triggers an emission of the styleChanged() signal.
bool isValid
Definition: qgsmaplayer.h:81
The QgsMapSettings class contains configuration for rendering of the map.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
A marker symbol type, for rendering Point and MultiPoint geometries.
Contains settings for how a map layer will be labeled.
QString legendString() const
legendString
static QPixmap labelSettingsPreviewPixmap(const QgsPalLayerSettings &settings, QSize size, const QString &previewText=QString(), int padding=0)
Returns a pixmap preview for label settings.
const QgsTextFormat & format() const
Returns the label text formatting settings, e.g., font settings, buffer settings, etc.
Represents a map layer supporting display of point clouds.
Base class for raster data providers.
virtual QgsImageFetcher * getLegendGraphicFetcher(const QgsMapSettings *mapSettings)
Returns a new image downloader for the raster legend.
Represents a raster layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
Raster renderer pipe that applies colors to a raster.
Qt::ItemFlags flags() const override
Returns item flags associated with the item. Default implementation returns Qt::ItemIsEnabled.
bool setData(const QVariant &value, int role) override
Sets some data associated with the item. Default implementation does nothing and returns false.
QJsonObject exportSymbolToJson(const QgsLegendSettings &settings, const QgsRenderContext &context) const override
Adds a symbol in base64 string within a JSON object with the key "icon".
QgsRasterSymbolLegendNode(QgsLayerTreeLayer *nodeLayer, const QColor &color, const QString &label, QObject *parent=nullptr, bool isCheckable=false, const QString &ruleKey=QString())
Constructor for QgsRasterSymbolLegendNode.
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
QSizeF drawSymbol(const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight) const override
Draws symbol on the left side of the item.
Contains information about the context of a rendering operation.
void setForceVectorOutput(bool force)
Sets whether rendering operations should use vector operations instead of any faster raster shortcuts...
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
void setMapToPixel(const QgsMapToPixel &mtp)
Sets the context's map to pixel transform, which transforms between map coordinates and device coordi...
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
void setRendererScale(double scale)
Sets the renderer map scale.
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
Scoped object for saving and restoring a QPainter object's state.
Scoped object for temporary scaling of a QgsRenderContext for pixel based rendering.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
QgsSimpleLegendNode(QgsLayerTreeLayer *nodeLayer, const QString &label, const QIcon &icon=QIcon(), QObject *parent=nullptr, const QString &key=QString())
Constructor for QgsSimpleLegendNode.
static QPixmap symbolPreviewPixmap(const QgsSymbol *symbol, QSize size, int padding=0, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *shape=nullptr)
Returns a pixmap preview for a color ramp.
static QgsSymbol * restrictedSizeSymbol(const QgsSymbol *s, double minSize, double maxSize, QgsRenderContext *context, double &width, double &height)
Creates a new symbol with size restricted to min/max size if original size is out of min/max range.
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
QString evaluateLabel(const QgsExpressionContext &context=QgsExpressionContext(), const QString &label=QString())
Evaluates and returns the text label of the current node.
const QgsSymbol * symbol() const
Returns the symbol used by the legend node.
void setPatchShape(const QgsLegendPatchShape &shape)
Sets the symbol patch shape to use when rendering the legend node symbol.
QgsSymbolLegendNode(QgsLayerTreeLayer *nodeLayer, const QgsLegendSymbolItem &item, QObject *parent=nullptr)
Constructor for QgsSymbolLegendNode.
QgsLegendPatchShape patchShape() const
Returns the symbol patch shape to use when rendering the legend node symbol.
QSize minimumIconSize() const
Calculates the minimum icon size to prevent cropping.
QSizeF drawSymbol(const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight) const override
Draws symbol on the left side of the item.
void invalidateMapBasedData() override
Notification from model that information from associated map view has changed.
bool setData(const QVariant &value, int role) override
Sets some data associated with the item. Default implementation does nothing and returns false.
Qt::ItemFlags flags() const override
Returns item flags associated with the item. Default implementation returns Qt::ItemIsEnabled.
void setCustomSymbol(QgsSymbol *symbol)
Sets the node's custom symbol.
void setEmbeddedInParent(bool embedded) override
QgsSymbol * customSymbol() const
Returns the node's custom symbol.
QString symbolLabel() const
Label of the symbol, user defined label will be used, otherwise will default to the label made by QGI...
void setSymbol(QgsSymbol *symbol)
Sets the symbol to be used by the legend node.
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
QJsonObject exportSymbolToJson(const QgsLegendSettings &settings, const QgsRenderContext &context) const override
Adds a symbol in base64 string within a JSON object with the key "icon".
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:38
void drawPreviewIcon(QPainter *painter, QSize size, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *patchShape=nullptr)
Draws an icon of the symbol that occupies an area given by size using the specified painter.
Definition: qgssymbol.cpp:566
bool usesMapUnits() const
Returns true if the symbol has any components which use map unit based sizes.
Definition: qgssymbol.cpp:296
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:97
Container for all settings relating to text rendering.
Definition: qgstextformat.h:41
QFont scaledFont(const QgsRenderContext &context, double scaleFactor=1.0, bool *isZeroSize=nullptr) const
Returns a font with the size scaled to match the format's size settings (including units and map unit...
static double textWidth(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF *fontMetrics=nullptr)
Returns the width of a text based on a given format.
static void drawText(const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true, VAlignment vAlignment=AlignTop, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags())
Draws text within a rectangle using the specified settings.
@ AlignLeft
Left align.
@ AlignCenter
Center align.
static QFontMetricsF fontMetrics(QgsRenderContext &context, const QgsTextFormat &format, double scaleFactor=1.0)
Returns the font metrics for the given text format, when rendered in the specified render context.
static double textHeight(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode=Point, QFontMetricsF *fontMetrics=nullptr, Qgis::TextRendererFlags flags=Qgis::TextRendererFlags(), double maxLineWidth=0)
Returns the height of a text based on a given format.
@ Point
Text at point of origin draw mode.
QgsVectorLabelLegendNode(QgsLayerTreeLayer *nodeLayer, const QgsPalLayerSettings &labelSettings, QObject *parent=nullptr)
QgsVectorLabelLegendNode.
QSizeF drawSymbol(const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight) const override
drawSymbol
QJsonObject exportSymbolToJson(const QgsLegendSettings &settings, const QgsRenderContext &context) const override
exportSymbolToJson
QVariant data(int role) const override
data Returns data associated with the item
Represents a vector layer which manages a vector based data sets.
QgsExpressionContextScope * createExpressionContextScope() const FINAL
This method needs to be reimplemented in all classes which implement this interface and return an exp...
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
void symbolFeatureCountMapChanged()
Emitted when the feature count for symbols on this layer has been recalculated.
QgsFeatureRenderer * renderer()
Returns the feature renderer used for rendering the features in the layer in 2D map views.
QSizeF drawSymbol(const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight) const override
Draws symbol on the left side of the item.
void invalidateMapBasedData() override
Notification from model that information from associated map view has changed.
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
~QgsWmsLegendNode() override
QgsWmsLegendNode(QgsLayerTreeLayer *nodeLayer, QObject *parent=nullptr)
Constructor for QgsWmsLegendNode.
QJsonObject exportSymbolToJson(const QgsLegendSettings &settings, const QgsRenderContext &context) const override
Adds a symbol in base64 string within a JSON object with the key "icon".
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:2065
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2064
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1578
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Single variable definition for use within a QgsExpressionContextScope.
double top
Top y-position of legend item.
QgsLegendPatchShape patchShape
The patch shape to render for the node.
double maxSiblingSymbolWidth
Largest symbol width, considering all other sibling legend components associated with the current com...
QSizeF patchSize
Symbol patch size to render for the node.
double columnLeft
Left side of current legend column.
double columnRight
Right side of current legend column.
Q_NOWARN_DEPRECATED_POP QgsRenderContext * context
Render context, if available.