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