QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgslayertreemodellegendnode.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayertreemodellegendnode.cpp
3 --------------------------------------
4 Date : August 2014
5 Copyright : (C) 2014 by Martin Dobias
6 Email : wonder dot sk at gmail dot com
7
8 QgsWMSLegendNode : Sandro Santilli < strk at keybit dot net >
9
10 ***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
20
22#include "qgslayertreemodel.h"
23#include "qgslegendsettings.h"
24#include "qgsrasterlayer.h"
25#include "qgsrenderer.h"
26#include "qgssymbollayerutils.h"
27#include "qgsimageoperation.h"
28#include "qgsvectorlayer.h"
29#include "qgspointcloudlayer.h"
31#include "qgsrasterrenderer.h"
33#include "qgsexpression.h"
34#include "qgstextrenderer.h"
35#include "qgssettings.h"
36#include "qgsfileutils.h"
37#include "qgsmarkersymbol.h"
38#include "qgsvariantutils.h"
39#include "qgslayertreelayer.h"
40#include "qgstextdocument.h"
42
43#include <QBuffer>
44#include <optional>
45
47 : QObject( parent )
48 , mLayerNode( nodeL )
49 , mEmbeddedInParent( false )
50{
51}
52
54{
55 return qobject_cast<QgsLayerTreeModel *>( parent() );
56}
57
59{
60 return Qt::ItemIsEnabled;
61}
62
63bool QgsLayerTreeModelLegendNode::setData( const QVariant &value, int role )
64{
65 Q_UNUSED( value )
66 Q_UNUSED( role )
67 return false;
68}
69
71{
73 return mLayerNode->patchSize();
74
75 return mUserSize;
76}
77
79{
80 if ( mUserSize == size )
81 return;
82
83 mUserSize = size;
84 emit sizeChanged();
85}
86
88{
90
91 const QStringList lines = settings.evaluateItemText( data( Qt::DisplayRole ).toString(), ctx->context->expressionContext() );
92
94 // cppcheck-suppress autoVariables
95 ctx->textDocument = &textDocument;
96
97 std::optional< QgsScopedRenderContextScaleToPixels > scaleToPx( *ctx->context );
98 const QgsTextDocumentMetrics textDocumentMetrics = QgsTextDocumentMetrics::calculateMetrics( textDocument, f, *ctx->context,
100 // cppcheck-suppress autoVariables
101 ctx->textDocumentMetrics = &textDocumentMetrics;
102 scaleToPx.reset();
103
104 // itemHeight here is not really item height, it is only for symbol
105 // vertical alignment purpose, i.e. OK take single line height
106 // if there are more lines, those run under the symbol
107 // also note that we explicitly use the first line cap height here, in order to match the Qgis::TextLayoutMode::RectangleCapHeightBased mode
108 // used when rendering the symbol text
109 const double textHeight = textDocumentMetrics.firstLineCapHeight() / ctx->context->scaleFactor();
110 const double itemHeight = std::max( static_cast< double >( ctx && ctx->patchSize.height() > 0 ? ctx->patchSize.height() : settings.symbolSize().height() ), textHeight );
111
112 ItemMetrics im;
113 im.symbolSize = drawSymbol( settings, ctx, itemHeight );
114 im.labelSize = drawSymbolText( settings, ctx, im.symbolSize );
115
116 ctx->textDocument = nullptr;
117 ctx->textDocumentMetrics = nullptr;
118 return im;
119}
120
122{
123 QJsonObject json = exportSymbolToJson( settings, context );
124 const QString text = data( Qt::DisplayRole ).toString();
125 json[ QStringLiteral( "title" ) ] = text;
126 return json;
127}
128
129QSizeF QgsLayerTreeModelLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
130{
131 const QIcon symbolIcon = data( Qt::DecorationRole ).value<QIcon>();
132 if ( symbolIcon.isNull() )
133 return QSizeF();
134
135 QSizeF size = settings.symbolSize();
136 if ( ctx )
137 {
138 if ( ctx->patchSize.width() > 0 )
139 size.setWidth( ctx->patchSize.width( ) );
140 if ( ctx->patchSize.height() > 0 )
141 size.setHeight( ctx->patchSize.height( ) );
142 }
143
144 if ( ctx && ctx->painter )
145 {
146 switch ( settings.symbolAlignment() )
147 {
148 case Qt::AlignLeft:
149 default:
150 symbolIcon.paint( ctx->painter,
151 static_cast< int >( ctx->columnLeft ),
152 static_cast< int >( ctx->top + ( itemHeight - size.height() ) / 2 ),
153 static_cast< int >( size.width() ),
154 static_cast< int >( size.height() ) );
155 break;
156
157 case Qt::AlignRight:
158 symbolIcon.paint( ctx->painter,
159 static_cast< int >( ctx->columnRight - size.width() ),
160 static_cast< int >( ctx->top + ( itemHeight - size.height() ) / 2 ),
161 static_cast< int >( size.width() ),
162 static_cast< int >( size.height() ) );
163 break;
164 }
165 }
166 return size;
167}
168
170{
171 const QIcon icon = data( Qt::DecorationRole ).value<QIcon>();
172 if ( icon.isNull() )
173 return QJsonObject();
174
175 const QImage image( icon.pixmap( settings.symbolSize().width(), settings.symbolSize().height() ).toImage() );
176 QByteArray byteArray;
177 QBuffer buffer( &byteArray );
178 image.save( &buffer, "PNG" );
179 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
180
181 QJsonObject json;
182 json[ QStringLiteral( "icon" ) ] = base64;
183 return json;
184}
185
186QSizeF QgsLayerTreeModelLegendNode::drawSymbolText( const QgsLegendSettings &settings, ItemContext *ctx, QSizeF symbolSizeMM ) const
187{
188 // we need a full render context here, so make one if we don't already have one
189 std::unique_ptr< QgsRenderContext > tempContext;
190 QgsRenderContext *context = ctx ? ctx->context : nullptr;
191 if ( !context )
192 {
193 tempContext.reset( new QgsRenderContext( QgsRenderContext::fromQPainter( ctx ? ctx->painter : nullptr ) ) );
194 context = tempContext.get();
195 }
196
197 const QgsTextFormat format = settings.style( QgsLegendStyle::SymbolLabel ).textFormat();
198
199 // TODO QGIS 4.0 -- make these all mandatory
200 std::optional< QgsTextDocument > tempDocument;
201 const QgsTextDocument *document = ctx ? ctx->textDocument : nullptr;
202 if ( !document )
203 {
204 const QStringList lines = settings.evaluateItemText( data( Qt::DisplayRole ).toString(), context->expressionContext() );
205 tempDocument.emplace( format.allowHtmlFormatting() ? QgsTextDocument::fromHtml( lines ) : QgsTextDocument::fromPlainText( lines ) );
206 document = &tempDocument.value();
207 }
208
209 std::optional< QgsTextDocumentMetrics > tempMetrics;
210 const QgsTextDocumentMetrics *metrics = ctx ? ctx->textDocumentMetrics : nullptr;
211 if ( !metrics )
212 {
213 tempMetrics.emplace( QgsTextDocumentMetrics::calculateMetrics( *document, format, *context ) );
214 metrics = &tempMetrics.value();
215 }
216
217 const double dotsPerMM = context->scaleFactor();
218 QgsScopedRenderContextScaleToPixels scaleToPx( *context );
219
220 const QSizeF documentSize = metrics->documentSize( Qgis::TextLayoutMode::RectangleCapHeightBased, Qgis::TextOrientation::Horizontal );
221 const QSizeF labelSizeMM( documentSize / dotsPerMM );
222
223 double labelXMin = 0.0;
224 double labelXMax = 0.0;
225 double labelYMM = 0.0;
226 if ( ctx && context->painter() )
227 {
228 switch ( settings.symbolAlignment() )
229 {
230 case Qt::AlignLeft:
231 default:
232 labelXMin = ctx->columnLeft + std::max( static_cast< double >( symbolSizeMM.width() ), ctx->maxSiblingSymbolWidth )
235 labelXMax = ctx->columnRight;
236 break;
237
238 case Qt::AlignRight:
239 labelXMin = ctx->columnLeft;
240 // NOTE -- while the below calculations use the flipped margins from the style, that's only done because
241 // those are the only margins we expose and use for now! (and we expose them as generic margins, not side-specific
242 // ones) TODO when/if we expose other margin settings, these should be reversed...
243 labelXMax = ctx->columnRight - std::max( static_cast< double >( symbolSizeMM.width() ), ctx->maxSiblingSymbolWidth )
246 break;
247 }
248
249 labelYMM = ctx->top;
250
251 // Vertical alignment of label with symbol
252 if ( labelSizeMM.height() < symbolSizeMM.height() )
253 labelYMM += ( symbolSizeMM.height() - labelSizeMM.height() ) / 2; // label centered with symbol
254 }
255
256 if ( context->painter() )
257 {
258 Qgis::TextHorizontalAlignment halign = settings.style( QgsLegendStyle::SymbolLabel ).alignment() == Qt::AlignLeft ? Qgis::TextHorizontalAlignment::Left :
259 settings.style( QgsLegendStyle::SymbolLabel ).alignment() == Qt::AlignRight ? Qgis::TextHorizontalAlignment::Right : Qgis::TextHorizontalAlignment::Center;
260
261
262 QgsTextRenderer::drawDocument( QRectF( labelXMin * dotsPerMM, std::round( labelYMM * dotsPerMM ),
263 ( labelXMax - labelXMin )* dotsPerMM,
264 std::max( symbolSizeMM.height(), labelSizeMM.height() ) * dotsPerMM ),
265 format, *document, *metrics, *context, halign, Qgis::TextVerticalAlignment::Top,
267 }
268
269 return labelSizeMM;
270}
271
273{
274 checkAll( true );
275}
276
278{
279 checkAll( false );
280}
281
283{
284 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ) )
285 {
286 if ( !vlayer->renderer() )
287 return;
288
289 const QgsLegendSymbolList symbolList = vlayer->renderer()->legendSymbolItems();
290 for ( const auto &item : symbolList )
291 {
292 vlayer->renderer()->checkLegendSymbolItem( item.ruleKey(), ! vlayer->renderer()->legendSymbolItemChecked( item.ruleKey() ) );
293 }
294
295 emit dataChanged();
296 vlayer->emitStyleChanged();
297 vlayer->triggerRepaint();
298 }
299 else if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
300 {
301 if ( !pclayer->renderer() )
302 return;
303
304 const QStringList ruleKeys = pclayer->renderer()->legendRuleKeys();
305 for ( const QString &rule : ruleKeys )
306 {
307 pclayer->renderer()->checkLegendItem( rule, !pclayer->renderer()->legendItemChecked( rule ) );
308 }
309
310 emit dataChanged();
311 pclayer->emitStyleChanged();
312 pclayer->triggerRepaint();
313 }
314}
315
316// -------------------------------------------------------------------------
317
320
322 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
323 , mItem( item )
324 , mSymbolUsesMapUnits( false )
325{
327 mIconSize = QSize( iconSize, iconSize );
328
329 if ( MINIMUM_SIZE < 0 )
330 {
331 // it's FAR too expensive to construct a QgsSettings object for every symbol node, especially for complex
332 // projects. So only read the valid size ranges once, and store them for subsequent use
333 const QgsSettings settings;
334 MINIMUM_SIZE = settings.value( "/qgis/legendsymbolMinimumSize", 0.5 ).toDouble();
335 MAXIMUM_SIZE = settings.value( "/qgis/legendsymbolMaximumSize", 20.0 ).toDouble();
336 }
337
338 updateLabel();
339 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() ) )
340 connect( vl, &QgsVectorLayer::symbolFeatureCountMapChanged, this, &QgsSymbolLegendNode::updateLabel );
341
342 connect( nodeLayer, &QObject::destroyed, this, [ = ]() { mLayerNode = nullptr; } );
343
344 if ( const QgsSymbol *symbol = mItem.symbol() )
345 {
346 mSymbolUsesMapUnits = symbol->usesMapUnits();
347 }
348}
349
351
352Qt::ItemFlags QgsSymbolLegendNode::flags() const
353{
354 if ( mItem.isCheckable() )
355 return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
356 else
357 return Qt::ItemIsEnabled;
358}
359
360
362{
363 const std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
364 return minimumIconSize( context.get() );
365}
366
368{
370 const int largeIconSize = QgsLayerTreeModel::scaleIconSize( 512 );
371 QSize minSz( iconSize, iconSize );
372 if ( mItem.symbol() && ( mItem.symbol()->type() == Qgis::SymbolType::Marker
373 || mItem.symbol()->type() == Qgis::SymbolType::Line ) )
374 {
375 int maxSize = largeIconSize;
376
377 // unusued width, height variables
378 double width = 0.0;
379 double height = 0.0;
380 bool ok;
381 std::unique_ptr<QgsSymbol> symbol( QgsSymbolLayerUtils::restrictedSizeSymbol( mItem.symbol(), MINIMUM_SIZE, MAXIMUM_SIZE, context, width, height, &ok ) );
382
383 if ( !ok && context )
384 {
385 // It's not possible to get a restricted size symbol, so we restrict
386 // pixmap target size to be sure it would fit MAXIMUM_SIZE
387 maxSize = static_cast<int>( std::round( MAXIMUM_SIZE * context->scaleFactor() ) );
388 }
389
390 const QSize size( mItem.symbol()->type() == Qgis::SymbolType::Marker ? maxSize : minSz.width(),
391 maxSize );
392
395 context ).toImage(),
396 minSz,
397 true ).size();
398 }
399
400 if ( !mTextOnSymbolLabel.isEmpty() && context )
401 {
402 const double w = QgsTextRenderer::textWidth( *context, mTextOnSymbolTextFormat, QStringList() << mTextOnSymbolLabel );
403 const double h = QgsTextRenderer::textHeight( *context, mTextOnSymbolTextFormat, QStringList() << mTextOnSymbolLabel, Qgis::TextLayoutMode::Point );
404 int wInt = ceil( w ), hInt = ceil( h );
405 if ( wInt > minSz.width() ) minSz.setWidth( wInt );
406 if ( hInt > minSz.height() ) minSz.setHeight( hInt );
407 }
408
409 return minSz;
410}
411
413{
414 return mItem.symbol();
415}
416
418{
419 QString label;
420 if ( mEmbeddedInParent )
421 {
422 const QVariant legendlabel = mLayerNode->customProperty( QStringLiteral( "legend/title-label" ) );
423 const QString layerName = QgsVariantUtils::isNull( legendlabel ) ? mLayerNode->name() : legendlabel.toString();
424 label = mUserLabel.isEmpty() ? layerName : mUserLabel;
425 }
426 else
427 label = mUserLabel.isEmpty() ? mItem.label() : mUserLabel;
428 return label;
429}
430
432{
433 if ( mEmbeddedInParent )
434 {
435 return mLayerNode->patchShape();
436 }
437 else
438 {
439 return mPatchShape;
440 }
441}
442
444{
445 mPatchShape = shape;
446}
447
449{
450 return mCustomSymbol.get();
451}
452
454{
455 mCustomSymbol.reset( symbol );
456}
457
459{
460 if ( !symbol )
461 return;
462
463 std::unique_ptr< QgsSymbol > s( symbol ); // this method takes ownership of symbol
464 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
465 if ( !vlayer || !vlayer->renderer() )
466 return;
467
468 mItem.setSymbol( s.get() ); // doesn't transfer ownership
469 vlayer->renderer()->setLegendSymbolItem( mItem.ruleKey(), s.release() ); // DOES transfer ownership!
470
471 mPixmap = QPixmap();
472
473 emit dataChanged();
474 vlayer->triggerRepaint();
475}
476
478{
479 double scale = 0.0;
480 double mupp = 0.0;
481 int dpi = 0;
482 if ( auto *lModel = model() )
483 lModel->legendMapViewData( &mupp, &dpi, &scale );
484
485 if ( qgsDoubleNear( mupp, 0.0 ) || dpi == 0 || qgsDoubleNear( scale, 0.0 ) )
486 return nullptr;
487
488 // setup temporary render context
489 std::unique_ptr<QgsRenderContext> context = std::make_unique<QgsRenderContext>( );
490 context->setScaleFactor( dpi / 25.4 );
491 context->setRendererScale( scale );
492 context->setMapToPixel( QgsMapToPixel( mupp ) );
493 context->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
494 context->setFlag( Qgis::RenderContextFlag::RenderSymbolPreview, true );
495
496 QgsExpressionContext expContext;
498 context->setExpressionContext( expContext );
499
500 return context.release();
501}
502
503void QgsLayerTreeModelLegendNode::checkAll( bool state )
504{
505 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ) )
506 {
507 if ( !vlayer->renderer() )
508 return;
509
510 const QgsLegendSymbolList symbolList = vlayer->renderer()->legendSymbolItems();
511 for ( const auto &item : symbolList )
512 {
513 vlayer->renderer()->checkLegendSymbolItem( item.ruleKey(), state );
514 }
515
516 emit dataChanged();
517 vlayer->emitStyleChanged();
518 vlayer->triggerRepaint();
519 }
520 else if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
521 {
522 if ( !pclayer->renderer() )
523 return;
524
525 const QStringList ruleKeys = pclayer->renderer()->legendRuleKeys();
526 for ( const QString &rule : ruleKeys )
527 {
528 pclayer->renderer()->checkLegendItem( rule, state );
529 }
530
531 emit dataChanged();
532 pclayer->emitStyleChanged();
533 pclayer->triggerRepaint();
534 }
535}
536
537QVariant QgsSymbolLegendNode::data( int role ) const
538{
539 if ( role == Qt::DisplayRole )
540 {
541 return mLabel;
542 }
543 else if ( role == Qt::EditRole )
544 {
545 return mUserLabel.isEmpty() ? mItem.label() : mUserLabel;
546 }
547 else if ( role == Qt::DecorationRole )
548 {
549 if ( mPixmap.isNull() || mPixmap.size() != mIconSize )
550 {
551 QPixmap pix;
552 if ( mItem.symbol() )
553 {
554 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
555
556 // unusued width, height variables
557 double width = 0.0;
558 double height = 0.0;
559 const std::unique_ptr<QgsSymbol> symbol( QgsSymbolLayerUtils::restrictedSizeSymbol( mItem.symbol(), MINIMUM_SIZE, MAXIMUM_SIZE, context.get(), width, height ) );
560 pix = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol ? symbol.get() : mItem.symbol(), mIconSize, 0, context.get() );
561
562 if ( !mTextOnSymbolLabel.isEmpty() && context )
563 {
564 QPainter painter( &pix );
565 painter.setRenderHint( QPainter::Antialiasing );
566 context->setPainter( &painter );
567 bool isNullSize = false;
568 const QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( *context, 1.0, &isNullSize ) );
569 if ( !isNullSize )
570 {
571 const qreal yBaselineVCenter = ( mIconSize.height() + fm.ascent() - fm.descent() ) / 2;
572 QgsTextRenderer::drawText( QPointF( mIconSize.width() / 2, yBaselineVCenter ), 0, Qgis::TextHorizontalAlignment::Center,
573 QStringList() << mTextOnSymbolLabel, *context, mTextOnSymbolTextFormat );
574 }
575 }
576 }
577 else
578 {
579 pix = QPixmap( mIconSize );
580 pix.fill( Qt::transparent );
581 }
582
583 mPixmap = pix;
584 }
585 return mPixmap;
586 }
587 else if ( role == Qt::CheckStateRole )
588 {
589 if ( !mItem.isCheckable() )
590 return QVariant();
591
592 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ) )
593 {
594 if ( !vlayer->renderer() )
595 return QVariant();
596
597 return vlayer->renderer()->legendSymbolItemChecked( mItem.ruleKey() ) ? Qt::Checked : Qt::Unchecked;
598 }
599 }
600 else if ( role == RuleKeyRole )
601 {
602 return mItem.ruleKey();
603 }
604 else if ( role == ParentRuleKeyRole )
605 {
606 return mItem.parentRuleKey();
607 }
609 {
611 }
612
613 return QVariant();
614}
615
616bool QgsSymbolLegendNode::setData( const QVariant &value, int role )
617{
618 if ( role != Qt::CheckStateRole )
619 return false;
620
621 if ( !mItem.isCheckable() )
622 return false;
623
624 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
625 if ( !vlayer || !vlayer->renderer() )
626 return false;
627
628 vlayer->renderer()->checkLegendSymbolItem( mItem.ruleKey(), value == Qt::Checked );
629
630 emit dataChanged();
631 vlayer->emitStyleChanged();
632
633 vlayer->triggerRepaint();
634
635 return true;
636}
637
638
639
640QSizeF QgsSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
641{
642 QgsSymbol *s = mCustomSymbol ? mCustomSymbol.get() : mItem.symbol();
643 if ( !s )
644 {
645 return QSizeF();
646 }
647
648 // setup temporary render context
649 QgsRenderContext *context = nullptr;
650 std::unique_ptr< QgsRenderContext > tempRenderContext;
652 if ( ctx && ctx->context )
653 context = ctx->context;
654 else
655 {
656 tempRenderContext = std::make_unique< QgsRenderContext >();
657 // QGIS 4.0 - make ItemContext compulsory, so we don't have to construct temporary render contexts here
659 tempRenderContext->setScaleFactor( settings.dpi() / 25.4 );
660 tempRenderContext->setRendererScale( settings.mapScale() );
661 tempRenderContext->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
662 tempRenderContext->setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * tempRenderContext->scaleFactor() ) ) );
664 tempRenderContext->setForceVectorOutput( true );
665 tempRenderContext->setPainter( ctx ? ctx->painter : nullptr );
666
667 // setup a minimal expression context
668 QgsExpressionContext expContext;
670 tempRenderContext->setExpressionContext( expContext );
671 context = tempRenderContext.get();
672 }
673
674 //Consider symbol size for point markers
675 const double desiredHeight = ctx && ctx->patchSize.height() > 0 ? ctx->patchSize.height() : settings.symbolSize().height();
676 const double desiredWidth = ctx && ctx->patchSize.width() > 0 ? ctx->patchSize.width() : settings.symbolSize().width();
677 double height = desiredHeight;
678 double width = desiredWidth;
679
680 //Center small marker symbols
681 double widthOffset = 0;
682 double heightOffset = 0;
683
684 const double maxSymbolSize = settings.maximumSymbolSize();
685 const double minSymbolSize = settings.minimumSymbolSize();
686
687 if ( QgsMarkerSymbol *markerSymbol = dynamic_cast<QgsMarkerSymbol *>( s ) )
688 {
689 const double size = markerSymbol->size( *context ) / context->scaleFactor();
690 height = size;
691 width = size;
692 }
693
694 const std::unique_ptr<QgsSymbol> minMaxSizeSymbol( QgsSymbolLayerUtils::restrictedSizeSymbol( s, minSymbolSize, maxSymbolSize, context, width, height ) );
695 if ( minMaxSizeSymbol )
696 {
697 s = minMaxSizeSymbol.get();
698 }
699
700 if ( s->type() == Qgis::SymbolType::Marker )
701 {
702 if ( width < desiredWidth )
703 {
704 widthOffset = ( desiredWidth - width ) / 2.0;
705 }
706 if ( height < desiredHeight )
707 {
708 heightOffset = ( desiredHeight - height ) / 2.0;
709 }
710 }
711 if ( ctx && ctx->painter )
712 {
713 const double currentYCoord = ctx->top + ( itemHeight - desiredHeight ) / 2;
714 QPainter *p = ctx->painter;
715
716 //setup painter scaling to dots so that raster symbology is drawn to scale
717 const double dotsPerMM = context->scaleFactor();
718
719 int opacity = 255;
720 if ( QgsMapLayer *layer = layerNode()->layer() )
721 opacity = static_cast<int >( std::round( 255 * layer->opacity() ) );
722
723 const QgsScopedQPainterState painterState( p );
724 context->setPainterFlagsUsingContext( p );
725
726 switch ( settings.symbolAlignment() )
727 {
728 case Qt::AlignLeft:
729 default:
730 p->translate( ctx->columnLeft + widthOffset, currentYCoord + heightOffset );
731 break;
732 case Qt::AlignRight:
733 p->translate( ctx->columnRight - widthOffset - width, currentYCoord + heightOffset );
734 break;
735 }
736
737 p->scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
739 // QGIS 4.0 -- ctx->context will be mandatory
740 const bool useAdvancedEffects = ctx->context ? ctx->context->flags() & Qgis::RenderContextFlag::UseAdvancedEffects : settings.useAdvancedEffects();
742 if ( opacity != 255 && useAdvancedEffects )
743 {
744 const int maxBleed = static_cast< int >( std::ceil( QgsSymbolLayerUtils::estimateMaxSymbolBleed( s, *context ) ) );
745
746 //semi transparent layer, so need to draw symbol to an image (to flatten it first)
747 //create image which is same size as legend rect, in case symbol bleeds outside its allotted space
748 const QSize symbolSize( static_cast< int >( std::round( width * dotsPerMM ) ), static_cast<int >( std::round( height * dotsPerMM ) ) );
749 const QSize tempImageSize( symbolSize.width() + maxBleed * 2, symbolSize.height() + maxBleed * 2 );
750 QImage tempImage = QImage( tempImageSize, QImage::Format_ARGB32 );
751 tempImage.fill( Qt::transparent );
752 QPainter imagePainter( &tempImage );
753 context->setPainterFlagsUsingContext( &imagePainter );
754
755 context->setPainter( &imagePainter );
756 imagePainter.translate( maxBleed, maxBleed );
757 s->drawPreviewIcon( &imagePainter, symbolSize, context, false, nullptr, &patchShape );
758 imagePainter.translate( -maxBleed, -maxBleed );
759 context->setPainter( ctx->painter );
760 //reduce opacity of image
761 imagePainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
762 imagePainter.fillRect( tempImage.rect(), QColor( 0, 0, 0, opacity ) );
763 imagePainter.end();
764 //draw rendered symbol image
765 p->drawImage( -maxBleed, -maxBleed, tempImage );
766 }
767 else
768 {
769 s->drawPreviewIcon( p, QSize( static_cast< int >( std::round( width * dotsPerMM ) ), static_cast< int >( std::round( height * dotsPerMM ) ) ), context, false, nullptr, &patchShape );
770 }
771
772 if ( !mTextOnSymbolLabel.isEmpty() )
773 {
774 bool isNullSize = false;
775 const QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( *context, 1.0, &isNullSize ) );
776 if ( !isNullSize )
777 {
778 const qreal yBaselineVCenter = ( height * dotsPerMM + fm.ascent() - fm.descent() ) / 2;
779 QgsTextRenderer::drawText( QPointF( width * dotsPerMM / 2, yBaselineVCenter ), 0, Qgis::TextHorizontalAlignment::Center,
780 QStringList() << mTextOnSymbolLabel, *context, mTextOnSymbolTextFormat );
781 }
782 }
783 }
784
785 return QSizeF( std::max( width + 2 * widthOffset, static_cast< double >( desiredWidth ) ),
786 std::max( height + 2 * heightOffset, static_cast< double >( desiredHeight ) ) );
787}
788
789QJsonObject QgsSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const
790{
791 const QgsSymbol *s = mCustomSymbol ? mCustomSymbol.get() : mItem.symbol();
792 if ( !s )
793 {
794 return QJsonObject();
795 }
796
797
799 // QGIS 4.0 - use render context directly here, and note in the dox that the context must be correctly setup
801 ctx.setScaleFactor( settings.dpi() / 25.4 );
802 ctx.setRendererScale( settings.mapScale() );
803 ctx.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * ctx.scaleFactor() ) ) );
804 ctx.setForceVectorOutput( true );
807
809
810 // ensure that a minimal expression context is available
811 QgsExpressionContext expContext = context.expressionContext();
813 ctx.setExpressionContext( expContext );
814
815 const QPixmap pix = QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), minimumIconSize(), 0, &ctx );
816 QImage img( pix.toImage().convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
817
818 int opacity = 255;
819 if ( QgsMapLayer *layer = layerNode()->layer() )
820 opacity = ( 255 * layer->opacity() );
821
822 if ( opacity != 255 )
823 {
824 QPainter painter;
825 painter.begin( &img );
826 painter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
827 painter.fillRect( pix.rect(), QColor( 0, 0, 0, opacity ) );
828 painter.end();
829 }
830
831 QByteArray byteArray;
832 QBuffer buffer( &byteArray );
833 img.save( &buffer, "PNG" );
834 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
835
836 QJsonObject json;
837 json[ QStringLiteral( "icon" ) ] = base64;
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 mItem.scaleMaxDenom();
847 return json;
848}
849
851{
853 updateLabel();
854}
855
856
858{
859 if ( mSymbolUsesMapUnits )
860 {
861 mPixmap = QPixmap();
862 emit dataChanged();
863 }
864}
865
866
867void QgsSymbolLegendNode::updateLabel()
868{
869 if ( !mLayerNode )
870 return;
871
872 const bool showFeatureCount = mLayerNode->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toBool();
873 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
874 mLabel = symbolLabel();
875
876 if ( showFeatureCount && vl )
877 {
878 const bool estimatedCount = vl->dataProvider() ? QgsDataSourceUri( vl->dataProvider()->dataSourceUri() ).useEstimatedMetadata() : false;
879 const qlonglong count = mEmbeddedInParent ? vl->featureCount() : vl->featureCount( mItem.ruleKey() ) ;
880
881 // if you modify this line, please update QgsLayerTreeModel::data (DisplayRole)
882 mLabel += QStringLiteral( " [%1%2]" ).arg(
883 estimatedCount ? QStringLiteral( "≈" ) : QString(),
884 count != -1 ? QLocale().toString( count ) : tr( "N/A" ) );
885 }
886
887 emit dataChanged();
888}
889
890QString QgsSymbolLegendNode::evaluateLabel( const QgsExpressionContext &context, const QString &label )
891{
892 if ( !mLayerNode )
893 return QString();
894
895 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
896
897 if ( vl )
898 {
899 QgsExpressionContext contextCopy = QgsExpressionContext( context );
900 QgsExpressionContextScope *symbolScope = createSymbolScope();
901 contextCopy.appendScope( symbolScope );
902 contextCopy.appendScope( vl->createExpressionContextScope() );
903
904 if ( label.isEmpty() )
905 {
906 if ( ! mLayerNode->labelExpression().isEmpty() )
907 mLabel = QgsExpression::replaceExpressionText( "[%" + mLayerNode->labelExpression() + "%]", &contextCopy );
908 else if ( mLabel.contains( "[%" ) )
909 {
910 const QString symLabel = symbolLabel();
911 mLabel = QgsExpression::replaceExpressionText( symLabel, &contextCopy );
912 }
913 return mLabel;
914 }
915 else
916 {
917 QString eLabel;
918 if ( ! mLayerNode->labelExpression().isEmpty() )
919 eLabel = QgsExpression::replaceExpressionText( label + "[%" + mLayerNode->labelExpression() + "%]", &contextCopy );
920 else if ( label.contains( "[%" ) )
921 eLabel = QgsExpression::replaceExpressionText( label, &contextCopy );
922 return eLabel;
923 }
924 }
925 return mLabel;
926}
927
928QgsExpressionContextScope *QgsSymbolLegendNode::createSymbolScope() const
929{
930 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
931
932 QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Symbol scope" ) );
933 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_label" ), symbolLabel().remove( "[%" ).remove( "%]" ), true ) );
934 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_id" ), mItem.ruleKey(), true ) );
935 if ( vl )
936 {
937 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_count" ), QVariant::fromValue( vl->featureCount( mItem.ruleKey() ) ), true ) );
938 }
939 return scope;
940}
941
942// -------------------------------------------------------------------------
943
944
945QgsSimpleLegendNode::QgsSimpleLegendNode( QgsLayerTreeLayer *nodeLayer, const QString &label, const QIcon &icon, QObject *parent, const QString &key )
946 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
947 , mLabel( label )
948 , mIcon( icon )
949 , mKey( key )
950{
951}
952
953QVariant QgsSimpleLegendNode::data( int role ) const
954{
955 if ( role == Qt::DisplayRole || role == Qt::EditRole )
956 return mUserLabel.isEmpty() ? mLabel : mUserLabel;
957 else if ( role == Qt::DecorationRole )
958 return mIcon;
959 else if ( role == RuleKeyRole && !mKey.isEmpty() )
960 return mKey;
963 else
964 return QVariant();
965}
966
967
968// -------------------------------------------------------------------------
969
970QgsImageLegendNode::QgsImageLegendNode( QgsLayerTreeLayer *nodeLayer, const QImage &img, QObject *parent )
971 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
972 , mImage( img )
973{
974}
975
976QVariant QgsImageLegendNode::data( int role ) const
977{
978 if ( role == Qt::DecorationRole )
979 {
980 return QPixmap::fromImage( mImage );
981 }
982 else if ( role == Qt::SizeHintRole )
983 {
984 return mImage.size();
985 }
987 {
989 }
990 return QVariant();
991}
992
993QSizeF QgsImageLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
994{
995 Q_UNUSED( itemHeight )
996
997 if ( ctx && ctx->painter && ctx->context )
998 {
999 const QgsScopedRenderContextScaleToPixels scopedScaleToPixels( *( ctx->context ) );
1000 const double scaleFactor = ctx->context->scaleFactor();
1001 const double imgWidth = settings.wmsLegendSize().width() * scaleFactor;
1002 const double imgHeight = settings.wmsLegendSize().height() * scaleFactor;
1003
1004 const QImage scaledImg = mImage.scaled( QSizeF( imgWidth, imgHeight ).toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
1005 switch ( settings.symbolAlignment() )
1006 {
1007 case Qt::AlignLeft:
1008 default:
1009 ctx->painter->drawImage( QPointF( ctx->columnLeft * scaleFactor, ctx->top * scaleFactor ), scaledImg );
1010 break;
1011
1012 case Qt::AlignRight:
1013 ctx->painter->drawImage( QPointF( ctx->columnRight * scaleFactor - imgWidth, ctx->top * scaleFactor ), scaledImg );
1014 break;
1015 }
1016 }
1017 return settings.wmsLegendSize();
1018}
1019
1021{
1022 QByteArray byteArray;
1023 QBuffer buffer( &byteArray );
1024 mImage.save( &buffer, "PNG" );
1025 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1026
1027 QJsonObject json;
1028 json[ QStringLiteral( "icon" ) ] = base64;
1029 return json;
1030}
1031
1032// -------------------------------------------------------------------------
1033
1034QgsRasterSymbolLegendNode::QgsRasterSymbolLegendNode( QgsLayerTreeLayer *nodeLayer, const QColor &color, const QString &label, QObject *parent, bool isCheckable, const QString &ruleKey )
1035 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1036 , mColor( color )
1037 , mLabel( label )
1038 , mCheckable( isCheckable )
1039 , mRuleKey( ruleKey )
1040{
1041}
1042
1044{
1045 if ( mCheckable )
1046 return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
1047 else
1048 return Qt::ItemIsEnabled;
1049}
1050
1051QVariant QgsRasterSymbolLegendNode::data( int role ) const
1052{
1053 switch ( role )
1054 {
1055 case Qt::DecorationRole:
1056 {
1057 const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 ); // TODO: configurable?
1058 QPixmap pix( iconSize, iconSize );
1059 pix.fill( mColor );
1060 return QIcon( pix );
1061 }
1062
1063 case Qt::DisplayRole:
1064 case Qt::EditRole:
1065 return mUserLabel.isEmpty() ? mLabel : mUserLabel;
1066
1069
1071 return mRuleKey;
1072
1073 case Qt::CheckStateRole:
1074 {
1075 if ( !mCheckable )
1076 return QVariant();
1077
1078 if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
1079 {
1080 if ( !pclayer->renderer() )
1081 return QVariant();
1082
1083 return pclayer->renderer()->legendItemChecked( mRuleKey ) ? Qt::Checked : Qt::Unchecked;
1084 }
1085
1086 return QVariant();
1087 }
1088
1089 default:
1090 return QVariant();
1091 }
1092}
1093
1094bool QgsRasterSymbolLegendNode::setData( const QVariant &value, int role )
1095{
1096 if ( role != Qt::CheckStateRole )
1097 return false;
1098
1099 if ( !mCheckable )
1100 return false;
1101
1102 if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
1103 {
1104 if ( !pclayer->renderer() )
1105 return false;
1106
1107 pclayer->renderer()->checkLegendItem( mRuleKey, value == Qt::Checked );
1108
1109 emit dataChanged();
1110 pclayer->emitStyleChanged();
1111
1112 pclayer->triggerRepaint();
1113 return true;
1114 }
1115 else
1116 {
1117 return false;
1118 }
1119}
1120
1121
1122QSizeF QgsRasterSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1123{
1124 QSizeF size = settings.symbolSize();
1125 double offsetX = 0;
1126 if ( ctx )
1127 {
1128 if ( ctx->patchSize.width() > 0 )
1129 {
1130 if ( ctx->patchSize.width() < size.width() )
1131 offsetX = ( size.width() - ctx->patchSize.width() ) / 2.0;
1132 size.setWidth( ctx->patchSize.width() );
1133 }
1134 if ( ctx->patchSize.height() > 0 )
1135 {
1136 size.setHeight( ctx->patchSize.height() );
1137 }
1138 }
1139
1140 if ( ctx && ctx->painter )
1141 {
1142 QColor itemColor = mColor;
1143 if ( QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layerNode()->layer() ) )
1144 {
1145 if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
1146 itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
1147 }
1148 ctx->painter->setBrush( itemColor );
1149
1150 if ( settings.drawRasterStroke() )
1151 {
1152 QPen pen;
1153 pen.setColor( settings.rasterStrokeColor() );
1154 pen.setWidthF( settings.rasterStrokeWidth() );
1155 pen.setJoinStyle( Qt::MiterJoin );
1156 ctx->painter->setPen( pen );
1157 }
1158 else
1159 {
1160 ctx->painter->setPen( Qt::NoPen );
1161 }
1162
1163 switch ( settings.symbolAlignment() )
1164 {
1165 case Qt::AlignLeft:
1166 default:
1167 ctx->painter->drawRect( QRectF( ctx->columnLeft + offsetX, ctx->top + ( itemHeight - size.height() ) / 2,
1168 size.width(), size.height() ) );
1169 break;
1170
1171 case Qt::AlignRight:
1172 ctx->painter->drawRect( QRectF( ctx->columnRight - size.width() - offsetX, ctx->top + ( itemHeight - size.height() ) / 2,
1173 size.width(), size.height() ) );
1174 break;
1175 }
1176 }
1177 return size;
1178}
1179
1181{
1182 QImage img = QImage( settings.symbolSize().toSize(), QImage::Format_ARGB32 );
1183 img.fill( Qt::transparent );
1184
1185 QPainter painter( &img );
1186 painter.setRenderHint( QPainter::Antialiasing );
1187
1188 QColor itemColor = mColor;
1189 if ( QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layerNode()->layer() ) )
1190 {
1191 if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
1192 itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
1193 }
1194 painter.setBrush( itemColor );
1195
1196 if ( settings.drawRasterStroke() )
1197 {
1198 QPen pen;
1199 pen.setColor( settings.rasterStrokeColor() );
1200 pen.setWidthF( settings.rasterStrokeWidth() );
1201 pen.setJoinStyle( Qt::MiterJoin );
1202 painter.setPen( pen );
1203 }
1204 else
1205 {
1206 painter.setPen( Qt::NoPen );
1207 }
1208
1209 painter.drawRect( QRectF( 0, 0, settings.symbolSize().width(), settings.symbolSize().height() ) );
1210
1211 QByteArray byteArray;
1212 QBuffer buffer( &byteArray );
1213 img.save( &buffer, "PNG" );
1214 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1215
1216 QJsonObject json;
1217 json[ QStringLiteral( "icon" ) ] = base64;
1218 return json;
1219}
1220
1221// -------------------------------------------------------------------------
1222
1224 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1225 , mValid( false )
1226{
1227}
1228
1230
1231QImage QgsWmsLegendNode::getLegendGraphic() const
1232{
1233 if ( ! mValid && ! mFetcher )
1234 {
1235 // or maybe in presence of a downloader we should just delete it
1236 // and start a new one ?
1237
1238 QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( mLayerNode->layer() );
1239
1240 if ( layer && layer->isValid() )
1241 {
1242 const QgsLayerTreeModel *mod = model();
1243 if ( ! mod )
1244 return mImage;
1245 const QgsMapSettings *ms = mod->legendFilterMapSettings();
1246
1247 QgsRasterDataProvider *prov = layer->dataProvider();
1248 if ( ! prov )
1249 return mImage;
1250
1251 Q_ASSERT( ! mFetcher );
1252 mFetcher.reset( prov->getLegendGraphicFetcher( ms ) );
1253 if ( mFetcher )
1254 {
1255 connect( mFetcher.get(), &QgsImageFetcher::finish, this, &QgsWmsLegendNode::getLegendGraphicFinished );
1256 connect( mFetcher.get(), &QgsImageFetcher::error, this, &QgsWmsLegendNode::getLegendGraphicErrored );
1257 connect( mFetcher.get(), &QgsImageFetcher::progress, this, &QgsWmsLegendNode::getLegendGraphicProgress );
1258 mFetcher->start();
1259 }
1260 }
1261 else
1262 {
1263 QgsDebugMsg( QStringLiteral( "Failed to download legend graphics: layer is not valid." ) );
1264 }
1265 }
1266
1267 return mImage;
1268}
1269
1270QVariant QgsWmsLegendNode::data( int role ) const
1271{
1272 if ( role == Qt::DecorationRole )
1273 {
1274 return QPixmap::fromImage( getLegendGraphic() );
1275 }
1276 else if ( role == Qt::SizeHintRole )
1277 {
1278 return getLegendGraphic().size();
1279 }
1281 {
1283 }
1284 return QVariant();
1285}
1286
1287QSizeF QgsWmsLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1288{
1289 Q_UNUSED( itemHeight )
1290
1291 const QImage image = getLegendGraphic();
1292
1293 double px2mm = 1000. / image.dotsPerMeterX();
1294 double mmWidth = image.width() * px2mm;
1295 double mmHeight = image.height() * px2mm;
1296
1297 QSize targetSize = QSize( mmWidth, mmHeight );
1298 if ( settings.wmsLegendSize().width() < mmWidth )
1299 {
1300 double targetHeight = mmHeight * settings.wmsLegendSize().width() / mmWidth;
1301 targetSize = QSize( settings.wmsLegendSize().width(), targetHeight );
1302 }
1303 else if ( settings.wmsLegendSize().height() < mmHeight )
1304 {
1305 double targetWidth = mmWidth * settings.wmsLegendSize().height() / mmHeight;
1306 targetSize = QSize( targetWidth, settings.wmsLegendSize().height() );
1307 }
1308
1309 if ( ctx && ctx->painter )
1310 {
1311 QImage smoothImage = image.scaled( targetSize / px2mm, Qt::KeepAspectRatio, Qt::SmoothTransformation );
1312
1313 switch ( settings.symbolAlignment() )
1314 {
1315 case Qt::AlignLeft:
1316 default:
1317 ctx->painter->drawImage( QRectF( ctx->columnLeft,
1318 ctx->top,
1319 targetSize.width(),
1320 targetSize.height() ),
1321 smoothImage,
1322 QRectF( QPointF( 0, 0 ), smoothImage.size() ) );
1323 break;
1324
1325 case Qt::AlignRight:
1326 ctx->painter->drawImage( QRectF( ctx->columnRight - settings.wmsLegendSize().width(),
1327 ctx->top,
1328 targetSize.width(),
1329 targetSize.height() ),
1330 smoothImage,
1331 QRectF( QPointF( 0, 0 ), smoothImage.size() ) );
1332 break;
1333 }
1334 }
1335 return targetSize;
1336}
1337
1339{
1340 QByteArray byteArray;
1341 QBuffer buffer( &byteArray );
1342 mImage.save( &buffer, "PNG" );
1343 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1344
1345 QJsonObject json;
1346 json[ QStringLiteral( "icon" ) ] = base64;
1347 return json;
1348}
1349
1350QImage QgsWmsLegendNode::renderMessage( const QString &msg ) const
1351{
1352 const int fontHeight = 10;
1353 const int margin = fontHeight / 2;
1354 const int nlines = 1;
1355
1356 const int w = 512, h = fontHeight * nlines + margin * ( nlines + 1 );
1357 QImage image( w, h, QImage::Format_ARGB32_Premultiplied );
1358 QPainter painter;
1359 painter.begin( &image );
1360 painter.setPen( QColor( 255, 0, 0 ) );
1361 painter.setFont( QFont( QStringLiteral( "Chicago" ), fontHeight ) );
1362 painter.fillRect( 0, 0, w, h, QColor( 255, 255, 255 ) );
1363 painter.drawText( 0, margin + fontHeight, msg );
1364 //painter.drawText(0,2*(margin+fontHeight),tr("retrying in 5 seconds…"));
1365 painter.end();
1366
1367 return image;
1368}
1369
1370void QgsWmsLegendNode::getLegendGraphicProgress( qint64 cur, qint64 tot )
1371{
1372 const QString msg = tot > 0 ? tr( "Downloading: %1% (%2)" ).arg( static_cast< int >( std::round( 100 * cur / tot ) ) ).arg( QgsFileUtils::representFileSize( tot ) )
1373 : tr( "Downloading: %1" ).arg( QgsFileUtils::representFileSize( cur ) );
1374 mImage = renderMessage( msg );
1375 emit dataChanged();
1376}
1377
1378void QgsWmsLegendNode::getLegendGraphicErrored( const QString & )
1379{
1380 if ( ! mFetcher )
1381 return; // must be coming after finish
1382
1383 mImage = QImage();
1384 emit dataChanged();
1385
1386 mFetcher.reset();
1387
1388 mValid = true; // we consider it valid anyway
1389}
1390
1391void QgsWmsLegendNode::getLegendGraphicFinished( const QImage &image )
1392{
1393 if ( ! mFetcher )
1394 return; // must be coming after error
1395
1396 if ( ! image.isNull() )
1397 {
1398 if ( image != mImage )
1399 {
1400 mImage = image;
1401 setUserPatchSize( mImage.size() );
1402 emit dataChanged();
1403 }
1404 mValid = true; // only if not null I guess
1405 }
1406 mFetcher.reset();
1407}
1408
1410{
1411 // TODO: do this only if this extent != prev extent ?
1412 mValid = false;
1413 emit dataChanged();
1414}
1415
1416// -------------------------------------------------------------------------
1417
1419 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1420 , mSettings( new QgsDataDefinedSizeLegend( settings ) )
1421{
1422}
1423
1425{
1426 delete mSettings;
1427}
1428
1429QVariant QgsDataDefinedSizeLegendNode::data( int role ) const
1430{
1431 if ( role == Qt::DecorationRole )
1432 {
1433 cacheImage();
1434 return QPixmap::fromImage( mImage );
1435 }
1436 else if ( role == Qt::SizeHintRole )
1437 {
1438 cacheImage();
1439 return mImage.size();
1440 }
1442 {
1444 }
1445 return QVariant();
1446}
1447
1449{
1450 // setup temporary render context if none specified
1451 QgsRenderContext *context = nullptr;
1452 std::unique_ptr< QgsRenderContext > tempRenderContext;
1453 if ( ctx && ctx->context )
1454 context = ctx->context;
1455 else
1456 {
1457 tempRenderContext = std::make_unique< QgsRenderContext >();
1458 // QGIS 4.0 - make ItemContext compulsory, so we don't have to construct temporary render contexts here
1460 tempRenderContext->setScaleFactor( settings.dpi() / 25.4 );
1461 tempRenderContext->setRendererScale( settings.mapScale() );
1462 tempRenderContext->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
1463 tempRenderContext->setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * tempRenderContext->scaleFactor() ) ) );
1464 tempRenderContext->setForceVectorOutput( true );
1465 tempRenderContext->setPainter( ctx ? ctx->painter : nullptr );
1466 tempRenderContext->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
1468
1469 // setup a minimal expression context
1470 QgsExpressionContext expContext;
1472 tempRenderContext->setExpressionContext( expContext );
1473 context = tempRenderContext.get();
1474 }
1475
1476 if ( context->painter() )
1477 {
1478 context->painter()->save();
1479 context->painter()->translate( ctx->columnLeft, ctx->top );
1480
1481 // scale to pixels
1482 context->painter()->scale( 1 / context->scaleFactor(), 1 / context->scaleFactor() );
1483 }
1484
1485 QgsDataDefinedSizeLegend ddsLegend( *mSettings );
1486 ddsLegend.setFont( settings.style( QgsLegendStyle::SymbolLabel ).textFormat().toQFont() );
1487 ddsLegend.setTextColor( settings.style( QgsLegendStyle::SymbolLabel ).textFormat().color() );
1488
1489 QSizeF contentSize;
1490 double labelXOffset;
1491 ddsLegend.drawCollapsedLegend( *context, &contentSize, &labelXOffset );
1492
1493 if ( context->painter() )
1494 context->painter()->restore();
1495
1496 ItemMetrics im;
1497 im.symbolSize = QSizeF( ( contentSize.width() - labelXOffset ) / context->scaleFactor(), contentSize.height() / context->scaleFactor() );
1498 im.labelSize = QSizeF( labelXOffset / context->scaleFactor(), contentSize.height() / context->scaleFactor() );
1499 return im;
1500}
1501
1502
1503void QgsDataDefinedSizeLegendNode::cacheImage() const
1504{
1505 if ( mImage.isNull() )
1506 {
1507 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1508 if ( !context )
1509 {
1510 context.reset( new QgsRenderContext );
1511 Q_ASSERT( context ); // to make cppcheck happy
1512 context->setScaleFactor( 96 / 25.4 );
1513 }
1514 mImage = mSettings->collapsedLegendImage( *context );
1515 }
1516}
1517
1518QgsVectorLabelLegendNode::QgsVectorLabelLegendNode( QgsLayerTreeLayer *nodeLayer, const QgsPalLayerSettings &labelSettings, QObject *parent ): QgsLayerTreeModelLegendNode( nodeLayer, parent ), mLabelSettings( labelSettings )
1519{
1520}
1521
1523{
1524}
1525
1526QVariant QgsVectorLabelLegendNode::data( int role ) const
1527{
1528 if ( role == Qt::DisplayRole )
1529 {
1530 return mUserLabel;
1531 }
1532 if ( role == Qt::DecorationRole )
1533 {
1535 return QgsPalLayerSettings::labelSettingsPreviewPixmap( mLabelSettings, QSize( iconSize, iconSize ), mLabelSettings.legendString() );
1536 }
1537 return QVariant();
1538}
1539
1540QSizeF QgsVectorLabelLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1541{
1542 Q_UNUSED( itemHeight );
1543 if ( !ctx )
1544 {
1545 return QSizeF( 0, 0 );
1546 }
1547
1548 const QgsRenderContext *renderContext = ctx->context;
1549 if ( renderContext )
1550 {
1551 return drawSymbol( settings, *renderContext, ctx->columnLeft, ctx->top );
1552 }
1553
1554 return QSizeF( 0, 0 );
1555}
1556
1557QSizeF QgsVectorLabelLegendNode::drawSymbol( const QgsLegendSettings &settings, const QgsRenderContext &renderContext, double xOffset, double yOffset ) const
1558{
1559 const QStringList textLines( mLabelSettings.legendString() );
1560 const QgsTextFormat textFormat = mLabelSettings.format();
1561 QgsRenderContext ctx( renderContext );
1562 double textWidth, textHeight;
1563 textWidthHeight( textWidth, textHeight, ctx, textFormat, textLines );
1564 textWidth /= renderContext.scaleFactor();
1565 textHeight /= renderContext.scaleFactor();
1566 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 ) );
1567
1568 const QgsScopedRenderContextScaleToPixels scopedScaleToPixels( ctx );
1569 QgsTextRenderer::drawText( textPos, 0.0, Qgis::TextHorizontalAlignment::Left, textLines, ctx, textFormat );
1570
1571 const double symbolWidth = std::max( textWidth, settings.symbolSize().width() );
1572 const double symbolHeight = std::max( textHeight, settings.symbolSize().height() );
1573 return QSizeF( symbolWidth, symbolHeight );
1574}
1575
1576QJsonObject QgsVectorLabelLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const
1577{
1578 Q_UNUSED( settings );
1579
1580 const double mmToPixel = 96.0 / 25.4; //settings.dpi() is deprecated
1581
1582 const QStringList textLines( mLabelSettings.legendString() );
1583 const QgsTextFormat textFormat = mLabelSettings.format();
1584 QgsRenderContext ctx( context );
1585 ctx.setScaleFactor( mmToPixel );
1586
1587 double textWidth, textHeight;
1588 textWidthHeight( textWidth, textHeight, ctx, textFormat, textLines );
1589 const QPixmap previewPixmap = QgsPalLayerSettings::labelSettingsPreviewPixmap( mLabelSettings, QSize( textWidth, textHeight ), mLabelSettings.legendString() );
1590
1591 QByteArray byteArray;
1592 QBuffer buffer( &byteArray );
1593 previewPixmap.save( &buffer, "PNG" );
1594 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1595
1596 QJsonObject json;
1597 json[ QStringLiteral( "icon" ) ] = base64;
1598 return json;
1599}
1600
1601void QgsVectorLabelLegendNode::textWidthHeight( double &width, double &height, QgsRenderContext &ctx, const QgsTextFormat &textFormat, const QStringList &textLines ) const
1602{
1603 QFontMetricsF fm = QgsTextRenderer::fontMetrics( ctx, textFormat );
1604 height = QgsTextRenderer::textHeight( ctx, textFormat, 'A', true );
1605 width = QgsTextRenderer::textWidth( ctx, textFormat, textLines, &fm );
1606}
@ 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 ...
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
@ ApplyScalingWorkaroundForTextRendering
Whether a scaling workaround designed to stablise the rendering of small font sizes (or for painters ...
@ 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:1834
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.
Class for storing the component parts of a RDBMS 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)
item in symbology was 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.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
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.
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.
@ ParentRuleKeyRole
Rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2....
@ RuleKeyRole
Rule key of the node (QString)
@ NodeTypeRole
Type of node. Added in 3.16.
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.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
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.
The QgsLegendSettings class stores the appearance and layout settings for legend drawing with QgsLege...
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns the style for a legend component.
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 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
Q_DECL_DEPRECATED int dpi() const
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.
double margin(Side side)
Returns the margin (in mm) for the specified side of the component.
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.
@ Symbol
Symbol icon (excluding label)
@ SymbolLabel
Symbol label (excluding icon)
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
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:73
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.
bool isValid
Definition: qgsmaplayer.h:81
The QgsMapSettings class contains configuration for rendering of the map.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
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)
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.
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())
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.
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.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:63
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)
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.
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:93
void drawPreviewIcon(QPainter *painter, QSize size, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *patchShape=nullptr)
Draws an icon of the symbol that occupies an area given by size using the specified painter.
Definition: qgssymbol.cpp:923
bool usesMapUnits() const
Returns true if the symbol has any components which use map unit based sizes.
Definition: qgssymbol.cpp:630
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:152
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)
Returns precalculated text metrics for a text document, when rendered using the given base format and...
Represents a document consisting of one or more QgsTextBlock objects.
static QgsTextDocument fromHtml(const QStringList &lines)
Constructor for QgsTextDocument consisting of a set of HTML formatted lines.
static QgsTextDocument fromPlainText(const QStringList &lines)
Constructor for QgsTextDocument consisting of a set of plain text lines.
Container for all settings relating to text rendering.
Definition: qgstextformat.h:41
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...
bool allowHtmlFormatting() const
Returns true if text should be treated as a HTML document and HTML tags should be used for formatting...
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 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 constexpr double FONT_WORKAROUND_SCALE
Scale factor for upscaling font sizes and downscaling destination painter devices.
static bool isNull(const QVariant &variant)
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 data sets.
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.
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".
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:4093
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:4092
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:3509
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Single variable definition for use within a QgsExpressionContextScope.
double top
Top y-position of legend item.
QgsLegendPatchShape patchShape
The patch shape to render for the node.
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.