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