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