QGIS API Documentation 3.99.0-Master (a26b91b364d)
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 context->setFlag( Qgis::RenderContextFlag::RenderLayerTree, true );
508
509 if ( model() && !model()->targetScreenProperties().isEmpty() )
510 {
511 model()->targetScreenProperties().begin()->updateRenderContextForScreen( *context );
512 }
513
514 QgsExpressionContext expContext;
516 context->setExpressionContext( expContext );
517
518 return context.release();
519}
520
521void QgsLayerTreeModelLegendNode::checkAll( bool state )
522{
523 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ) )
524 {
525 if ( !vlayer->renderer() )
526 return;
527
528 const QgsLegendSymbolList symbolList = vlayer->renderer()->legendSymbolItems();
529 for ( const auto &item : symbolList )
530 {
531 vlayer->renderer()->checkLegendSymbolItem( item.ruleKey(), state );
532 }
533
534 emit dataChanged();
535 vlayer->emitStyleChanged();
536 vlayer->triggerRepaint();
537 }
538 else if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
539 {
540 if ( !pclayer->renderer() )
541 return;
542
543 const QStringList ruleKeys = pclayer->renderer()->legendRuleKeys();
544 for ( const QString &rule : ruleKeys )
545 {
546 pclayer->renderer()->checkLegendItem( rule, state );
547 }
548
549 emit dataChanged();
550 pclayer->emitStyleChanged();
551 pclayer->triggerRepaint();
552 }
553}
554
555QVariant QgsSymbolLegendNode::data( int role ) const
556{
557 if ( role == Qt::DisplayRole )
558 {
559 return mLabel;
560 }
561 else if ( role == Qt::EditRole )
562 {
563 return mUserLabel.isEmpty() ? mItem.label() : mUserLabel;
564 }
565 else if ( role == Qt::DecorationRole )
566 {
567 if ( mPixmap.isNull() )
568 {
569 QgsScreenProperties targetScreen = model() && !model()->targetScreenProperties().isEmpty()
571
572 if ( mItem.symbol() )
573 {
574 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
575
576 // unusued width, height variables
577 double width = 0.0;
578 double height = 0.0;
579 const std::unique_ptr<QgsSymbol> symbol( QgsSymbolLayerUtils::restrictedSizeSymbol( mItem.symbol(), MINIMUM_SIZE, MAXIMUM_SIZE, context.get(), width, height ) );
580 mPixmap = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol ? symbol.get() : mItem.symbol(), mIconSize, 0, context.get(), false, nullptr, nullptr, targetScreen );
581
582 if ( !mTextOnSymbolLabel.isEmpty() && context )
583 {
584 QPainter painter( &mPixmap );
585 painter.setRenderHint( QPainter::Antialiasing );
586 context->setPainter( &painter );
587 bool isNullSize = false;
588 const QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( *context, 1.0, &isNullSize ) );
589 if ( !isNullSize )
590 {
591 const qreal yBaselineVCenter = ( mIconSize.height() + fm.ascent() - fm.descent() ) / 2;
592 QgsTextRenderer::drawText( QPointF( mIconSize.width() / 2, yBaselineVCenter ), 0, Qgis::TextHorizontalAlignment::Center,
593 QStringList() << mTextOnSymbolLabel, *context, mTextOnSymbolTextFormat );
594 }
595 }
596 }
597 else
598 {
599 mPixmap = QPixmap( mIconSize * targetScreen.devicePixelRatio() );
600 mPixmap.fill( Qt::transparent );
601 }
602 }
603 return mPixmap;
604 }
605 else if ( role == Qt::CheckStateRole )
606 {
607 if ( !mItem.isCheckable() )
608 return QVariant();
609
610 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ) )
611 {
612 if ( !vlayer->renderer() )
613 return QVariant();
614
615 return vlayer->renderer()->legendSymbolItemChecked( mItem.ruleKey() ) ? Qt::Checked : Qt::Unchecked;
616 }
617 }
618 else if ( role == static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) )
619 {
620 return mItem.ruleKey();
621 }
622 else if ( role == static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::ParentRuleKey ) )
623 {
624 return mItem.parentRuleKey();
625 }
626 else if ( role == static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::NodeType ) )
627 {
629 }
630
631 return QVariant();
632}
633
634bool QgsSymbolLegendNode::setData( const QVariant &value, int role )
635{
636 if ( role != Qt::CheckStateRole )
637 return false;
638
639 if ( !mItem.isCheckable() )
640 return false;
641
642 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
643 if ( !vlayer || !vlayer->renderer() )
644 return false;
645
646 vlayer->renderer()->checkLegendSymbolItem( mItem.ruleKey(), value == Qt::Checked );
647
648 if ( QgsProject *project = vlayer->project() )
649 project->setDirty( true );
650
651 emit dataChanged();
652 vlayer->emitStyleChanged();
653
654 vlayer->triggerRepaint();
655
656 return true;
657}
658
659QSizeF QgsSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
660{
661 QgsSymbol *s = mCustomSymbol ? mCustomSymbol.get() : mItem.symbol();
662 if ( !s )
663 {
664 return QSizeF();
665 }
666
667 // setup temporary render context
668 QgsRenderContext *context = nullptr;
669 std::unique_ptr< QgsRenderContext > tempRenderContext;
671 if ( ctx && ctx->context )
672 context = ctx->context;
673 else
674 {
675 tempRenderContext = std::make_unique< QgsRenderContext >();
676 // QGIS 4.0 - make ItemContext compulsory, so we don't have to construct temporary render contexts here
678 tempRenderContext->setScaleFactor( settings.dpi() / 25.4 );
679 tempRenderContext->setRendererScale( settings.mapScale() );
680 tempRenderContext->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
681 tempRenderContext->setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * tempRenderContext->scaleFactor() ) ) );
683 tempRenderContext->setRasterizedRenderingPolicy( Qgis::RasterizedRenderingPolicy::PreferVector );
684 tempRenderContext->setPainter( ctx ? ctx->painter : nullptr );
685
686 // setup a minimal expression context
687 QgsExpressionContext expContext;
689 tempRenderContext->setExpressionContext( expContext );
690 context = tempRenderContext.get();
691 }
692
693 //Consider symbol size for point markers
694 const bool hasFixedWidth = ctx && ctx->patchSize.width() > 0;
695 const bool hasFixedHeight = ctx && ctx->patchSize.height() > 0;
696 const double desiredHeight = hasFixedHeight ? ctx->patchSize.height() : settings.symbolSize().height();
697 const double desiredWidth = hasFixedWidth ? ctx->patchSize.width() : settings.symbolSize().width();
698 double height = desiredHeight;
699 double width = desiredWidth;
700
701 //Center small marker symbols
702 double widthOffset = 0;
703 double heightOffset = 0;
704
705 const double maxSymbolSize = settings.maximumSymbolSize();
706 const double minSymbolSize = settings.minimumSymbolSize();
707
708 if ( QgsMarkerSymbol *markerSymbol = dynamic_cast<QgsMarkerSymbol *>( s ) )
709 {
710 const double size = markerSymbol->size( *context ) / context->scaleFactor();
711 if ( size > 0 )
712 {
713 if ( !hasFixedHeight )
714 height = size;
715 if ( !hasFixedWidth )
716 width = size;
717 }
718 }
719
720 bool restrictedSizeSymbolOK;
721 double restrictedSymbolWidth = width;
722 double restrictedSymbolHeight = height;
723 const std::unique_ptr<QgsSymbol> minMaxSizeSymbol( QgsSymbolLayerUtils::restrictedSizeSymbol( s, minSymbolSize, maxSymbolSize, context, restrictedSymbolWidth, restrictedSymbolHeight, &restrictedSizeSymbolOK ) );
724 if ( minMaxSizeSymbol )
725 {
726 s = minMaxSizeSymbol.get();
727 if ( !hasFixedHeight )
728 height = restrictedSymbolHeight;
729 if ( !hasFixedWidth )
730 width = restrictedSymbolWidth;
731 }
732
733 if ( s->type() == Qgis::SymbolType::Marker )
734 {
735 if ( width < desiredWidth )
736 {
737 widthOffset = ( desiredWidth - width ) / 2.0;
738 }
739 if ( height < desiredHeight )
740 {
741 heightOffset = ( desiredHeight - height ) / 2.0;
742 }
743 }
744 if ( ctx && ctx->painter )
745 {
746 const double currentYCoord = ctx->top + ( itemHeight - desiredHeight ) / 2;
747 QPainter *p = ctx->painter;
748
749 //setup painter scaling to dots so that raster symbology is drawn to scale
750 const double dotsPerMM = context->scaleFactor();
751
752 int opacity = 255;
753 if ( QgsMapLayer *layer = layerNode()->layer() )
754 opacity = static_cast<int >( std::round( 255 * layer->opacity() ) );
755
756 const QgsScopedQPainterState painterState( p );
757 context->setPainterFlagsUsingContext( p );
758
759 switch ( settings.symbolAlignment() )
760 {
761 case Qt::AlignLeft:
762 default:
763 p->translate( ctx->columnLeft + widthOffset, currentYCoord + heightOffset );
764 break;
765 case Qt::AlignRight:
766 p->translate( ctx->columnRight - widthOffset - width, currentYCoord + heightOffset );
767 break;
768 }
769
770 p->scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
772 // QGIS 4.0 -- ctx->context will be mandatory
775
776 if ( opacity != 255 && !forceVector )
777 {
778 // if this is a semi transparent layer, we need to draw symbol to an image (to flatten it first)
779
780 const int maxBleed = static_cast< int >( std::ceil( QgsSymbolLayerUtils::estimateMaxSymbolBleed( s, *context ) ) );
781
782 // create image which is same size as legend rect, in case symbol bleeds outside its allotted space
783 const QSize symbolSize( static_cast< int >( std::round( width * dotsPerMM ) ), static_cast<int >( std::round( height * dotsPerMM ) ) );
784 const QSize tempImageSize( symbolSize.width() + maxBleed * 2, symbolSize.height() + maxBleed * 2 );
785 QImage tempImage = QImage( tempImageSize, QImage::Format_ARGB32 );
786 tempImage.fill( Qt::transparent );
787 QPainter imagePainter( &tempImage );
788 context->setPainterFlagsUsingContext( &imagePainter );
789
790 context->setPainter( &imagePainter );
791 imagePainter.translate( maxBleed, maxBleed );
792 s->drawPreviewIcon( &imagePainter, symbolSize, context, false, nullptr, &patchShape, ctx->screenProperties );
793 imagePainter.translate( -maxBleed, -maxBleed );
794 context->setPainter( ctx->painter );
795 //reduce opacity of image
796 imagePainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
797 imagePainter.fillRect( tempImage.rect(), QColor( 0, 0, 0, opacity ) );
798 imagePainter.end();
799 //draw rendered symbol image
800 p->drawImage( -maxBleed, -maxBleed, tempImage );
801 }
802 else if ( !restrictedSizeSymbolOK )
803 {
804 // if there is no restricted symbol size (because of geometry generator mainly) we need to ensure
805 // that there is no drawing outside the given size
806 const int maxBleed = static_cast< int >( std::ceil( QgsSymbolLayerUtils::estimateMaxSymbolBleed( s, *context ) ) );
807 const QSize symbolSize( static_cast< int >( std::round( width * dotsPerMM ) ), static_cast<int >( std::round( height * dotsPerMM ) ) );
808 const QSize maxSize( symbolSize.width() + maxBleed * 2, symbolSize.height() + maxBleed * 2 );
809 p->save();
810 p->setClipRect( -maxBleed, -maxBleed, maxSize.width(), maxSize.height(), Qt::IntersectClip );
811 s->drawPreviewIcon( p, symbolSize, context, false, nullptr, &patchShape, ctx->screenProperties );
812 p->restore();
813 }
814 else
815 {
816 s->drawPreviewIcon( p, QSize( static_cast< int >( std::round( width * dotsPerMM ) ), static_cast< int >( std::round( height * dotsPerMM ) ) ), context, false, nullptr, &patchShape, ctx->screenProperties );
817 }
818
819 if ( !mTextOnSymbolLabel.isEmpty() )
820 {
821 bool isNullSize = false;
822 const QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( *context, 1.0, &isNullSize ) );
823 if ( !isNullSize )
824 {
825 const qreal yBaselineVCenter = ( height * dotsPerMM + fm.ascent() - fm.descent() ) / 2;
826 QgsTextRenderer::drawText( QPointF( width * dotsPerMM / 2, yBaselineVCenter ), 0, Qgis::TextHorizontalAlignment::Center,
827 QStringList() << mTextOnSymbolLabel, *context, mTextOnSymbolTextFormat );
828 }
829 }
830 }
831
832 return QSizeF( std::max( width + 2 * widthOffset, static_cast< double >( desiredWidth ) ),
833 std::max( height + 2 * heightOffset, static_cast< double >( desiredHeight ) ) );
834}
835
836QJsonObject QgsSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const
837{
838 QJsonObject json;
839 if ( mItem.scaleMaxDenom() > 0 )
840 {
841 json[ QStringLiteral( "scaleMaxDenom" ) ] = mItem.scaleMaxDenom();
842 }
843 if ( mItem.scaleMinDenom() > 0 )
844 {
845 json[ QStringLiteral( "scaleMinDenom" ) ] = mItem.scaleMinDenom();
846 }
847
848 const QgsSymbol *s = mCustomSymbol ? mCustomSymbol.get() : mItem.symbol();
849 if ( !s )
850 {
851 return json;
852 }
853
854
856 // QGIS 4.0 - use render context directly here, and note in the dox that the context must be correctly setup
858 ctx.setScaleFactor( settings.dpi() / 25.4 );
859 ctx.setRendererScale( settings.mapScale() );
860 ctx.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * ctx.scaleFactor() ) ) );
861 ctx.setForceVectorOutput( true );
864
866
867 // ensure that a minimal expression context is available
868 QgsExpressionContext expContext = context.expressionContext();
870 ctx.setExpressionContext( expContext );
871
872 const QPixmap pix = QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), minimumIconSize(), 0, &ctx );
873 QImage img( pix.toImage().convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
874
875 int opacity = 255;
876 if ( QgsMapLayer *layer = layerNode()->layer() )
877 opacity = ( 255 * layer->opacity() );
878
879 if ( opacity != 255 )
880 {
881 QPainter painter;
882 painter.begin( &img );
883 painter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
884 painter.fillRect( pix.rect(), QColor( 0, 0, 0, opacity ) );
885 painter.end();
886 }
887
888 QByteArray byteArray;
889 QBuffer buffer( &byteArray );
890 img.save( &buffer, "PNG" );
891 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
892
893 json[ QStringLiteral( "icon" ) ] = base64;
894 return json;
895}
896
898{
900 updateLabel();
901}
902
903
905{
906 if ( mSymbolUsesMapUnits )
907 {
908 mPixmap = QPixmap();
909 emit dataChanged();
910 }
911}
912
914{
915 if ( mIconSize == sz )
916 return;
917
918 mIconSize = sz;
919 mPixmap = QPixmap();
920 emit dataChanged();
921}
922
923void QgsSymbolLegendNode::updateLabel()
924{
925 if ( !mLayerNode )
926 return;
927
928 const bool showFeatureCount = mLayerNode->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toBool();
929 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
930 if ( !mLayerNode->labelExpression().isEmpty() )
931 mLabel = "[%" + mLayerNode->labelExpression() + "%]";
932 else
933 mLabel = symbolLabel();
934
935 if ( showFeatureCount && vl )
936 {
937 const bool estimatedCount = vl->dataProvider() ? QgsDataSourceUri( vl->dataProvider()->dataSourceUri() ).useEstimatedMetadata() : false;
938 const qlonglong count = mEmbeddedInParent ? vl->featureCount() : vl->featureCount( mItem.ruleKey() ) ;
939
940 // if you modify this line, please update QgsLayerTreeModel::data (DisplayRole)
941 mLabel += QStringLiteral( " [%1%2]" ).arg(
942 estimatedCount ? QStringLiteral( "≈" ) : QString(),
943 count != -1 ? QLocale().toString( count ) : tr( "N/A" ) );
944 }
945
946 emit dataChanged();
947}
948
949QString QgsSymbolLegendNode::evaluateLabel( const QgsExpressionContext &context, const QString &label )
950{
951 if ( !mLayerNode )
952 return QString();
953
954 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
955
956 if ( vl )
957 {
958 QgsExpressionContext contextCopy = QgsExpressionContext( context );
960 contextCopy.appendScope( symbolScope );
961 contextCopy.appendScope( vl->createExpressionContextScope() );
962
963 if ( label.isEmpty() )
964 {
965 const QString symLabel = symbolLabel();
966 if ( ! mLayerNode->labelExpression().isEmpty() )
967 mLabel = QgsExpression::replaceExpressionText( "[%" + mLayerNode->labelExpression() + "%]", &contextCopy );
968 else if ( symLabel.contains( "[%" ) )
969 mLabel = QgsExpression::replaceExpressionText( symLabel, &contextCopy );
970 return mLabel;
971 }
972 else
973 {
974 QString eLabel;
975 if ( ! mLayerNode->labelExpression().isEmpty() )
976 eLabel = QgsExpression::replaceExpressionText( label + "[%" + mLayerNode->labelExpression() + "%]", &contextCopy );
977 else if ( label.contains( "[%" ) )
978 eLabel = QgsExpression::replaceExpressionText( label, &contextCopy );
979 return eLabel;
980 }
981 }
982 return mLabel;
983}
984
986{
987 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
988
989 QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Symbol scope" ) );
990 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_label" ), symbolLabel().remove( "[%" ).remove( "%]" ), true ) );
991 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_id" ), mItem.ruleKey(), true ) );
992 if ( vl )
993 {
994 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_count" ), QVariant::fromValue( vl->featureCount( mItem.ruleKey() ) ), true ) );
995 }
996 return scope;
997}
998
999// -------------------------------------------------------------------------
1000
1001
1002QgsSimpleLegendNode::QgsSimpleLegendNode( QgsLayerTreeLayer *nodeLayer, const QString &label, const QIcon &icon, QObject *parent, const QString &key )
1003 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1004 , mLabel( label )
1005 , mIcon( icon )
1006 , mKey( key )
1007{
1008}
1009
1010QVariant QgsSimpleLegendNode::data( int role ) const
1011{
1012 if ( role == Qt::DisplayRole || role == Qt::EditRole )
1013 return mUserLabel.isEmpty() ? mLabel : mUserLabel;
1014 else if ( role == Qt::DecorationRole )
1015 return mIcon;
1016 else if ( role == static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) && !mKey.isEmpty() )
1017 return mKey;
1018 else if ( role == static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::NodeType ) )
1020 else
1021 return QVariant();
1022}
1023
1024
1025// -------------------------------------------------------------------------
1026
1027QgsImageLegendNode::QgsImageLegendNode( QgsLayerTreeLayer *nodeLayer, const QImage &img, QObject *parent )
1028 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1029 , mImage( img )
1030{
1031}
1032
1033QVariant QgsImageLegendNode::data( int role ) const
1034{
1035 if ( role == Qt::DecorationRole )
1036 {
1037 return QPixmap::fromImage( mImage );
1038 }
1039 else if ( role == Qt::SizeHintRole )
1040 {
1041 return mImage.size();
1042 }
1043 else if ( role == static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::NodeType ) )
1044 {
1046 }
1047 return QVariant();
1048}
1049
1050QSizeF QgsImageLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1051{
1052 Q_UNUSED( itemHeight )
1053
1054 if ( ctx && ctx->painter && ctx->context )
1055 {
1056 const QgsScopedRenderContextScaleToPixels scopedScaleToPixels( *( ctx->context ) );
1057 const double scaleFactor = ctx->context->scaleFactor();
1058 const double imgWidth = settings.wmsLegendSize().width() * scaleFactor;
1059 const double imgHeight = settings.wmsLegendSize().height() * scaleFactor;
1060
1061 const QImage scaledImg = mImage.scaled( QSizeF( imgWidth, imgHeight ).toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
1062 switch ( settings.symbolAlignment() )
1063 {
1064 case Qt::AlignLeft:
1065 default:
1066 ctx->painter->drawImage( QPointF( ctx->columnLeft * scaleFactor, ctx->top * scaleFactor ), scaledImg );
1067 break;
1068
1069 case Qt::AlignRight:
1070 ctx->painter->drawImage( QPointF( ctx->columnRight * scaleFactor - imgWidth, ctx->top * scaleFactor ), scaledImg );
1071 break;
1072 }
1073 }
1074 return settings.wmsLegendSize();
1075}
1076
1078{
1079 QByteArray byteArray;
1080 QBuffer buffer( &byteArray );
1081 mImage.save( &buffer, "PNG" );
1082 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1083
1084 QJsonObject json;
1085 json[ QStringLiteral( "icon" ) ] = base64;
1086 return json;
1087}
1088
1089// -------------------------------------------------------------------------
1090
1091QgsRasterSymbolLegendNode::QgsRasterSymbolLegendNode( QgsLayerTreeLayer *nodeLayer, const QColor &color, const QString &label, QObject *parent, bool isCheckable, const QString &ruleKey, const QString &parentRuleKey )
1092 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1093 , mColor( color )
1094 , mLabel( label )
1095 , mCheckable( isCheckable )
1096 , mRuleKey( ruleKey )
1097 , mParentRuleKey( parentRuleKey )
1098{
1099}
1100
1102{
1103 if ( mCheckable )
1104 return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable;
1105 else
1106 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
1107}
1108
1109QVariant QgsRasterSymbolLegendNode::data( int role ) const
1110{
1111 switch ( role )
1112 {
1113 case Qt::DecorationRole:
1114 {
1115 const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 ); // TODO: configurable?
1116 QPixmap pix( iconSize, iconSize );
1117 pix.fill( mColor );
1118 return QIcon( pix );
1119 }
1120
1121 case Qt::DisplayRole:
1122 case Qt::EditRole:
1123 return mUserLabel.isEmpty() ? mLabel : mUserLabel;
1124
1125 case static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::NodeType ):
1127
1128 case static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ):
1129 return mRuleKey;
1130
1132 return mParentRuleKey;
1133
1134 case Qt::CheckStateRole:
1135 {
1136 if ( !mCheckable )
1137 return QVariant();
1138
1139 if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
1140 {
1141 if ( !pclayer->renderer() )
1142 return QVariant();
1143
1144 return pclayer->renderer()->legendItemChecked( mRuleKey ) ? Qt::Checked : Qt::Unchecked;
1145 }
1146
1147 return QVariant();
1148 }
1149
1150 default:
1151 return QVariant();
1152 }
1153}
1154
1155bool QgsRasterSymbolLegendNode::setData( const QVariant &value, int role )
1156{
1157 if ( role != Qt::CheckStateRole )
1158 return false;
1159
1160 if ( !mCheckable )
1161 return false;
1162
1163 if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
1164 {
1165 if ( !pclayer->renderer() )
1166 return false;
1167
1168 pclayer->renderer()->checkLegendItem( mRuleKey, value == Qt::Checked );
1169
1170 emit dataChanged();
1171 pclayer->emitStyleChanged();
1172
1173 pclayer->triggerRepaint();
1174 if ( pclayer->sync3DRendererTo2DRenderer() )
1175 pclayer->convertRenderer3DFromRenderer2D();
1176
1177 return true;
1178 }
1179 else
1180 {
1181 return false;
1182 }
1183}
1184
1185
1186QSizeF QgsRasterSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1187{
1188 QSizeF size = settings.symbolSize();
1189 double offsetX = 0;
1190 if ( ctx )
1191 {
1192 if ( ctx->patchSize.width() > 0 )
1193 {
1194 if ( ctx->patchSize.width() < size.width() )
1195 offsetX = ( size.width() - ctx->patchSize.width() ) / 2.0;
1196 size.setWidth( ctx->patchSize.width() );
1197 }
1198 if ( ctx->patchSize.height() > 0 )
1199 {
1200 size.setHeight( ctx->patchSize.height() );
1201 }
1202 }
1203
1204 if ( ctx && ctx->painter )
1205 {
1206 QColor itemColor = mColor;
1207 if ( QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layerNode()->layer() ) )
1208 {
1209 if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
1210 itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
1211 }
1212 ctx->painter->setBrush( itemColor );
1213
1214 if ( settings.drawRasterStroke() )
1215 {
1216 QPen pen;
1217 pen.setColor( settings.rasterStrokeColor() );
1218 pen.setWidthF( settings.rasterStrokeWidth() );
1219 pen.setJoinStyle( Qt::MiterJoin );
1220 ctx->painter->setPen( pen );
1221 }
1222 else
1223 {
1224 ctx->painter->setPen( Qt::NoPen );
1225 }
1226
1227 switch ( settings.symbolAlignment() )
1228 {
1229 case Qt::AlignLeft:
1230 default:
1231 ctx->painter->drawRect( QRectF( ctx->columnLeft + offsetX, ctx->top + ( itemHeight - size.height() ) / 2,
1232 size.width(), size.height() ) );
1233 break;
1234
1235 case Qt::AlignRight:
1236 ctx->painter->drawRect( QRectF( ctx->columnRight - size.width() - offsetX, ctx->top + ( itemHeight - size.height() ) / 2,
1237 size.width(), size.height() ) );
1238 break;
1239 }
1240 }
1241 return size;
1242}
1243
1245{
1246 QImage img = QImage( settings.symbolSize().toSize(), QImage::Format_ARGB32 );
1247 img.fill( Qt::transparent );
1248
1249 QPainter painter( &img );
1250 painter.setRenderHint( QPainter::Antialiasing );
1251
1252 QColor itemColor = mColor;
1253 if ( QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layerNode()->layer() ) )
1254 {
1255 if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
1256 itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
1257 }
1258 painter.setBrush( itemColor );
1259
1260 if ( settings.drawRasterStroke() )
1261 {
1262 QPen pen;
1263 pen.setColor( settings.rasterStrokeColor() );
1264 pen.setWidthF( settings.rasterStrokeWidth() );
1265 pen.setJoinStyle( Qt::MiterJoin );
1266 painter.setPen( pen );
1267 }
1268 else
1269 {
1270 painter.setPen( Qt::NoPen );
1271 }
1272
1273 painter.drawRect( QRectF( 0, 0, settings.symbolSize().width(), settings.symbolSize().height() ) );
1274
1275 QByteArray byteArray;
1276 QBuffer buffer( &byteArray );
1277 img.save( &buffer, "PNG" );
1278 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1279
1280 QJsonObject json;
1281 json[ QStringLiteral( "icon" ) ] = base64;
1282 return json;
1283}
1284
1285// -------------------------------------------------------------------------
1286
1288 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1289 , mValid( false )
1290{
1291}
1292
1294
1295QImage QgsWmsLegendNode::getLegendGraphic( bool synchronous ) const
1296{
1297 if ( ! mValid && ! mFetcher )
1298 {
1299 // or maybe in presence of a downloader we should just delete it
1300 // and start a new one ?
1301
1302 QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( mLayerNode->layer() );
1303
1304 if ( layer && layer->isValid() )
1305 {
1306 const QgsLayerTreeModel *mod = model();
1307 if ( ! mod )
1308 return mImage;
1309 const QgsMapSettings *ms = mod->legendFilterMapSettings();
1310
1311 QgsRasterDataProvider *prov = layer->dataProvider();
1312 if ( ! prov )
1313 return mImage;
1314
1315 Q_ASSERT( ! mFetcher );
1316 mFetcher.reset( prov->getLegendGraphicFetcher( ms ) );
1317 if ( mFetcher )
1318 {
1319 connect( mFetcher.get(), &QgsImageFetcher::finish, this, &QgsWmsLegendNode::getLegendGraphicFinished );
1320 connect( mFetcher.get(), &QgsImageFetcher::error, this, &QgsWmsLegendNode::getLegendGraphicErrored );
1321 connect( mFetcher.get(), &QgsImageFetcher::progress, this, &QgsWmsLegendNode::getLegendGraphicProgress );
1322 mFetcher->start();
1323 if ( synchronous )
1324 {
1325 QEventLoop loop;
1326 // The slots getLegendGraphicFinished and getLegendGraphicErrored will destroy the fetcher
1327 connect( mFetcher.get(), &QObject::destroyed, &loop, &QEventLoop::quit );
1328 loop.exec();
1329 }
1330 }
1331 }
1332 else
1333 {
1334 QgsDebugError( QStringLiteral( "Failed to download legend graphics: layer is not valid." ) );
1335 }
1336 }
1337
1338 return mImage;
1339}
1340
1341QVariant QgsWmsLegendNode::data( int role ) const
1342{
1343 if ( role == Qt::DecorationRole )
1344 {
1345 return QPixmap::fromImage( getLegendGraphic() );
1346 }
1347 else if ( role == Qt::SizeHintRole )
1348 {
1349 return getLegendGraphic().size();
1350 }
1351 else if ( role == static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::NodeType ) )
1352 {
1354 }
1355 return QVariant();
1356}
1357
1358QSizeF QgsWmsLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1359{
1360 Q_UNUSED( itemHeight )
1361
1362 const QImage image = getLegendGraphic( settings.synchronousLegendRequests() );
1363
1364 double px2mm = 1000. / image.dotsPerMeterX();
1365 double mmWidth = image.width() * px2mm;
1366 double mmHeight = image.height() * px2mm;
1367
1368 QSize targetSize = QSize( mmWidth, mmHeight );
1369 if ( settings.wmsLegendSize().width() < mmWidth )
1370 {
1371 double targetHeight = mmHeight * settings.wmsLegendSize().width() / mmWidth;
1372 targetSize = QSize( settings.wmsLegendSize().width(), targetHeight );
1373 }
1374 else if ( settings.wmsLegendSize().height() < mmHeight )
1375 {
1376 double targetWidth = mmWidth * settings.wmsLegendSize().height() / mmHeight;
1377 targetSize = QSize( targetWidth, settings.wmsLegendSize().height() );
1378 }
1379
1380 if ( ctx && ctx->painter )
1381 {
1382 QImage smoothImage = image.scaled( targetSize / px2mm, Qt::KeepAspectRatio, Qt::SmoothTransformation );
1383
1384 switch ( settings.symbolAlignment() )
1385 {
1386 case Qt::AlignLeft:
1387 default:
1388 ctx->painter->drawImage( QRectF( ctx->columnLeft,
1389 ctx->top,
1390 targetSize.width(),
1391 targetSize.height() ),
1392 smoothImage,
1393 QRectF( QPointF( 0, 0 ), smoothImage.size() ) );
1394 break;
1395
1396 case Qt::AlignRight:
1397 ctx->painter->drawImage( QRectF( ctx->columnRight - settings.wmsLegendSize().width(),
1398 ctx->top,
1399 targetSize.width(),
1400 targetSize.height() ),
1401 smoothImage,
1402 QRectF( QPointF( 0, 0 ), smoothImage.size() ) );
1403 break;
1404 }
1405 }
1406 return targetSize;
1407}
1408
1410{
1411 QByteArray byteArray;
1412 QBuffer buffer( &byteArray );
1413 mImage.save( &buffer, "PNG" );
1414 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1415
1416 QJsonObject json;
1417 json[ QStringLiteral( "icon" ) ] = base64;
1418 return json;
1419}
1420
1421QImage QgsWmsLegendNode::renderMessage( const QString &msg ) const
1422{
1423 const int fontHeight = 10;
1424 const int margin = fontHeight / 2;
1425 const int nlines = 1;
1426
1427 const int w = 512, h = fontHeight * nlines + margin * ( nlines + 1 );
1428 QImage image( w, h, QImage::Format_ARGB32_Premultiplied );
1429 QPainter painter;
1430 painter.begin( &image );
1431 painter.setPen( QColor( 255, 0, 0 ) );
1432 painter.setFont( QFont( QStringLiteral( "Chicago" ), fontHeight ) );
1433 painter.fillRect( 0, 0, w, h, QColor( 255, 255, 255 ) );
1434 painter.drawText( 0, margin + fontHeight, msg );
1435 //painter.drawText(0,2*(margin+fontHeight),tr("retrying in 5 seconds…"));
1436 painter.end();
1437
1438 return image;
1439}
1440
1441void QgsWmsLegendNode::getLegendGraphicProgress( qint64 cur, qint64 tot )
1442{
1443 const QString msg = tot > 0 ? tr( "Downloading: %1% (%2)" ).arg( static_cast< int >( std::round( 100 * cur / tot ) ) ).arg( QgsFileUtils::representFileSize( tot ) )
1444 : tr( "Downloading: %1" ).arg( QgsFileUtils::representFileSize( cur ) );
1445 mImage = renderMessage( msg );
1446 emit dataChanged();
1447}
1448
1449void QgsWmsLegendNode::getLegendGraphicErrored( const QString & )
1450{
1451 if ( ! mFetcher )
1452 return; // must be coming after finish
1453
1454 mImage = QImage();
1455 emit dataChanged();
1456
1457 mFetcher.reset();
1458
1459 mValid = true; // we consider it valid anyway
1460}
1461
1462void QgsWmsLegendNode::getLegendGraphicFinished( const QImage &image )
1463{
1464 if ( ! mFetcher )
1465 return; // must be coming after error
1466
1467 if ( ! image.isNull() )
1468 {
1469 if ( image != mImage )
1470 {
1471 mImage = image;
1472 setUserPatchSize( mImage.size() );
1473 emit dataChanged();
1474 }
1475 mValid = true; // only if not null I guess
1476 }
1477 mFetcher.reset();
1478}
1479
1481{
1482 // TODO: do this only if this extent != prev extent ?
1483 mValid = false;
1484 emit dataChanged();
1485}
1486
1488{
1489 return getLegendGraphic( true );
1490}
1491
1492// -------------------------------------------------------------------------
1493
1495 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1496 , mSettings( std::make_unique<QgsDataDefinedSizeLegend>( settings ) )
1497{
1498}
1499
1504
1505QVariant QgsDataDefinedSizeLegendNode::data( int role ) const
1506{
1507 if ( role == Qt::DecorationRole )
1508 {
1509 cacheImage();
1510 return QPixmap::fromImage( mImage );
1511 }
1512 else if ( role == Qt::SizeHintRole )
1513 {
1514 cacheImage();
1515 return mImage.size();
1516 }
1517 else if ( role == static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::NodeType ) )
1518 {
1520 }
1521 return QVariant();
1522}
1523
1525{
1526 // setup temporary render context if none specified
1527 QgsRenderContext *context = nullptr;
1528 std::unique_ptr< QgsRenderContext > tempRenderContext;
1529 if ( ctx && ctx->context )
1530 context = ctx->context;
1531 else
1532 {
1533 tempRenderContext = std::make_unique< QgsRenderContext >();
1534 // QGIS 4.0 - make ItemContext compulsory, so we don't have to construct temporary render contexts here
1536 tempRenderContext->setScaleFactor( settings.dpi() / 25.4 );
1537 tempRenderContext->setRendererScale( settings.mapScale() );
1538 tempRenderContext->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
1539 tempRenderContext->setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * tempRenderContext->scaleFactor() ) ) );
1540 tempRenderContext->setForceVectorOutput( true );
1541 tempRenderContext->setPainter( ctx ? ctx->painter : nullptr );
1542 tempRenderContext->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
1544
1545 // setup a minimal expression context
1546 QgsExpressionContext expContext;
1548 tempRenderContext->setExpressionContext( expContext );
1549 context = tempRenderContext.get();
1550 }
1551
1552 if ( context->painter() )
1553 {
1554 context->painter()->save();
1555 context->painter()->translate( ctx->columnLeft, ctx->top );
1556
1557 // scale to pixels
1558 context->painter()->scale( 1 / context->scaleFactor(), 1 / context->scaleFactor() );
1559 }
1560
1561 QgsDataDefinedSizeLegend ddsLegend( *mSettings );
1564
1565 QSizeF contentSize;
1566 double labelXOffset;
1567 ddsLegend.drawCollapsedLegend( *context, &contentSize, &labelXOffset );
1568
1569 if ( context->painter() )
1570 context->painter()->restore();
1571
1572 ItemMetrics im;
1573 im.symbolSize = QSizeF( ( contentSize.width() - labelXOffset ) / context->scaleFactor(), contentSize.height() / context->scaleFactor() );
1574 im.labelSize = QSizeF( labelXOffset / context->scaleFactor(), contentSize.height() / context->scaleFactor() );
1575 return im;
1576}
1577
1578
1579void QgsDataDefinedSizeLegendNode::cacheImage() const
1580{
1581 if ( mImage.isNull() )
1582 {
1583 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1584 if ( !context )
1585 {
1586 context.reset( new QgsRenderContext );
1587 Q_ASSERT( context ); // to make cppcheck happy
1588 context->setScaleFactor( 96 / 25.4 );
1589 }
1590 mImage = mSettings->collapsedLegendImage( *context );
1591 }
1592}
1593
1594QgsVectorLabelLegendNode::QgsVectorLabelLegendNode( QgsLayerTreeLayer *nodeLayer, const QgsPalLayerSettings &labelSettings, QObject *parent ): QgsLayerTreeModelLegendNode( nodeLayer, parent ), mLabelSettings( labelSettings )
1595{
1596}
1597
1601
1602QVariant QgsVectorLabelLegendNode::data( int role ) const
1603{
1604 if ( role == Qt::DisplayRole )
1605 {
1606 return mUserLabel;
1607 }
1608 if ( role == Qt::DecorationRole )
1609 {
1610 const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 );
1611 return QgsPalLayerSettings::labelSettingsPreviewPixmap( mLabelSettings, QSize( iconSize, iconSize ), mLabelSettings.legendString() );
1612 }
1613 return QVariant();
1614}
1615
1616QSizeF QgsVectorLabelLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1617{
1618 Q_UNUSED( itemHeight );
1619 if ( !ctx )
1620 {
1621 return QSizeF( 0, 0 );
1622 }
1623
1624 const QgsRenderContext *renderContext = ctx->context;
1625 if ( renderContext )
1626 {
1627 return drawSymbol( settings, *renderContext, ctx->columnLeft, ctx->top );
1628 }
1629
1630 return QSizeF( 0, 0 );
1631}
1632
1633QSizeF QgsVectorLabelLegendNode::drawSymbol( const QgsLegendSettings &settings, const QgsRenderContext &renderContext, double xOffset, double yOffset ) const
1634{
1635 const QStringList textLines( mLabelSettings.legendString() );
1636 const QgsTextFormat textFormat = mLabelSettings.format();
1637 QgsRenderContext ctx( renderContext );
1638 double textWidth, textHeight;
1639 textWidthHeight( textWidth, textHeight, ctx, textFormat, textLines );
1640 textWidth /= renderContext.scaleFactor();
1641 textHeight /= renderContext.scaleFactor();
1642 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 ) );
1643
1644 const QgsScopedRenderContextScaleToPixels scopedScaleToPixels( ctx );
1645 QgsTextRenderer::drawText( textPos, 0.0, Qgis::TextHorizontalAlignment::Left, textLines, ctx, textFormat );
1646
1647 const double symbolWidth = std::max( textWidth, settings.symbolSize().width() );
1648 const double symbolHeight = std::max( textHeight, settings.symbolSize().height() );
1649 return QSizeF( symbolWidth, symbolHeight );
1650}
1651
1652QJsonObject QgsVectorLabelLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const
1653{
1654 Q_UNUSED( settings );
1655
1656 const double mmToPixel = 96.0 / 25.4; //settings.dpi() is deprecated
1657
1658 const QStringList textLines( mLabelSettings.legendString() );
1659 const QgsTextFormat textFormat = mLabelSettings.format();
1660 QgsRenderContext ctx( context );
1661 ctx.setScaleFactor( mmToPixel );
1662
1663 double textWidth, textHeight;
1664 textWidthHeight( textWidth, textHeight, ctx, textFormat, textLines );
1665 const QPixmap previewPixmap = QgsPalLayerSettings::labelSettingsPreviewPixmap( mLabelSettings, QSize( textWidth, textHeight ), mLabelSettings.legendString() );
1666
1667 QByteArray byteArray;
1668 QBuffer buffer( &byteArray );
1669 previewPixmap.save( &buffer, "PNG" );
1670 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1671
1672 QJsonObject json;
1673 json[ QStringLiteral( "icon" ) ] = base64;
1674 return json;
1675}
1676
1677void QgsVectorLabelLegendNode::textWidthHeight( double &width, double &height, QgsRenderContext &ctx, const QgsTextFormat &textFormat, const QStringList &textLines ) const
1678{
1679 QFontMetricsF fm = QgsTextRenderer::fontMetrics( ctx, textFormat );
1680 height = QgsTextRenderer::textHeight( ctx, textFormat, 'A', true );
1681 width = QgsTextRenderer::textWidth( ctx, textFormat, textLines, &fm );
1682}
@ PreferVector
Prefer vector-based rendering, when the result will still be visually near-identical to a raster-base...
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
@ 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,...
@ RenderLayerTree
The render is for a layer tree display where map based properties are not available and where avoidan...
@ LosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
@ Antialiasing
Use antialiasing while drawing.
@ Marker
Marker symbol.
@ Line
Line symbol.
TextHorizontalAlignment
Text horizontal alignment.
Definition qgis.h:2892
@ 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.
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:78
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.
Q_DECL_DEPRECATED 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...
Qgis::RasterizedRenderingPolicy rasterizedRenderingPolicy() const
Returns the policy controlling when rasterisation of content during renders is permitted.
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:65
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:6945
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6944
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6392
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.