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