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