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