QGIS API Documentation  3.0.2-Girona (307d082)
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 
131 
133  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
134  , mItem( item )
135  , mSymbolUsesMapUnits( false )
136  , mIconSize( 16, 16 )
137 {
138  updateLabel();
139  connect( qobject_cast<QgsVectorLayer *>( nodeLayer->layer() ), &QgsVectorLayer::symbolFeatureCountMapChanged, this, &QgsSymbolLegendNode::updateLabel );
140  connect( nodeLayer, &QObject::destroyed, this, [ = ]() { mLayerNode = nullptr; } );
141 
142  if ( mItem.symbol() )
143  mSymbolUsesMapUnits = ( mItem.symbol()->outputUnit() != QgsUnitTypes::RenderMillimeters );
144 }
145 
146 Qt::ItemFlags QgsSymbolLegendNode::flags() const
147 {
148  if ( mItem.isCheckable() )
149  return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
150  else
151  return Qt::ItemIsEnabled;
152 }
153 
154 
156 {
157  std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
158  return minimumIconSize( context.get() );
159 }
160 
162 {
163  QSize minSz( 16, 16 );
164  if ( mItem.symbol() && mItem.symbol()->type() == QgsSymbol::Marker )
165  {
167  QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), QSize( 512, 512 ), 0,
168  context ).toImage(),
169  minSz,
170  true ).size();
171  }
172  else if ( mItem.symbol() && mItem.symbol()->type() == QgsSymbol::Line )
173  {
175  QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), QSize( minSz.width(), 512 ), 0,
176  context ).toImage(),
177  minSz,
178  true ).size();
179  }
180 
181  if ( mItem.level() != 0 && !( model() && model()->testFlag( QgsLayerTreeModel::ShowLegendAsTree ) ) )
182  minSz.setWidth( mItem.level() * INDENT_SIZE + minSz.width() );
183 
184  return minSz;
185 }
186 
188 {
189  return mItem.symbol();
190 }
191 
193 {
194  if ( !symbol )
195  return;
196 
197  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
198  if ( !vlayer || !vlayer->renderer() )
199  return;
200 
201  mItem.setSymbol( symbol );
202  vlayer->renderer()->setLegendSymbolItem( mItem.ruleKey(), symbol );
203 
204  mPixmap = QPixmap();
205 
206  emit dataChanged();
207  vlayer->triggerRepaint();
208 }
209 
211 {
212  checkAll( true );
213 }
214 
216 {
217  checkAll( false );
218 }
219 
221 {
222  double scale = 0.0;
223  double mupp = 0.0;
224  int dpi = 0;
225  if ( model() )
226  model()->legendMapViewData( &mupp, &dpi, &scale );
227 
228  if ( qgsDoubleNear( mupp, 0.0 ) || dpi == 0 || qgsDoubleNear( scale, 0.0 ) )
229  return nullptr;
230 
231  // setup temporary render context
232  std::unique_ptr<QgsRenderContext> context( new QgsRenderContext );
233  context->setScaleFactor( dpi / 25.4 );
234  context->setRendererScale( scale );
235  context->setMapToPixel( QgsMapToPixel( mupp ) );
236  return context.release();
237 }
238 
239 void QgsSymbolLegendNode::checkAll( bool state )
240 {
241  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
242  if ( !vlayer || !vlayer->renderer() )
243  return;
244 
245  QgsLegendSymbolList symbolList = vlayer->renderer()->legendSymbolItems();
246  Q_FOREACH ( const QgsLegendSymbolItem &item, symbolList )
247  {
248  vlayer->renderer()->checkLegendSymbolItem( item.ruleKey(), state );
249  }
250 
251  emit dataChanged();
252  vlayer->triggerRepaint();
253 }
254 
255 QVariant QgsSymbolLegendNode::data( int role ) const
256 {
257  if ( role == Qt::DisplayRole )
258  {
259  return mLabel;
260  }
261  else if ( role == Qt::EditRole )
262  {
263  return mUserLabel.isEmpty() ? mItem.label() : mUserLabel;
264  }
265  else if ( role == Qt::DecorationRole )
266  {
267  if ( mPixmap.isNull() || mPixmap.size() != mIconSize )
268  {
269  QPixmap pix;
270  if ( mItem.symbol() )
271  {
272  std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
273  pix = QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), mIconSize, 0, context.get() );
274  }
275  else
276  {
277  pix = QPixmap( mIconSize );
278  pix.fill( Qt::transparent );
279  }
280 
281  if ( mItem.level() == 0 || ( model() && model()->testFlag( QgsLayerTreeModel::ShowLegendAsTree ) ) )
282  mPixmap = pix;
283  else
284  {
285  // ident the symbol icon to make it look like a tree structure
286  QPixmap pix2( pix.width() + mItem.level() * INDENT_SIZE, pix.height() );
287  pix2.fill( Qt::transparent );
288  QPainter p( &pix2 );
289  p.drawPixmap( mItem.level() * INDENT_SIZE, 0, pix );
290  p.end();
291  mPixmap = pix2;
292  }
293  }
294  return mPixmap;
295  }
296  else if ( role == Qt::CheckStateRole )
297  {
298  if ( !mItem.isCheckable() )
299  return QVariant();
300 
301  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
302  if ( !vlayer || !vlayer->renderer() )
303  return QVariant();
304 
305  return vlayer->renderer()->legendSymbolItemChecked( mItem.ruleKey() ) ? Qt::Checked : Qt::Unchecked;
306  }
307  else if ( role == RuleKeyRole )
308  {
309  return mItem.ruleKey();
310  }
311  else if ( role == ParentRuleKeyRole )
312  {
313  return mItem.parentRuleKey();
314  }
315 
316  return QVariant();
317 }
318 
319 bool QgsSymbolLegendNode::setData( const QVariant &value, int role )
320 {
321  if ( role != Qt::CheckStateRole )
322  return false;
323 
324  if ( !mItem.isCheckable() )
325  return false;
326 
327  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
328  if ( !vlayer || !vlayer->renderer() )
329  return false;
330 
331  vlayer->renderer()->checkLegendSymbolItem( mItem.ruleKey(), value == Qt::Checked );
332 
333  emit dataChanged();
334 
335  vlayer->triggerRepaint();
336 
337  return true;
338 }
339 
340 
341 
342 QSizeF QgsSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
343 {
344  QgsSymbol *s = mItem.symbol();
345  if ( !s )
346  {
347  return QSizeF();
348  }
349 
350  // setup temporary render context
351  QgsRenderContext context;
352  context.setScaleFactor( settings.dpi() / 25.4 );
353  context.setRendererScale( settings.mapScale() );
354  context.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * context.scaleFactor() ) ) );
355  context.setForceVectorOutput( true );
356  context.setPainter( ctx ? ctx->painter : nullptr );
357 
358  //Consider symbol size for point markers
359  double height = settings.symbolSize().height();
360  double width = settings.symbolSize().width();
361 
362  //Center small marker symbols
363  double widthOffset = 0;
364  double heightOffset = 0;
365 
366  if ( QgsMarkerSymbol *markerSymbol = dynamic_cast<QgsMarkerSymbol *>( s ) )
367  {
368  // allow marker symbol to occupy bigger area if necessary
369  double size = context.convertToPainterUnits( markerSymbol->size(), markerSymbol->sizeUnit(), markerSymbol->sizeMapUnitScale() ) / context.scaleFactor();
370  height = size;
371  width = size;
372  if ( width < settings.symbolSize().width() )
373  {
374  widthOffset = ( settings.symbolSize().width() - width ) / 2.0;
375  }
376  if ( height < settings.symbolSize().height() )
377  {
378  heightOffset = ( settings.symbolSize().height() - height ) / 2.0;
379  }
380  }
381 
382  if ( ctx )
383  {
384  double currentXPosition = ctx->point.x();
385  double currentYCoord = ctx->point.y() + ( itemHeight - settings.symbolSize().height() ) / 2;
386  QPainter *p = ctx->painter;
387 
388  //setup painter scaling to dots so that raster symbology is drawn to scale
389  double dotsPerMM = context.scaleFactor();
390 
391  int opacity = 255;
392  if ( QgsVectorLayer *vectorLayer = dynamic_cast<QgsVectorLayer *>( layerNode()->layer() ) )
393  opacity = ( 255 * vectorLayer->opacity() );
394 
395  p->save();
396  p->setRenderHint( QPainter::Antialiasing );
397  p->translate( currentXPosition + widthOffset, currentYCoord + heightOffset );
398  p->scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
399  if ( opacity != 255 && settings.useAdvancedEffects() )
400  {
401  //semi transparent layer, so need to draw symbol to an image (to flatten it first)
402  //create image which is same size as legend rect, in case symbol bleeds outside its alloted space
403  QSize tempImageSize( width * dotsPerMM, height * dotsPerMM );
404  QImage tempImage = QImage( tempImageSize, QImage::Format_ARGB32 );
405  tempImage.fill( Qt::transparent );
406  QPainter imagePainter( &tempImage );
407  context.setPainter( &imagePainter );
408  s->drawPreviewIcon( &imagePainter, tempImageSize, &context );
409  context.setPainter( ctx->painter );
410  //reduce opacity of image
411  imagePainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
412  imagePainter.fillRect( tempImage.rect(), QColor( 0, 0, 0, opacity ) );
413  imagePainter.end();
414  //draw rendered symbol image
415  p->drawImage( 0, 0, tempImage );
416  }
417  else
418  {
419  s->drawPreviewIcon( p, QSize( width * dotsPerMM, height * dotsPerMM ), &context );
420  }
421  p->restore();
422  }
423 
424  return QSizeF( std::max( width + 2 * widthOffset, static_cast< double >( settings.symbolSize().width() ) ),
425  std::max( height + 2 * heightOffset, static_cast< double >( settings.symbolSize().height() ) ) );
426 }
427 
428 
430 {
432  updateLabel();
433 }
434 
435 
437 {
438  if ( mSymbolUsesMapUnits )
439  {
440  mPixmap = QPixmap();
441  emit dataChanged();
442  }
443 }
444 
445 
446 void QgsSymbolLegendNode::updateLabel()
447 {
448  if ( !mLayerNode )
449  return;
450 
451  bool showFeatureCount = mLayerNode->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toBool();
452  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
453 
454  if ( mEmbeddedInParent )
455  {
456  QString layerName = mLayerNode->name();
457  if ( !mLayerNode->customProperty( QStringLiteral( "legend/title-label" ) ).isNull() )
458  layerName = mLayerNode->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
459 
460  mLabel = mUserLabel.isEmpty() ? layerName : mUserLabel;
461  if ( showFeatureCount && vl && vl->featureCount() >= 0 )
462  mLabel += QStringLiteral( " [%1]" ).arg( vl->featureCount() );
463  }
464  else
465  {
466  mLabel = mUserLabel.isEmpty() ? mItem.label() : mUserLabel;
467  if ( showFeatureCount && vl )
468  {
469  qlonglong count = vl->featureCount( mItem.ruleKey() );
470  mLabel += QStringLiteral( " [%1]" ).arg( count != -1 ? QLocale().toString( count ) : tr( "N/A" ) );
471  }
472  }
473 
474  emit dataChanged();
475 }
476 
477 
478 
479 // -------------------------------------------------------------------------
480 
481 
482 QgsSimpleLegendNode::QgsSimpleLegendNode( QgsLayerTreeLayer *nodeLayer, const QString &label, const QIcon &icon, QObject *parent, const QString &key )
483  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
484  , mLabel( label )
485  , mIcon( icon )
486  , mKey( key )
487 {
488 }
489 
490 QVariant QgsSimpleLegendNode::data( int role ) const
491 {
492  if ( role == Qt::DisplayRole || role == Qt::EditRole )
493  return mUserLabel.isEmpty() ? mLabel : mUserLabel;
494  else if ( role == Qt::DecorationRole )
495  return mIcon;
496  else if ( role == RuleKeyRole && !mKey.isEmpty() )
497  return mKey;
498  else
499  return QVariant();
500 }
501 
502 
503 // -------------------------------------------------------------------------
504 
505 QgsImageLegendNode::QgsImageLegendNode( QgsLayerTreeLayer *nodeLayer, const QImage &img, QObject *parent )
506  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
507  , mImage( img )
508 {
509 }
510 
511 QVariant QgsImageLegendNode::data( int role ) const
512 {
513  if ( role == Qt::DecorationRole )
514  {
515  return QPixmap::fromImage( mImage );
516  }
517  else if ( role == Qt::SizeHintRole )
518  {
519  return mImage.size();
520  }
521  return QVariant();
522 }
523 
524 QSizeF QgsImageLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
525 {
526  Q_UNUSED( itemHeight );
527 
528  if ( ctx )
529  {
530  ctx->painter->drawImage( QRectF( ctx->point.x(), ctx->point.y(), settings.wmsLegendSize().width(), settings.wmsLegendSize().height() ),
531  mImage, QRectF( 0, 0, mImage.width(), mImage.height() ) );
532  }
533  return settings.wmsLegendSize();
534 }
535 
536 // -------------------------------------------------------------------------
537 
538 QgsRasterSymbolLegendNode::QgsRasterSymbolLegendNode( QgsLayerTreeLayer *nodeLayer, const QColor &color, const QString &label, QObject *parent )
539  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
540  , mColor( color )
541  , mLabel( label )
542 {
543 }
544 
545 QVariant QgsRasterSymbolLegendNode::data( int role ) const
546 {
547  if ( role == Qt::DecorationRole )
548  {
549  QSize iconSize( 16, 16 ); // TODO: configurable?
550  QPixmap pix( iconSize );
551  pix.fill( mColor );
552  return QIcon( pix );
553  }
554  else if ( role == Qt::DisplayRole || role == Qt::EditRole )
555  return mUserLabel.isEmpty() ? mLabel : mUserLabel;
556  else
557  return QVariant();
558 }
559 
560 
561 QSizeF QgsRasterSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
562 {
563  if ( ctx )
564  {
565  QColor itemColor = mColor;
566  if ( QgsRasterLayer *rasterLayer = dynamic_cast<QgsRasterLayer *>( layerNode()->layer() ) )
567  {
568  if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
569  itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
570  }
571  ctx->painter->setBrush( itemColor );
572 
573  if ( settings.drawRasterStroke() )
574  {
575  QPen pen;
576  pen.setColor( settings.rasterStrokeColor() );
577  pen.setWidthF( settings.rasterStrokeWidth() );
578  pen.setJoinStyle( Qt::MiterJoin );
579  ctx->painter->setPen( pen );
580  }
581  else
582  {
583  ctx->painter->setPen( Qt::NoPen );
584  }
585 
586  ctx->painter->drawRect( QRectF( ctx->point.x(), ctx->point.y() + ( itemHeight - settings.symbolSize().height() ) / 2,
587  settings.symbolSize().width(), settings.symbolSize().height() ) );
588  }
589  return settings.symbolSize();
590 }
591 
592 // -------------------------------------------------------------------------
593 
595  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
596  , mValid( false )
597 {
598 }
599 
600 QImage QgsWmsLegendNode::getLegendGraphic() const
601 {
602  if ( ! mValid && ! mFetcher )
603  {
604  // or maybe in presence of a downloader we should just delete it
605  // and start a new one ?
606 
607  QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( mLayerNode->layer() );
608  const QgsLayerTreeModel *mod = model();
609  if ( ! mod ) return mImage;
610  const QgsMapSettings *ms = mod->legendFilterMapSettings();
611 
612  QgsRasterDataProvider *prov = layer->dataProvider();
613 
614  Q_ASSERT( ! mFetcher );
615  mFetcher.reset( prov->getLegendGraphicFetcher( ms ) );
616  if ( mFetcher )
617  {
618  connect( mFetcher.get(), &QgsImageFetcher::finish, this, &QgsWmsLegendNode::getLegendGraphicFinished );
619  connect( mFetcher.get(), &QgsImageFetcher::error, this, &QgsWmsLegendNode::getLegendGraphicErrored );
620  connect( mFetcher.get(), &QgsImageFetcher::progress, this, &QgsWmsLegendNode::getLegendGraphicProgress );
621  mFetcher->start();
622  } // else QgsDebugMsg("XXX No legend supported?");
623 
624  }
625 
626  return mImage;
627 }
628 
629 QVariant QgsWmsLegendNode::data( int role ) const
630 {
631  //QgsDebugMsg( QString("XXX data called with role %1 -- mImage size is %2x%3").arg(role).arg(mImage.width()).arg(mImage.height()) );
632 
633  if ( role == Qt::DecorationRole )
634  {
635  return QPixmap::fromImage( getLegendGraphic() );
636  }
637  else if ( role == Qt::SizeHintRole )
638  {
639  return getLegendGraphic().size();
640  }
641  return QVariant();
642 }
643 
644 QSizeF QgsWmsLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
645 {
646  Q_UNUSED( itemHeight );
647 
648  if ( ctx )
649  {
650  ctx->painter->drawImage( QRectF( ctx->point, settings.wmsLegendSize() ),
651  mImage,
652  QRectF( QPointF( 0, 0 ), mImage.size() ) );
653  }
654  return settings.wmsLegendSize();
655 }
656 
657 /* private */
658 QImage QgsWmsLegendNode::renderMessage( const QString &msg ) const
659 {
660  const int fontHeight = 10;
661  const int margin = fontHeight / 2;
662  const int nlines = 1;
663 
664  const int w = 512, h = fontHeight * nlines + margin * ( nlines + 1 );
665  QImage image( w, h, QImage::Format_ARGB32_Premultiplied );
666  QPainter painter;
667  painter.begin( &image );
668  painter.setPen( QColor( 255, 0, 0 ) );
669  painter.setFont( QFont( QStringLiteral( "Chicago" ), fontHeight ) );
670  painter.fillRect( 0, 0, w, h, QColor( 255, 255, 255 ) );
671  painter.drawText( 0, margin + fontHeight, msg );
672  //painter.drawText(0,2*(margin+fontHeight),tr("retrying in 5 seconds…"));
673  painter.end();
674 
675  return image;
676 }
677 
678 void QgsWmsLegendNode::getLegendGraphicProgress( qint64 cur, qint64 tot )
679 {
680  QString msg = QStringLiteral( "Downloading... %1/%2" ).arg( cur ).arg( tot );
681  //QgsDebugMsg ( QString("XXX %1").arg(msg) );
682  mImage = renderMessage( msg );
683  emit dataChanged();
684 }
685 
686 void QgsWmsLegendNode::getLegendGraphicErrored( const QString &msg )
687 {
688  if ( ! mFetcher ) return; // must be coming after finish
689 
690  mImage = renderMessage( msg );
691  //QgsDebugMsg( QString("XXX emitting dataChanged after writing an image of %1x%2").arg(mImage.width()).arg(mImage.height()) );
692 
693  emit dataChanged();
694 
695  mFetcher.reset();
696 
697  mValid = true; // we consider it valid anyway
698  // ... but remove validity after 5 seconds
699  //QTimer::singleShot(5000, this, SLOT(invalidateMapBasedData()));
700 }
701 
702 void QgsWmsLegendNode::getLegendGraphicFinished( const QImage &image )
703 {
704  if ( ! mFetcher ) return; // must be coming after error
705 
706  //QgsDebugMsg( QString("XXX legend graphic finished, image is %1x%2").arg(theImage.width()).arg(theImage.height()) );
707  if ( ! image.isNull() )
708  {
709  if ( image != mImage )
710  {
711  mImage = image;
712  //QgsDebugMsg( QString("XXX emitting dataChanged") );
713  emit dataChanged();
714  }
715  mValid = true; // only if not null I guess
716  }
717  mFetcher.reset();
718 }
719 
721 {
722  //QgsDebugMsg( QString("XXX invalidateMapBasedData called") );
723  // TODO: do this only if this extent != prev extent ?
724  mValid = false;
725  emit dataChanged();
726 }
727 
728 // -------------------------------------------------------------------------
729 
731  : QgsLayerTreeModelLegendNode( nodeLayer, parent )
732  , mSettings( new QgsDataDefinedSizeLegend( settings ) )
733 {
734 }
735 
737 {
738  delete mSettings;
739 }
740 
741 QVariant QgsDataDefinedSizeLegendNode::data( int role ) const
742 {
743  if ( role == Qt::DecorationRole )
744  {
745  cacheImage();
746  return QPixmap::fromImage( mImage );
747  }
748  else if ( role == Qt::SizeHintRole )
749  {
750  cacheImage();
751  return mImage.size();
752  }
753  return QVariant();
754 }
755 
757 {
758  // setup temporary render context
759  QgsRenderContext context;
760  context.setScaleFactor( settings.dpi() / 25.4 );
761  context.setRendererScale( settings.mapScale() );
762  context.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * context.scaleFactor() ) ) );
763  context.setForceVectorOutput( true );
764 
765  if ( ctx )
766  {
767  context.setPainter( ctx->painter );
768  ctx->painter->save();
769  ctx->painter->setRenderHint( QPainter::Antialiasing );
770  ctx->painter->translate( ctx->point );
771  ctx->painter->scale( 1 / context.scaleFactor(), 1 / context.scaleFactor() );
772  }
773 
774  QgsDataDefinedSizeLegend ddsLegend( *mSettings );
775  ddsLegend.setFont( settings.style( QgsLegendStyle::SymbolLabel ).font() );
776  ddsLegend.setTextColor( settings.fontColor() );
777 
778  QSize contentSize;
779  int labelXOffset;
780  ddsLegend.drawCollapsedLegend( context, &contentSize, &labelXOffset );
781 
782  if ( ctx )
783  ctx->painter->restore();
784 
785  ItemMetrics im;
786  im.symbolSize = QSizeF( ( contentSize.width() - labelXOffset ) / context.scaleFactor(), contentSize.height() / context.scaleFactor() );
787  im.labelSize = QSizeF( labelXOffset / context.scaleFactor(), contentSize.height() / context.scaleFactor() );
788  return im;
789 }
790 
791 
792 void QgsDataDefinedSizeLegendNode::cacheImage() const
793 {
794  if ( mImage.isNull() )
795  {
796  std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
797  if ( !context )
798  {
799  context.reset( new QgsRenderContext );
800  context->setScaleFactor( 96 / 25.4 );
801  }
802  mImage = mSettings->collapsedLegendImage( *context );
803  }
804 }
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.
static QPixmap symbolPreviewPixmap(QgsSymbol *symbol, QSize size, int padding=0, QgsRenderContext *customContext=nullptr)
Returns a pixmap preview for a color ramp.
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
Return 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
QgsSymbol * symbol() const
Return associated symbol. May be null.
QString ruleKey() const
Return unique identifier of the rule for identification of the item within renderer.
bool useAdvancedEffects() const
QVariant data(int role) const override
Return 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)
Set 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
Return data associated with the item. Must be implemented in derived class.
bool testFlag(Flag f) const
Check whether a flag is enabled.
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 setRendererScale(double scale)
Sets the renderer map scale.
QFont font() const
The font for this style.
virtual QgsImageFetcher * getLegendGraphicFetcher(const QgsMapSettings *mapSettings)
Get an 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
Return pointer to the parent layer node.
QVariant data(int role) const override
Return 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
Return text label.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:251
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.
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:205
QSize minimumIconSize() const
Calculates the minimum icon size to prevent cropping.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:36
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
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...
Qt::ItemFlags flags() const override
Return item flags associated with the item. Default implementation returns Qt::ItemIsEnabled.
void setSymbol(QgsSymbol *s)
Set symbol of the item. Takes ownership of symbol.
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.
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
Return 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)
Draw icon of the symbol that occupyies area given by size using the painter.
Definition: qgssymbol.cpp:465
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()
Return 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.
bool isCheckable() const
Return 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
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.
void setEmbeddedInParent(bool embedded) override
QgsMapLayer * layer() const
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)
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 convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
SymbolType type() const
Definition: qgssymbol.h:113
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
Set 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
Return data associated with the item. Must be implemented in derived class.
void legendMapViewData(double *mapUnitsPerPixel, int *dpi, double *scale) const
Get 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
Return 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 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
Return 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
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
Return 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.
Layer tree node points to a map layer.
Base class for raster data providers.