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