QGIS API Documentation  3.6.0-Noosa (5873452)
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 "qgsrasterrenderer.h"
32 
34  : QObject( parent )
35  , mLayerNode( nodeL )
36  , mEmbeddedInParent( false )
37 {
38 }
39 
41 {
42  return qobject_cast<QgsLayerTreeModel *>( parent() );
43 }
44 
46 {
47  return Qt::ItemIsEnabled;
48 }
49 
50 bool QgsLayerTreeModelLegendNode::setData( const QVariant &value, int role )
51 {
52  Q_UNUSED( value );
53  Q_UNUSED( role );
54  return false;
55 }
56 
57 
59 {
60  QFont symbolLabelFont = settings.style( QgsLegendStyle::SymbolLabel ).font();
61 
62  double textHeight = settings.fontHeightCharacterMM( symbolLabelFont, QChar( '0' ) );
63  // itemHeight here is not really item height, it is only for symbol
64  // vertical alignment purpose, i.e. OK take single line height
65  // if there are more lines, thos run under the symbol
66  double itemHeight = std::max( static_cast< double >( settings.symbolSize().height() ), textHeight );
67 
68  ItemMetrics im;
69  im.symbolSize = drawSymbol( settings, ctx, itemHeight );
70  im.labelSize = drawSymbolText( settings, ctx, im.symbolSize );
71  return im;
72 }
73 
74 
75 QSizeF QgsLayerTreeModelLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
76 {
77  QIcon symbolIcon = data( Qt::DecorationRole ).value<QIcon>();
78  if ( symbolIcon.isNull() )
79  return QSizeF();
80 
81  if ( ctx && ctx->painter )
82  symbolIcon.paint( ctx->painter, ctx->point.x(), ctx->point.y() + ( itemHeight - settings.symbolSize().height() ) / 2,
83  settings.symbolSize().width(), settings.symbolSize().height() );
84  return settings.symbolSize();
85 }
86 
87 
88 QSizeF QgsLayerTreeModelLegendNode::drawSymbolText( const QgsLegendSettings &settings, ItemContext *ctx, QSizeF symbolSize ) const
89 {
90  QSizeF labelSize( 0, 0 );
91 
92  QFont symbolLabelFont = settings.style( QgsLegendStyle::SymbolLabel ).font();
93  double textHeight = settings.fontHeightCharacterMM( symbolLabelFont, QChar( '0' ) );
94  double textDescent = settings.fontDescentMillimeters( symbolLabelFont );
95 
96  QgsExpressionContext tempContext;
97 
98  const QStringList lines = settings.evaluateItemText( data( Qt::DisplayRole ).toString(), ctx && ctx->context ? ctx->context->expressionContext() : tempContext );
99 
100  labelSize.rheight() = lines.count() * textHeight + ( lines.count() - 1 ) * ( settings.lineSpacing() + textDescent );
101 
102  double labelX = 0.0, labelY = 0.0;
103  if ( ctx && ctx->painter )
104  {
105  ctx->painter->setPen( settings.fontColor() );
106 
107  labelX = ctx->point.x() + std::max( static_cast< double >( symbolSize.width() ), ctx->labelXOffset );
108  labelY = ctx->point.y();
109 
110  // Vertical alignment of label with symbol
111  if ( labelSize.height() < symbolSize.height() )
112  labelY += symbolSize.height() / 2 - labelSize.height() / 2; // label centered with symbol
113 
114  labelY += textHeight;
115  }
116 
117  for ( QStringList::ConstIterator itemPart = lines.constBegin(); itemPart != lines.constEnd(); ++itemPart )
118  {
119  labelSize.rwidth() = std::max( settings.textWidthMillimeters( symbolLabelFont, *itemPart ), double( labelSize.width() ) );
120 
121  if ( ctx && ctx->painter )
122  {
123  settings.drawText( ctx->painter, labelX, labelY, *itemPart, symbolLabelFont );
124  if ( itemPart != ( lines.end() - 1 ) )
125  labelY += textDescent + settings.lineSpacing() + textHeight;
126  }
127  }
128 
129  return labelSize;
130 }
131 
132 // -------------------------------------------------------------------------
133 
135  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
136  , mItem( item )
137  , mSymbolUsesMapUnits( false )
138 {
139  const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 );
140  mIconSize = QSize( iconSize, iconSize );
141 
142  updateLabel();
143  connect( qobject_cast<QgsVectorLayer *>( nodeLayer->layer() ), &QgsVectorLayer::symbolFeatureCountMapChanged, this, &QgsSymbolLegendNode::updateLabel );
144  connect( nodeLayer, &QObject::destroyed, this, [ = ]() { mLayerNode = nullptr; } );
145 
146  if ( mItem.symbol() )
147  mSymbolUsesMapUnits = ( mItem.symbol()->outputUnit() != QgsUnitTypes::RenderMillimeters );
148 }
149 
150 Qt::ItemFlags QgsSymbolLegendNode::flags() const
151 {
152  if ( mItem.isCheckable() )
153  return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
154  else
155  return Qt::ItemIsEnabled;
156 }
157 
158 
160 {
161  std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
162  return minimumIconSize( context.get() );
163 }
164 
166 {
167  const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 );
168  const int largeIconSize = QgsLayerTreeModel::scaleIconSize( 512 );
169  QSize minSz( iconSize, iconSize );
170  if ( mItem.symbol() && mItem.symbol()->type() == QgsSymbol::Marker )
171  {
173  QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), QSize( largeIconSize, largeIconSize ), 0,
174  context ).toImage(),
175  minSz,
176  true ).size();
177  }
178  else if ( mItem.symbol() && mItem.symbol()->type() == QgsSymbol::Line )
179  {
181  QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), QSize( minSz.width(), largeIconSize ), 0,
182  context ).toImage(),
183  minSz,
184  true ).size();
185  }
186 
187  if ( !mTextOnSymbolLabel.isEmpty() && context )
188  {
189  double w = QgsTextRenderer::textWidth( *context, mTextOnSymbolTextFormat, QStringList() << mTextOnSymbolLabel );
190  double h = QgsTextRenderer::textHeight( *context, mTextOnSymbolTextFormat, QStringList() << mTextOnSymbolLabel, QgsTextRenderer::Point );
191  int wInt = ceil( w ), hInt = ceil( h );
192  if ( wInt > minSz.width() ) minSz.setWidth( wInt );
193  if ( hInt > minSz.height() ) minSz.setHeight( hInt );
194  }
195 
196  if ( mItem.level() != 0 && !( model() && model()->testFlag( QgsLayerTreeModel::ShowLegendAsTree ) ) )
197  minSz.setWidth( mItem.level() * INDENT_SIZE + minSz.width() );
198 
199  return minSz;
200 }
201 
203 {
204  return mItem.symbol();
205 }
206 
208 {
209  if ( !symbol )
210  return;
211 
212  std::unique_ptr< QgsSymbol > s( symbol ); // this method takes ownership of symbol
213  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
214  if ( !vlayer || !vlayer->renderer() )
215  return;
216 
217  mItem.setSymbol( s.get() ); // doesn't transfer ownership
218  vlayer->renderer()->setLegendSymbolItem( mItem.ruleKey(), s.release() ); // DOES transfer ownership!
219 
220  mPixmap = QPixmap();
221 
222  emit dataChanged();
223  vlayer->triggerRepaint();
224 }
225 
227 {
228  checkAll( true );
229 }
230 
232 {
233  checkAll( false );
234 }
235 
237 {
238  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
239  if ( !vlayer || !vlayer->renderer() )
240  return;
241 
242  const QgsLegendSymbolList symbolList = vlayer->renderer()->legendSymbolItems();
243  for ( const auto &item : symbolList )
244  {
245  vlayer->renderer()->checkLegendSymbolItem( item.ruleKey(), ! vlayer->renderer()->legendSymbolItemChecked( item.ruleKey() ) );
246  }
247 
248  emit dataChanged();
249  vlayer->triggerRepaint();
250 }
251 
253 {
254  double scale = 0.0;
255  double mupp = 0.0;
256  int dpi = 0;
257  if ( model() )
258  model()->legendMapViewData( &mupp, &dpi, &scale );
259 
260  if ( qgsDoubleNear( mupp, 0.0 ) || dpi == 0 || qgsDoubleNear( scale, 0.0 ) )
261  return nullptr;
262 
263  // setup temporary render context
264  std::unique_ptr<QgsRenderContext> context = qgis::make_unique<QgsRenderContext>( );
265  context->setScaleFactor( dpi / 25.4 );
266  context->setRendererScale( scale );
267  context->setMapToPixel( QgsMapToPixel( mupp ) );
268  return context.release();
269 }
270 
271 void QgsSymbolLegendNode::checkAll( bool state )
272 {
273  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
274  if ( !vlayer || !vlayer->renderer() )
275  return;
276 
277  const QgsLegendSymbolList symbolList = vlayer->renderer()->legendSymbolItems();
278  for ( const auto &item : symbolList )
279  {
280  vlayer->renderer()->checkLegendSymbolItem( item.ruleKey(), state );
281  }
282 
283  emit dataChanged();
284  vlayer->triggerRepaint();
285 }
286 
287 QVariant QgsSymbolLegendNode::data( int role ) const
288 {
289  if ( role == Qt::DisplayRole )
290  {
291  return mLabel;
292  }
293  else if ( role == Qt::EditRole )
294  {
295  return mUserLabel.isEmpty() ? mItem.label() : mUserLabel;
296  }
297  else if ( role == Qt::DecorationRole )
298  {
299  if ( mPixmap.isNull() || mPixmap.size() != mIconSize )
300  {
301  QPixmap pix;
302  if ( mItem.symbol() )
303  {
304  std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
305  pix = QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), mIconSize, 0, context.get() );
306 
307  if ( !mTextOnSymbolLabel.isEmpty() && context )
308  {
309  QPainter painter( &pix );
310  painter.setRenderHint( QPainter::Antialiasing );
311  context->setPainter( &painter );
312  QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( *context ) );
313  qreal yBaselineVCenter = ( mIconSize.height() + fm.ascent() - fm.descent() ) / 2;
314  QgsTextRenderer::drawText( QPointF( mIconSize.width() / 2, yBaselineVCenter ), 0, QgsTextRenderer::AlignCenter,
315  QStringList() << mTextOnSymbolLabel, *context, mTextOnSymbolTextFormat );
316  }
317  }
318  else
319  {
320  pix = QPixmap( mIconSize );
321  pix.fill( Qt::transparent );
322  }
323 
324  if ( mItem.level() == 0 || ( model() && model()->testFlag( QgsLayerTreeModel::ShowLegendAsTree ) ) )
325  mPixmap = pix;
326  else
327  {
328  // ident the symbol icon to make it look like a tree structure
329  QPixmap pix2( pix.width() + mItem.level() * INDENT_SIZE, pix.height() );
330  pix2.fill( Qt::transparent );
331  QPainter p( &pix2 );
332  p.drawPixmap( mItem.level() * INDENT_SIZE, 0, pix );
333  p.end();
334  mPixmap = pix2;
335  }
336  }
337  return mPixmap;
338  }
339  else if ( role == Qt::CheckStateRole )
340  {
341  if ( !mItem.isCheckable() )
342  return QVariant();
343 
344  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
345  if ( !vlayer || !vlayer->renderer() )
346  return QVariant();
347 
348  return vlayer->renderer()->legendSymbolItemChecked( mItem.ruleKey() ) ? Qt::Checked : Qt::Unchecked;
349  }
350  else if ( role == RuleKeyRole )
351  {
352  return mItem.ruleKey();
353  }
354  else if ( role == ParentRuleKeyRole )
355  {
356  return mItem.parentRuleKey();
357  }
358 
359  return QVariant();
360 }
361 
362 bool QgsSymbolLegendNode::setData( const QVariant &value, int role )
363 {
364  if ( role != Qt::CheckStateRole )
365  return false;
366 
367  if ( !mItem.isCheckable() )
368  return false;
369 
370  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
371  if ( !vlayer || !vlayer->renderer() )
372  return false;
373 
374  vlayer->renderer()->checkLegendSymbolItem( mItem.ruleKey(), value == Qt::Checked );
375 
376  emit dataChanged();
377  vlayer->emitStyleChanged();
378 
379  vlayer->triggerRepaint();
380 
381  return true;
382 }
383 
384 
385 
386 QSizeF QgsSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
387 {
388  QgsSymbol *s = mItem.symbol();
389  if ( !s )
390  {
391  return QSizeF();
392  }
393 
394  // setup temporary render context
395  QgsRenderContext context;
396  context.setScaleFactor( settings.dpi() / 25.4 );
397  context.setRendererScale( settings.mapScale() );
398  context.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * context.scaleFactor() ) ) );
399  context.setForceVectorOutput( true );
400  context.setPainter( ctx ? ctx->painter : nullptr );
401 
402  if ( ctx && ctx->context )
403  {
405  }
406  else
407  {
408  // setup a minimal expression context
409  QgsExpressionContext expContext;
411  context.setExpressionContext( expContext );
412  }
413 
414  //Consider symbol size for point markers
415  double height = settings.symbolSize().height();
416  double width = settings.symbolSize().width();
417 
418  //Center small marker symbols
419  double widthOffset = 0;
420  double heightOffset = 0;
421 
422  if ( QgsMarkerSymbol *markerSymbol = dynamic_cast<QgsMarkerSymbol *>( s ) )
423  {
424  // allow marker symbol to occupy bigger area if necessary
425  double size = markerSymbol->size( context ) / context.scaleFactor();
426  height = size;
427  width = size;
428  if ( width < settings.symbolSize().width() )
429  {
430  widthOffset = ( settings.symbolSize().width() - width ) / 2.0;
431  }
432  if ( height < settings.symbolSize().height() )
433  {
434  heightOffset = ( settings.symbolSize().height() - height ) / 2.0;
435  }
436  }
437 
438  if ( ctx && ctx->painter )
439  {
440  double currentXPosition = ctx->point.x();
441  double currentYCoord = ctx->point.y() + ( itemHeight - settings.symbolSize().height() ) / 2;
442  QPainter *p = ctx->painter;
443 
444  //setup painter scaling to dots so that raster symbology is drawn to scale
445  double dotsPerMM = context.scaleFactor();
446 
447  int opacity = 255;
448  if ( QgsVectorLayer *vectorLayer = dynamic_cast<QgsVectorLayer *>( layerNode()->layer() ) )
449  opacity = ( 255 * vectorLayer->opacity() );
450 
451  p->save();
452  p->setRenderHint( QPainter::Antialiasing );
453  p->translate( currentXPosition + widthOffset, currentYCoord + heightOffset );
454  p->scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
455  if ( opacity != 255 && settings.useAdvancedEffects() )
456  {
457  //semi transparent layer, so need to draw symbol to an image (to flatten it first)
458  //create image which is same size as legend rect, in case symbol bleeds outside its alloted space
459  QSize tempImageSize( width * dotsPerMM, height * dotsPerMM );
460  QImage tempImage = QImage( tempImageSize, QImage::Format_ARGB32 );
461  tempImage.fill( Qt::transparent );
462  QPainter imagePainter( &tempImage );
463  context.setPainter( &imagePainter );
464  s->drawPreviewIcon( &imagePainter, tempImageSize, &context );
465  context.setPainter( ctx->painter );
466  //reduce opacity of image
467  imagePainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
468  imagePainter.fillRect( tempImage.rect(), QColor( 0, 0, 0, opacity ) );
469  imagePainter.end();
470  //draw rendered symbol image
471  p->drawImage( 0, 0, tempImage );
472  }
473  else
474  {
475  s->drawPreviewIcon( p, QSize( width * dotsPerMM, height * dotsPerMM ), &context );
476  }
477 
478  if ( !mTextOnSymbolLabel.isEmpty() )
479  {
480  QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( context ) );
481  qreal yBaselineVCenter = ( height * dotsPerMM + fm.ascent() - fm.descent() ) / 2;
482  QgsTextRenderer::drawText( QPointF( width * dotsPerMM / 2, yBaselineVCenter ), 0, QgsTextRenderer::AlignCenter,
483  QStringList() << mTextOnSymbolLabel, context, mTextOnSymbolTextFormat );
484  }
485 
486  p->restore();
487  }
488 
489  return QSizeF( std::max( width + 2 * widthOffset, static_cast< double >( settings.symbolSize().width() ) ),
490  std::max( height + 2 * heightOffset, static_cast< double >( settings.symbolSize().height() ) ) );
491 }
492 
493 
495 {
497  updateLabel();
498 }
499 
500 
502 {
503  if ( mSymbolUsesMapUnits )
504  {
505  mPixmap = QPixmap();
506  emit dataChanged();
507  }
508 }
509 
510 
511 void QgsSymbolLegendNode::updateLabel()
512 {
513  if ( !mLayerNode )
514  return;
515 
516  bool showFeatureCount = mLayerNode->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toBool();
517  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
518 
519  if ( mEmbeddedInParent )
520  {
521  QString layerName = mLayerNode->name();
522  if ( !mLayerNode->customProperty( QStringLiteral( "legend/title-label" ) ).isNull() )
523  layerName = mLayerNode->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
524 
525  mLabel = mUserLabel.isEmpty() ? layerName : mUserLabel;
526  if ( showFeatureCount && vl && vl->featureCount() >= 0 )
527  mLabel += QStringLiteral( " [%1]" ).arg( vl->featureCount() );
528  }
529  else
530  {
531  mLabel = mUserLabel.isEmpty() ? mItem.label() : mUserLabel;
532  if ( showFeatureCount && vl )
533  {
534  qlonglong count = vl->featureCount( mItem.ruleKey() );
535  mLabel += QStringLiteral( " [%1]" ).arg( count != -1 ? QLocale().toString( count ) : tr( "N/A" ) );
536  }
537  }
538 
539  emit dataChanged();
540 }
541 
542 
543 
544 // -------------------------------------------------------------------------
545 
546 
547 QgsSimpleLegendNode::QgsSimpleLegendNode( QgsLayerTreeLayer *nodeLayer, const QString &label, const QIcon &icon, QObject *parent, const QString &key )
548  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
549  , mLabel( label )
550  , mIcon( icon )
551  , mKey( key )
552 {
553 }
554 
555 QVariant QgsSimpleLegendNode::data( int role ) const
556 {
557  if ( role == Qt::DisplayRole || role == Qt::EditRole )
558  return mUserLabel.isEmpty() ? mLabel : mUserLabel;
559  else if ( role == Qt::DecorationRole )
560  return mIcon;
561  else if ( role == RuleKeyRole && !mKey.isEmpty() )
562  return mKey;
563  else
564  return QVariant();
565 }
566 
567 
568 // -------------------------------------------------------------------------
569 
570 QgsImageLegendNode::QgsImageLegendNode( QgsLayerTreeLayer *nodeLayer, const QImage &img, QObject *parent )
571  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
572  , mImage( img )
573 {
574 }
575 
576 QVariant QgsImageLegendNode::data( int role ) const
577 {
578  if ( role == Qt::DecorationRole )
579  {
580  return QPixmap::fromImage( mImage );
581  }
582  else if ( role == Qt::SizeHintRole )
583  {
584  return mImage.size();
585  }
586  return QVariant();
587 }
588 
589 QSizeF QgsImageLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
590 {
591  Q_UNUSED( itemHeight );
592 
593  if ( ctx && ctx->painter )
594  {
595  ctx->painter->drawImage( QRectF( ctx->point.x(), ctx->point.y(), settings.wmsLegendSize().width(), settings.wmsLegendSize().height() ),
596  mImage, QRectF( 0, 0, mImage.width(), mImage.height() ) );
597  }
598  return settings.wmsLegendSize();
599 }
600 
601 // -------------------------------------------------------------------------
602 
603 QgsRasterSymbolLegendNode::QgsRasterSymbolLegendNode( QgsLayerTreeLayer *nodeLayer, const QColor &color, const QString &label, QObject *parent )
604  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
605  , mColor( color )
606  , mLabel( label )
607 {
608 }
609 
610 QVariant QgsRasterSymbolLegendNode::data( int role ) const
611 {
612  if ( role == Qt::DecorationRole )
613  {
614  const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 ); // TODO: configurable?
615  QPixmap pix( iconSize, iconSize );
616  pix.fill( mColor );
617  return QIcon( pix );
618  }
619  else if ( role == Qt::DisplayRole || role == Qt::EditRole )
620  return mUserLabel.isEmpty() ? mLabel : mUserLabel;
621  else
622  return QVariant();
623 }
624 
625 
626 QSizeF QgsRasterSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
627 {
628  if ( ctx && ctx->painter )
629  {
630  QColor itemColor = mColor;
631  if ( QgsRasterLayer *rasterLayer = dynamic_cast<QgsRasterLayer *>( layerNode()->layer() ) )
632  {
633  if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
634  itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
635  }
636  ctx->painter->setBrush( itemColor );
637 
638  if ( settings.drawRasterStroke() )
639  {
640  QPen pen;
641  pen.setColor( settings.rasterStrokeColor() );
642  pen.setWidthF( settings.rasterStrokeWidth() );
643  pen.setJoinStyle( Qt::MiterJoin );
644  ctx->painter->setPen( pen );
645  }
646  else
647  {
648  ctx->painter->setPen( Qt::NoPen );
649  }
650 
651  ctx->painter->drawRect( QRectF( ctx->point.x(), ctx->point.y() + ( itemHeight - settings.symbolSize().height() ) / 2,
652  settings.symbolSize().width(), settings.symbolSize().height() ) );
653  }
654  return settings.symbolSize();
655 }
656 
657 // -------------------------------------------------------------------------
658 
660  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
661  , mValid( false )
662 {
663 }
664 
665 QImage QgsWmsLegendNode::getLegendGraphic() const
666 {
667  if ( ! mValid && ! mFetcher )
668  {
669  // or maybe in presence of a downloader we should just delete it
670  // and start a new one ?
671 
672  QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( mLayerNode->layer() );
673  const QgsLayerTreeModel *mod = model();
674  if ( ! mod )
675  return mImage;
676  const QgsMapSettings *ms = mod->legendFilterMapSettings();
677 
678  QgsRasterDataProvider *prov = layer->dataProvider();
679  if ( ! prov )
680  return mImage;
681 
682  Q_ASSERT( ! mFetcher );
683  mFetcher.reset( prov->getLegendGraphicFetcher( ms ) );
684  if ( mFetcher )
685  {
686  connect( mFetcher.get(), &QgsImageFetcher::finish, this, &QgsWmsLegendNode::getLegendGraphicFinished );
687  connect( mFetcher.get(), &QgsImageFetcher::error, this, &QgsWmsLegendNode::getLegendGraphicErrored );
688  connect( mFetcher.get(), &QgsImageFetcher::progress, this, &QgsWmsLegendNode::getLegendGraphicProgress );
689  mFetcher->start();
690  } // else QgsDebugMsg("XXX No legend supported?");
691 
692  }
693 
694  return mImage;
695 }
696 
697 QVariant QgsWmsLegendNode::data( int role ) const
698 {
699  //QgsDebugMsg( QStringLiteral("XXX data called with role %1 -- mImage size is %2x%3").arg(role).arg(mImage.width()).arg(mImage.height()) );
700 
701  if ( role == Qt::DecorationRole )
702  {
703  return QPixmap::fromImage( getLegendGraphic() );
704  }
705  else if ( role == Qt::SizeHintRole )
706  {
707  return getLegendGraphic().size();
708  }
709  return QVariant();
710 }
711 
712 QSizeF QgsWmsLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
713 {
714  Q_UNUSED( itemHeight );
715 
716  if ( ctx && ctx->painter )
717  {
718  ctx->painter->drawImage( QRectF( ctx->point, settings.wmsLegendSize() ),
719  mImage,
720  QRectF( QPointF( 0, 0 ), mImage.size() ) );
721  }
722  return settings.wmsLegendSize();
723 }
724 
725 /* private */
726 QImage QgsWmsLegendNode::renderMessage( const QString &msg ) const
727 {
728  const int fontHeight = 10;
729  const int margin = fontHeight / 2;
730  const int nlines = 1;
731 
732  const int w = 512, h = fontHeight * nlines + margin * ( nlines + 1 );
733  QImage image( w, h, QImage::Format_ARGB32_Premultiplied );
734  QPainter painter;
735  painter.begin( &image );
736  painter.setPen( QColor( 255, 0, 0 ) );
737  painter.setFont( QFont( QStringLiteral( "Chicago" ), fontHeight ) );
738  painter.fillRect( 0, 0, w, h, QColor( 255, 255, 255 ) );
739  painter.drawText( 0, margin + fontHeight, msg );
740  //painter.drawText(0,2*(margin+fontHeight),tr("retrying in 5 seconds…"));
741  painter.end();
742 
743  return image;
744 }
745 
746 void QgsWmsLegendNode::getLegendGraphicProgress( qint64 cur, qint64 tot )
747 {
748  QString msg = QStringLiteral( "Downloading... %1/%2" ).arg( cur ).arg( tot );
749  //QgsDebugMsg ( QString("XXX %1").arg(msg) );
750  mImage = renderMessage( msg );
751  emit dataChanged();
752 }
753 
754 void QgsWmsLegendNode::getLegendGraphicErrored( const QString &msg )
755 {
756  if ( ! mFetcher ) return; // must be coming after finish
757 
758  mImage = renderMessage( msg );
759  //QgsDebugMsg( QStringLiteral("XXX emitting dataChanged after writing an image of %1x%2").arg(mImage.width()).arg(mImage.height()) );
760 
761  emit dataChanged();
762 
763  mFetcher.reset();
764 
765  mValid = true; // we consider it valid anyway
766  // ... but remove validity after 5 seconds
767  //QTimer::singleShot(5000, this, SLOT(invalidateMapBasedData()));
768 }
769 
770 void QgsWmsLegendNode::getLegendGraphicFinished( const QImage &image )
771 {
772  if ( ! mFetcher ) return; // must be coming after error
773 
774  //QgsDebugMsg( QStringLiteral("XXX legend graphic finished, image is %1x%2").arg(theImage.width()).arg(theImage.height()) );
775  if ( ! image.isNull() )
776  {
777  if ( image != mImage )
778  {
779  mImage = image;
780  //QgsDebugMsg( QStringLiteral("XXX emitting dataChanged") );
781  emit dataChanged();
782  }
783  mValid = true; // only if not null I guess
784  }
785  mFetcher.reset();
786 }
787 
789 {
790  //QgsDebugMsg( QStringLiteral("XXX invalidateMapBasedData called") );
791  // TODO: do this only if this extent != prev extent ?
792  mValid = false;
793  emit dataChanged();
794 }
795 
796 // -------------------------------------------------------------------------
797 
799  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
800  , mSettings( new QgsDataDefinedSizeLegend( settings ) )
801 {
802 }
803 
805 {
806  delete mSettings;
807 }
808 
809 QVariant QgsDataDefinedSizeLegendNode::data( int role ) const
810 {
811  if ( role == Qt::DecorationRole )
812  {
813  cacheImage();
814  return QPixmap::fromImage( mImage );
815  }
816  else if ( role == Qt::SizeHintRole )
817  {
818  cacheImage();
819  return mImage.size();
820  }
821  return QVariant();
822 }
823 
825 {
826  // setup temporary render context
827  QgsRenderContext context;
828  context.setScaleFactor( settings.dpi() / 25.4 );
829  context.setRendererScale( settings.mapScale() );
830  context.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * context.scaleFactor() ) ) );
831  context.setForceVectorOutput( true );
832 
833  if ( ctx && ctx->painter )
834  {
835  context.setPainter( ctx->painter );
836  ctx->painter->save();
837  ctx->painter->setRenderHint( QPainter::Antialiasing );
838  ctx->painter->translate( ctx->point );
839  ctx->painter->scale( 1 / context.scaleFactor(), 1 / context.scaleFactor() );
840  }
841 
842  QgsDataDefinedSizeLegend ddsLegend( *mSettings );
843  ddsLegend.setFont( settings.style( QgsLegendStyle::SymbolLabel ).font() );
844  ddsLegend.setTextColor( settings.fontColor() );
845 
846  QSize contentSize;
847  int labelXOffset;
848  ddsLegend.drawCollapsedLegend( context, &contentSize, &labelXOffset );
849 
850  if ( ctx && ctx->painter )
851  ctx->painter->restore();
852 
853  ItemMetrics im;
854  im.symbolSize = QSizeF( ( contentSize.width() - labelXOffset ) / context.scaleFactor(), contentSize.height() / context.scaleFactor() );
855  im.labelSize = QSizeF( labelXOffset / context.scaleFactor(), contentSize.height() / context.scaleFactor() );
856  return im;
857 }
858 
859 
860 void QgsDataDefinedSizeLegendNode::cacheImage() const
861 {
862  if ( mImage.isNull() )
863  {
864  std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
865  if ( !context )
866  {
867  context.reset( new QgsRenderContext );
868  context->setScaleFactor( 96 / 25.4 );
869  }
870  mImage = mSettings->collapsedLegendImage( *context );
871  }
872 }
void setForceVectorOutput(bool force)
void drawText(QPainter *p, double x, double y, const QString &text, const QFont &font) const
Draws Text.
virtual QgsLegendSymbolList legendSymbolItems() const
Returns a list of symbology items for the legend.
QgsRenderContext * context
Render context, if available.
QgsDataDefinedSizeLegendNode(QgsLayerTreeLayer *nodeLayer, const QgsDataDefinedSizeLegend &settings, QObject *parent=nullptr)
Construct the node using QgsDataDefinedSizeLegend as definition of the node&#39;s appearance.
virtual Qt::ItemFlags flags() const
Returns item flags associated with the item. Default implementation returns Qt::ItemIsEnabled.
double mapScale() const
Returns the legend map scale.
QgsRenderContext * createTemporaryRenderContext() const
Returns a temporary context or null if legendMapViewData are not valid.
QList< QgsLegendSymbolItem > QgsLegendSymbolList
Text at point of origin draw mode.
QgsSymbol * symbol() const
Returns associated symbol. May be null.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:61
QString ruleKey() const
Returns unique identifier of the rule for identification of the item within renderer.
bool useAdvancedEffects() const
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
virtual QSizeF drawSymbol(const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight) const
Draws symbol on the left side of the item.
void setFont(const QFont &font)
Sets font used for rendering of labels - only valid for collapsed legend.
QgsImageLegendNode(QgsLayerTreeLayer *nodeLayer, const QImage &img, QObject *parent=nullptr)
Constructor for QgsImageLegendNode.
virtual bool setData(const QVariant &value, int role)
Sets some data associated with the item. Default implementation does nothing and returns false...
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...
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
QgsSymbolLegendNode(QgsLayerTreeLayer *nodeLayer, const QgsLegendSymbolItem &item, QObject *parent=nullptr)
Constructor for QgsSymbolLegendNode.
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
bool testFlag(Flag f) const
Check whether a flag is enabled.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:265
void emitStyleChanged()
Triggers an emission of the styleChanged() signal.
void setRendererScale(double scale)
Sets the renderer map scale.
QFont font() const
The font for this style.
virtual QgsImageFetcher * getLegendGraphicFetcher(const QgsMapSettings *mapSettings)
Returns a new image downloader for the raster legend.
void invalidateMapBasedData() override
Notification from model that information from associated map view has changed.
void invalidateMapBasedData() override
Notification from model that information from associated map view has changed.
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
bool drawRasterStroke() const
Returns whether a stroke will be drawn around raster symbol items.
Line symbol.
Definition: qgssymbol.h:86
double textWidthMillimeters(const QFont &font, const QString &text) const
Returns the font width in millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE...
QgsLayerTreeModelLegendNode(QgsLayerTreeLayer *nodeL, QObject *parent=nullptr)
Construct the node with pointer to its parent layer node.
QString label() const
Returns text label.
QgsWmsLegendNode(QgsLayerTreeLayer *nodeLayer, QObject *parent=nullptr)
Constructor for QgsWmsLegendNode.
QSizeF drawSymbol(const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight) const override
Draws symbol on the left side of the item.
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:732
QColor rasterStrokeColor() const
Returns the stroke color for the stroke drawn around raster symbol items.
The QgsMapSettings class contains configuration for rendering of the map.
QgsUnitTypes::RenderUnit outputUnit() const
Returns the units to use for sizes and widths within the symbol.
Definition: qgssymbol.cpp:216
void toggleAllItems()
Toggle all items belonging to the same layer as this node.
QSize minimumIconSize() const
Calculates the minimum icon size to prevent cropping.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
The QgsLayerTreeModel class is model implementation for Qt item views framework.
QSizeF wmsLegendSize() const
Rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2...
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns style.
QgsRasterDataProvider * dataProvider() override
Returns the layer&#39;s data provider, it may be null.
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...
QString name() const override
Returns the layer&#39;s name.
void setScaleFactor(double factor)
Sets the scaling factor for the render to convert painter units to physical sizes.
QSizeF drawSymbol(const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight) const override
Draws symbol on the left side of the item.
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted...
static int scaleIconSize(int standardSize)
Scales an layer tree model icon size to compensate for display pixel density, making the icon size hi...
Qt::ItemFlags flags() const override
Returns item flags associated with the item. Default implementation returns Qt::ItemIsEnabled.
void setSymbol(QgsSymbol *s)
Sets the symbol of the item.
QgsRasterSymbolLegendNode(QgsLayerTreeLayer *nodeLayer, const QColor &color, const QString &label, QObject *parent=nullptr)
Constructor for QgsRasterSymbolLegendNode.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
void checkAllItems()
Checks all items belonging to the same layer as this node.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void error(const QString &msg)
Emitted when an error occurs.
double rasterStrokeWidth() const
Returns the stroke width (in millimeters) for the stroke drawn around raster symbol items...
QgsLayerTreeModel * model() const
Returns pointer to model owning this legend node.
The QgsLegendSettings class stores the appearance and layout settings for legend drawing with QgsLege...
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...
ItemMetrics draw(const QgsLegendSettings &settings, ItemContext *ctx) override
Entry point called from QgsLegendRenderer to do the rendering.
void drawPreviewIcon(QPainter *painter, QSize size, QgsRenderContext *customContext=nullptr)
Draws an icon of the symbol that occupies an area given by size using the specified painter...
Definition: qgssymbol.cpp:472
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
virtual bool legendSymbolItemChecked(const QString &key)
items of symbology items in legend is checked
QgsFeatureRenderer * renderer()
Returns renderer.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer&#39;s project and layer.
void dataChanged()
Emitted on internal data change so the layer tree model can forward the signal to views...
void symbolFeatureCountMapChanged()
Emitted when the feature count for symbols on this layer has been recalculated.
virtual void checkLegendSymbolItem(const QString &key, bool state=true)
item in symbology was checked
virtual void setLegendSymbolItem(const QString &key, QgsSymbol *symbol)
Sets the symbol to be used for a legend symbol item.
bool isCheckable() const
Returns whether the item is user-checkable - whether renderer supports enabling/disabling it...
QSizeF drawSymbol(const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight) const override
Draws symbol on the left side of the item.
QSizeF symbolSize() const
static void drawText(const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true)
Draws text within a rectangle using the specified settings.
double fontHeightCharacterMM(const QFont &font, QChar c) const
Returns the font height of a character in millimeters.
virtual ItemMetrics draw(const QgsLegendSettings &settings, ItemContext *ctx)
Entry point called from QgsLegendRenderer to do the rendering.
QgsExpressionContext & expressionContext()
Gets the expression context.
void setEmbeddedInParent(bool embedded) override
QgsMapLayer * layer() const
Returns the map layer associated with this node.
void setSymbol(QgsSymbol *symbol)
Sets the symbol to be used by the legend node.
const QgsSymbol * symbol() const
Returns the symbol used by the legend node.
Marker symbol.
Definition: qgssymbol.h:85
QgsSimpleLegendNode(QgsLayerTreeLayer *nodeLayer, const QString &label, const QIcon &icon=QIcon(), QObject *parent=nullptr, const QString &key=QString())
Constructor for QgsSimpleLegendNode.
virtual void setEmbeddedInParent(bool embedded)
QFont scaledFont(const QgsRenderContext &context) const
Returns a font with the size scaled to match the format&#39;s size settings (including units and map unit...
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
Contains information about the context of a rendering operation.
SymbolType type() const
Returns the symbol&#39;s type.
Definition: qgssymbol.h:120
QSizeF drawSymbol(const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight) const override
Draws symbol on the left side of the item.
QPointF point
Top-left corner of the legend item.
bool setData(const QVariant &value, int role) override
Sets some data associated with the item. Default implementation does nothing and returns false...
void setMapToPixel(const QgsMapToPixel &mtp)
virtual QVariant data(int role) const =0
Returns data associated with the item. Must be implemented in derived class.
void legendMapViewData(double *mapUnitsPerPixel, int *dpi, double *scale) const
Gets hints about map view - to be used in legend nodes.
static QRect nonTransparentImageRect(const QImage &image, QSize minSize=QSize(), bool center=false)
Calculates the non-transparent region of an image.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
void uncheckAllItems()
Unchecks all items belonging to the same layer as this node.
void appendScopes(const QList< QgsExpressionContextScope *> &scopes)
Appends a list of scopes to the end of the context.
void setTextColor(const QColor &color)
Sets text color for rendering of labels - only valid for collapsed legend.
virtual QSizeF drawSymbolText(const QgsLegendSettings &settings, ItemContext *ctx, QSizeF symbolSize) const
Draws label on the right side of the item.
QString parentRuleKey() const
Key of the parent legend node.
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
void drawCollapsedLegend(QgsRenderContext &context, QSize *outputSize SIP_OUT=nullptr, int *labelXOffset SIP_OUT=nullptr) const
Draw the legend if using LegendOneNodeForAll and optionally output size of the legend and x offset of...
double lineSpacing() const
For legends that support it, will show them in a tree instead of a list (needs also ShowLegend)...
QColor fontColor() const
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.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Represents a vector layer which manages a vector based data sets.
double labelXOffset
offset from the left side where label should start
void finish(const QImage &legend)
Emitted when the download completes.
Object that keeps configuration of appearance of marker symbol&#39;s data-defined size in legend...
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
int level() const
Indentation level that tells how deep the item is in a hierarchy of items. For flat lists level is 0...
double fontDescentMillimeters(const QFont &font) const
Returns the font descent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCA...
double mmPerMapUnit() const
Raster renderer pipe that applies colors to a raster.
void progress(qint64 received, qint64 total)
Emitted to report progress.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
Layer tree node points to a map layer.
Base class for raster data providers.
static double textHeight(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode, QFontMetricsF *fontMetrics=nullptr)
Returns the height of a text based on a given format.
static QPixmap symbolPreviewPixmap(const QgsSymbol *symbol, QSize size, int padding=0, QgsRenderContext *customContext=nullptr)
Returns a pixmap preview for a color ramp.