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