QGIS API Documentation 4.1.0-Master (659fe69c07c)
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 | Qt::ItemIsEditable;
381 else
382 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
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::EditRole && role != Qt::CheckStateRole )
649 return false;
650
651 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
652 if ( !( vlayer && vlayer->renderer() ) )
653 return false;
654
655 if ( role == Qt::EditRole )
656 {
657 const QString newLabel = value.toString();
658 setUserLabel( newLabel );
659 vlayer->renderer()->setLegendSymbolItemLabel( mItem.ruleKey(), newLabel );
660 }
661 else if ( role == Qt::CheckStateRole )
662 {
663 if ( !mItem.isCheckable() )
664 return false;
665
666 vlayer->renderer()->checkLegendSymbolItem( mItem.ruleKey(), value == Qt::Checked );
667 }
668
669 if ( QgsProject *project = vlayer->project() )
670 project->setDirty( true );
671
672 emit dataChanged();
673 vlayer->emitStyleChanged();
674
675 vlayer->triggerRepaint();
676
677 return true;
678}
679
680QSizeF QgsSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
681{
682 QgsSymbol *s = mCustomSymbol ? mCustomSymbol.get() : mItem.symbol();
683 if ( !s )
684 {
685 return QSizeF();
686 }
687
688 // setup temporary render context
689 QgsRenderContext *context = nullptr;
690 std::unique_ptr< QgsRenderContext > tempRenderContext;
692 if ( ctx && ctx->context )
693 context = ctx->context;
694 else
695 {
696 tempRenderContext = std::make_unique< QgsRenderContext >();
697 // QGIS 5.0 - make ItemContext compulsory, so we don't have to construct temporary render contexts here
699 tempRenderContext->setScaleFactor( settings.dpi() / 25.4 );
700 tempRenderContext->setRendererScale( settings.mapScale() );
701 tempRenderContext->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
702 tempRenderContext->setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * tempRenderContext->scaleFactor() ) ) );
704 tempRenderContext->setRasterizedRenderingPolicy( Qgis::RasterizedRenderingPolicy::PreferVector );
705 tempRenderContext->setPainter( ctx ? ctx->painter : nullptr );
706
707 // setup a minimal expression context
708 QgsExpressionContext expContext;
710 tempRenderContext->setExpressionContext( expContext );
711 context = tempRenderContext.get();
712 }
713
714 //Consider symbol size for point markers
715 const bool hasFixedWidth = ctx && ctx->patchSize.width() > 0;
716 const bool hasFixedHeight = ctx && ctx->patchSize.height() > 0;
717 const double desiredHeight = hasFixedHeight ? ctx->patchSize.height() : settings.symbolSize().height();
718 const double desiredWidth = hasFixedWidth ? ctx->patchSize.width() : settings.symbolSize().width();
719 double height = desiredHeight;
720 double width = desiredWidth;
721
722 //Center small marker symbols
723 double widthOffset = 0;
724 double heightOffset = 0;
725
726 const double maxSymbolSize = settings.maximumSymbolSize();
727 const double minSymbolSize = settings.minimumSymbolSize();
728
729 if ( QgsMarkerSymbol *markerSymbol = dynamic_cast<QgsMarkerSymbol *>( s ) )
730 {
731 const double size = markerSymbol->size( *context ) / context->scaleFactor();
732 if ( size > 0 )
733 {
734 if ( !hasFixedHeight )
735 height = size;
736 if ( !hasFixedWidth )
737 width = size;
738 }
739 }
740
741 bool restrictedSizeSymbolOK;
742 double restrictedSymbolWidth = width;
743 double restrictedSymbolHeight = height;
744 const std::unique_ptr<QgsSymbol> minMaxSizeSymbol(
745 QgsSymbolLayerUtils::restrictedSizeSymbol( s, minSymbolSize, maxSymbolSize, context, restrictedSymbolWidth, restrictedSymbolHeight, &restrictedSizeSymbolOK )
746 );
747 if ( minMaxSizeSymbol )
748 {
749 s = minMaxSizeSymbol.get();
750 if ( !hasFixedHeight )
751 height = restrictedSymbolHeight;
752 if ( !hasFixedWidth )
753 width = restrictedSymbolWidth;
754 }
755
756 if ( s->type() == Qgis::SymbolType::Marker )
757 {
758 if ( width < desiredWidth )
759 {
760 widthOffset = ( desiredWidth - width ) / 2.0;
761 }
762 if ( height < desiredHeight )
763 {
764 heightOffset = ( desiredHeight - height ) / 2.0;
765 }
766 }
767 if ( ctx && ctx->painter )
768 {
769 const double currentYCoord = ctx->top + ( itemHeight - desiredHeight ) / 2;
770 QPainter *p = ctx->painter;
771
772 //setup painter scaling to dots so that raster symbology is drawn to scale
773 const double dotsPerMM = context->scaleFactor();
774
775 int opacity = 255;
776 if ( QgsMapLayer *layer = layerNode()->layer() )
777 opacity = static_cast<int >( std::round( 255 * layer->opacity() ) );
778
779 const QgsScopedQPainterState painterState( p );
780 context->setPainterFlagsUsingContext( p );
781
782 switch ( settings.symbolAlignment() )
783 {
784 case Qt::AlignLeft:
785 default:
786 p->translate( ctx->columnLeft + widthOffset, currentYCoord + heightOffset );
787 break;
788 case Qt::AlignRight:
789 p->translate( ctx->columnRight - widthOffset - width, currentYCoord + heightOffset );
790 break;
791 }
792
793 p->scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
795 // QGIS 5.0 -- ctx->context will be mandatory
798
799 if ( opacity != 255 && !forceVector )
800 {
801 // if this is a semi transparent layer, we need to draw symbol to an image (to flatten it first)
802
803 const int maxBleed = static_cast< int >( std::ceil( QgsSymbolLayerUtils::estimateMaxSymbolBleed( s, *context ) ) );
804
805 // create image which is same size as legend rect, in case symbol bleeds outside its allotted space
806 const QSize symbolSize( static_cast< int >( std::round( width * dotsPerMM ) ), static_cast<int >( std::round( height * dotsPerMM ) ) );
807 const QSize tempImageSize( symbolSize.width() + maxBleed * 2, symbolSize.height() + maxBleed * 2 );
808 QImage tempImage = QImage( tempImageSize, QImage::Format_ARGB32 );
809 tempImage.fill( Qt::transparent );
810 QPainter imagePainter( &tempImage );
811 context->setPainterFlagsUsingContext( &imagePainter );
812
813 context->setPainter( &imagePainter );
814 imagePainter.translate( maxBleed, maxBleed );
815 s->drawPreviewIcon( &imagePainter, symbolSize, context, false, nullptr, &patchShape, ctx->screenProperties );
816 imagePainter.translate( -maxBleed, -maxBleed );
817 context->setPainter( ctx->painter );
818 //reduce opacity of image
819 imagePainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
820 imagePainter.fillRect( tempImage.rect(), QColor( 0, 0, 0, opacity ) );
821 imagePainter.end();
822 //draw rendered symbol image
823 p->drawImage( -maxBleed, -maxBleed, tempImage );
824 }
825 else if ( !restrictedSizeSymbolOK )
826 {
827 // if there is no restricted symbol size (because of geometry generator mainly) we need to ensure
828 // that there is no drawing outside the given size
829 const int maxBleed = static_cast< int >( std::ceil( QgsSymbolLayerUtils::estimateMaxSymbolBleed( s, *context ) ) );
830 const QSize symbolSize( static_cast< int >( std::round( width * dotsPerMM ) ), static_cast<int >( std::round( height * dotsPerMM ) ) );
831 const QSize maxSize( symbolSize.width() + maxBleed * 2, symbolSize.height() + maxBleed * 2 );
832 p->save();
833 p->setClipRect( -maxBleed, -maxBleed, maxSize.width(), maxSize.height(), Qt::IntersectClip );
834 s->drawPreviewIcon( p, symbolSize, context, false, nullptr, &patchShape, ctx->screenProperties );
835 p->restore();
836 }
837 else
838 {
839 s->drawPreviewIcon( p, QSize( static_cast< int >( std::round( width * dotsPerMM ) ), static_cast< int >( std::round( height * dotsPerMM ) ) ), context, false, nullptr, &patchShape, ctx->screenProperties );
840 }
841
842 if ( !mTextOnSymbolLabel.isEmpty() )
843 {
844 bool isNullSize = false;
845 const QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( *context, 1.0, &isNullSize ) );
846 if ( !isNullSize )
847 {
848 const qreal yBaselineVCenter = ( height * dotsPerMM + fm.ascent() - fm.descent() ) / 2;
849 QgsTextRenderer::drawText( QPointF( width * dotsPerMM / 2, yBaselineVCenter ), 0, Qgis::TextHorizontalAlignment::Center, QStringList() << mTextOnSymbolLabel, *context, mTextOnSymbolTextFormat );
850 }
851 }
852 }
853
854 return QSizeF( std::max( width + 2 * widthOffset, static_cast< double >( desiredWidth ) ), std::max( height + 2 * heightOffset, static_cast< double >( desiredHeight ) ) );
855}
856
857QJsonObject QgsSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const
858{
859 QJsonObject json;
860 if ( mItem.scaleMaxDenom() > 0 )
861 {
862 json[u"scaleMaxDenom"_s] = mItem.scaleMaxDenom();
863 }
864 if ( mItem.scaleMinDenom() > 0 )
865 {
866 json[u"scaleMinDenom"_s] = mItem.scaleMinDenom();
867 }
868
869 const QgsSymbol *s = mCustomSymbol ? mCustomSymbol.get() : mItem.symbol();
870 if ( !s )
871 {
872 return json;
873 }
874
875
877 // QGIS 5.0 - use render context directly here, and note in the dox that the context must be correctly setup
879 ctx.setScaleFactor( settings.dpi() / 25.4 );
880 ctx.setRendererScale( settings.mapScale() );
881 ctx.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * ctx.scaleFactor() ) ) );
882 ctx.setForceVectorOutput( true );
885
887
888 // ensure that a minimal expression context is available
889 QgsExpressionContext expContext = context.expressionContext();
891 ctx.setExpressionContext( expContext );
892
893 const QPixmap pix = QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), minimumIconSize(), 0, &ctx );
894 QImage img( pix.toImage().convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
895
896 int opacity = 255;
897 if ( QgsMapLayer *layer = layerNode()->layer() )
898 opacity = ( 255 * layer->opacity() );
899
900 if ( opacity != 255 )
901 {
902 QPainter painter;
903 painter.begin( &img );
904 painter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
905 painter.fillRect( pix.rect(), QColor( 0, 0, 0, opacity ) );
906 painter.end();
907 }
908
909 QByteArray byteArray;
910 QBuffer buffer( &byteArray );
911 img.save( &buffer, "PNG" );
912 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
913
914 json[u"icon"_s] = base64;
915 return json;
916}
917
919{
921 updateLabel();
922}
923
924
926{
927 if ( mSymbolUsesMapUnits )
928 {
929 mPixmap = QPixmap();
930 emit dataChanged();
931 }
932}
933
935{
936 if ( mIconSize == sz )
937 return;
938
939 mIconSize = sz;
940 mPixmap = QPixmap();
941 emit dataChanged();
942}
943
944void QgsSymbolLegendNode::updateLabel()
945{
946 if ( !mLayerNode )
947 return;
948
949 const bool showFeatureCount = mLayerNode->customProperty( u"showFeatureCount"_s, 0 ).toBool();
950 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
951 if ( !mLayerNode->labelExpression().isEmpty() )
952 mLabel = "[%" + mLayerNode->labelExpression() + "%]";
953 else
954 mLabel = symbolLabel();
955
956 if ( showFeatureCount && vl )
957 {
958 const bool estimatedCount = vl->dataProvider() ? QgsDataSourceUri( vl->dataProvider()->dataSourceUri() ).useEstimatedMetadata() : false;
959 const qlonglong count = mEmbeddedInParent ? vl->featureCount() : vl->featureCount( mItem.ruleKey() );
960
961 // if you modify this line, please update QgsLayerTreeModel::data (DisplayRole)
962 mLabel += u" [%1%2]"_s.arg( estimatedCount ? u"≈"_s : QString(), count != -1 ? QLocale().toString( count ) : tr( "N/A" ) );
963 }
964
965 emit dataChanged();
966}
967
968QString QgsSymbolLegendNode::evaluateLabel( const QgsExpressionContext &context, const QString &label )
969{
970 if ( !mLayerNode )
971 return QString();
972
973 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
974
975 if ( vl )
976 {
977 QgsExpressionContext contextCopy = QgsExpressionContext( context );
979 contextCopy.appendScope( symbolScope );
980 contextCopy.appendScope( vl->createExpressionContextScope() );
981
982 if ( label.isEmpty() )
983 {
984 const QString symLabel = symbolLabel();
985 if ( !mLayerNode->labelExpression().isEmpty() )
986 mLabel = QgsExpression::replaceExpressionText( "[%" + mLayerNode->labelExpression() + "%]", &contextCopy );
987 else if ( symLabel.contains( "[%" ) )
988 mLabel = QgsExpression::replaceExpressionText( symLabel, &contextCopy );
989 return mLabel;
990 }
991 else
992 {
993 QString eLabel;
994 if ( !mLayerNode->labelExpression().isEmpty() )
995 eLabel = QgsExpression::replaceExpressionText( label + "[%" + mLayerNode->labelExpression() + "%]", &contextCopy );
996 else if ( label.contains( "[%" ) )
997 eLabel = QgsExpression::replaceExpressionText( label, &contextCopy );
998 return eLabel;
999 }
1000 }
1001 return mLabel;
1002}
1003
1005{
1006 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
1007
1008 QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Symbol scope" ) );
1009 scope->addVariable( QgsExpressionContextScope::StaticVariable( u"symbol_label"_s, symbolLabel().remove( "[%" ).remove( "%]" ), true ) );
1010 scope->addVariable( QgsExpressionContextScope::StaticVariable( u"symbol_id"_s, mItem.ruleKey(), true ) );
1011 if ( vl )
1012 {
1013 scope->addVariable( QgsExpressionContextScope::StaticVariable( u"symbol_count"_s, QVariant::fromValue( vl->featureCount( mItem.ruleKey() ) ), true ) );
1014 }
1015 return scope;
1016}
1017
1018// -------------------------------------------------------------------------
1019
1020
1021QgsSimpleLegendNode::QgsSimpleLegendNode( QgsLayerTreeLayer *nodeLayer, const QString &label, const QIcon &icon, QObject *parent, const QString &key )
1022 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1023 , mLabel( label )
1024 , mIcon( icon )
1025 , mKey( key )
1026{}
1027
1028QVariant QgsSimpleLegendNode::data( int role ) const
1029{
1030 if ( role == Qt::DisplayRole || role == Qt::EditRole )
1031 return mUserLabel.isEmpty() ? mLabel : mUserLabel;
1032 else if ( role == Qt::DecorationRole )
1033 return mIcon;
1034 else if ( role == static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ) && !mKey.isEmpty() )
1035 return mKey;
1036 else if ( role == static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::NodeType ) )
1038 else
1039 return QVariant();
1040}
1041
1042
1043// -------------------------------------------------------------------------
1044
1045QgsImageLegendNode::QgsImageLegendNode( QgsLayerTreeLayer *nodeLayer, const QImage &img, QObject *parent )
1046 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1047 , mImage( img )
1048{}
1049
1050QVariant QgsImageLegendNode::data( int role ) const
1051{
1052 if ( role == Qt::DecorationRole )
1053 {
1054 return QPixmap::fromImage( mImage );
1055 }
1056 else if ( role == Qt::SizeHintRole )
1057 {
1058 return mImage.size();
1059 }
1060 else if ( role == static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::NodeType ) )
1061 {
1063 }
1064 return QVariant();
1065}
1066
1067QSizeF QgsImageLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1068{
1069 Q_UNUSED( itemHeight )
1070
1071 if ( ctx && ctx->painter && ctx->context )
1072 {
1073 const QgsScopedRenderContextScaleToPixels scopedScaleToPixels( *( ctx->context ) );
1074 const double scaleFactor = ctx->context->scaleFactor();
1075 const double imgWidth = settings.wmsLegendSize().width() * scaleFactor;
1076 const double imgHeight = settings.wmsLegendSize().height() * scaleFactor;
1077
1078 const QImage scaledImg = mImage.scaled( QSizeF( imgWidth, imgHeight ).toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
1079 switch ( settings.symbolAlignment() )
1080 {
1081 case Qt::AlignLeft:
1082 default:
1083 ctx->painter->drawImage( QPointF( ctx->columnLeft * scaleFactor, ctx->top * scaleFactor ), scaledImg );
1084 break;
1085
1086 case Qt::AlignRight:
1087 ctx->painter->drawImage( QPointF( ctx->columnRight * scaleFactor - imgWidth, ctx->top * scaleFactor ), scaledImg );
1088 break;
1089 }
1090 }
1091 return settings.wmsLegendSize();
1092}
1093
1095{
1096 QByteArray byteArray;
1097 QBuffer buffer( &byteArray );
1098 mImage.save( &buffer, "PNG" );
1099 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1100
1101 QJsonObject json;
1102 json[u"icon"_s] = base64;
1103 return json;
1104}
1105
1106// -------------------------------------------------------------------------
1107
1109 QgsLayerTreeLayer *nodeLayer, const QColor &color, const QString &label, QObject *parent, bool isCheckable, const QString &ruleKey, const QString &parentRuleKey
1110)
1111 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1112 , mColor( color )
1113 , mLabel( label )
1114 , mCheckable( isCheckable )
1115 , mRuleKey( ruleKey )
1116 , mParentRuleKey( parentRuleKey )
1117{}
1118
1120{
1121 if ( mCheckable )
1122 return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable;
1123 else
1124 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
1125}
1126
1127QVariant QgsRasterSymbolLegendNode::data( int role ) const
1128{
1129 switch ( role )
1130 {
1131 case Qt::DecorationRole:
1132 {
1133 const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 ); // TODO: configurable?
1134 QPixmap pix( iconSize, iconSize );
1135 pix.fill( mColor );
1136 return QIcon( pix );
1137 }
1138
1139 case Qt::DisplayRole:
1140 case Qt::EditRole:
1141 return mUserLabel.isEmpty() ? mLabel : mUserLabel;
1142
1143 case static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::NodeType ):
1145
1146 case static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::RuleKey ):
1147 return mRuleKey;
1148
1150 return mParentRuleKey;
1151
1152 case Qt::CheckStateRole:
1153 {
1154 if ( !mCheckable )
1155 return QVariant();
1156
1157 if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
1158 {
1159 if ( !pclayer->renderer() )
1160 return QVariant();
1161
1162 return pclayer->renderer()->legendItemChecked( mRuleKey ) ? Qt::Checked : Qt::Unchecked;
1163 }
1164
1165 return QVariant();
1166 }
1167
1168 default:
1169 return QVariant();
1170 }
1171}
1172
1173bool QgsRasterSymbolLegendNode::setData( const QVariant &value, int role )
1174{
1175 if ( role != Qt::CheckStateRole )
1176 return false;
1177
1178 if ( !mCheckable )
1179 return false;
1180
1181 if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
1182 {
1183 if ( !pclayer->renderer() )
1184 return false;
1185
1186 pclayer->renderer()->checkLegendItem( mRuleKey, value == Qt::Checked );
1187
1188 emit dataChanged();
1189 pclayer->emitStyleChanged();
1190
1191 pclayer->triggerRepaint();
1192 if ( pclayer->sync3DRendererTo2DRenderer() )
1193 pclayer->convertRenderer3DFromRenderer2D();
1194
1195 return true;
1196 }
1197 else
1198 {
1199 return false;
1200 }
1201}
1202
1203
1204QSizeF QgsRasterSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1205{
1206 QSizeF size = settings.symbolSize();
1207 double offsetX = 0;
1208 if ( ctx )
1209 {
1210 if ( ctx->patchSize.width() > 0 )
1211 {
1212 if ( ctx->patchSize.width() < size.width() )
1213 offsetX = ( size.width() - ctx->patchSize.width() ) / 2.0;
1214 size.setWidth( ctx->patchSize.width() );
1215 }
1216 if ( ctx->patchSize.height() > 0 )
1217 {
1218 size.setHeight( ctx->patchSize.height() );
1219 }
1220 }
1221
1222 if ( ctx && ctx->painter )
1223 {
1224 QColor itemColor = mColor;
1225 if ( QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layerNode()->layer() ) )
1226 {
1227 if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
1228 itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
1229 }
1230 ctx->painter->setBrush( itemColor );
1231
1232 if ( settings.drawRasterStroke() )
1233 {
1234 QPen pen;
1235 pen.setColor( settings.rasterStrokeColor() );
1236 pen.setWidthF( settings.rasterStrokeWidth() );
1237 pen.setJoinStyle( Qt::MiterJoin );
1238 ctx->painter->setPen( pen );
1239 }
1240 else
1241 {
1242 ctx->painter->setPen( Qt::NoPen );
1243 }
1244
1245 switch ( settings.symbolAlignment() )
1246 {
1247 case Qt::AlignLeft:
1248 default:
1249 ctx->painter->drawRect( QRectF( ctx->columnLeft + offsetX, ctx->top + ( itemHeight - size.height() ) / 2, size.width(), size.height() ) );
1250 break;
1251
1252 case Qt::AlignRight:
1253 ctx->painter->drawRect( QRectF( ctx->columnRight - size.width() - offsetX, ctx->top + ( itemHeight - size.height() ) / 2, size.width(), size.height() ) );
1254 break;
1255 }
1256 }
1257 return size;
1258}
1259
1261{
1262 QImage img = QImage( settings.symbolSize().toSize(), QImage::Format_ARGB32 );
1263 img.fill( Qt::transparent );
1264
1265 QPainter painter( &img );
1266 painter.setRenderHint( QPainter::Antialiasing );
1267
1268 QColor itemColor = mColor;
1269 if ( QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layerNode()->layer() ) )
1270 {
1271 if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
1272 itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
1273 }
1274 painter.setBrush( itemColor );
1275
1276 if ( settings.drawRasterStroke() )
1277 {
1278 QPen pen;
1279 pen.setColor( settings.rasterStrokeColor() );
1280 pen.setWidthF( settings.rasterStrokeWidth() );
1281 pen.setJoinStyle( Qt::MiterJoin );
1282 painter.setPen( pen );
1283 }
1284 else
1285 {
1286 painter.setPen( Qt::NoPen );
1287 }
1288
1289 painter.drawRect( QRectF( 0, 0, settings.symbolSize().width(), settings.symbolSize().height() ) );
1290
1291 QByteArray byteArray;
1292 QBuffer buffer( &byteArray );
1293 img.save( &buffer, "PNG" );
1294 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1295
1296 QJsonObject json;
1297 json[u"icon"_s] = base64;
1298 return json;
1299}
1300
1301// -------------------------------------------------------------------------
1302
1304 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1305{}
1306
1308
1309QImage QgsWmsLegendNode::getLegendGraphic( bool synchronous ) const
1310{
1311 if ( !mValid && !mFetcher )
1312 {
1313 // or maybe in presence of a downloader we should just delete it
1314 // and start a new one ?
1315
1316 QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( mLayerNode->layer() );
1317
1318 if ( layer && layer->isValid() )
1319 {
1320 const QgsLayerTreeModel *mod = model();
1321 if ( !mod )
1322 return mImage;
1323 const QgsMapSettings *ms = mod->legendFilterMapSettings();
1324
1325 QgsRasterDataProvider *prov = layer->dataProvider();
1326 if ( !prov )
1327 return mImage;
1328
1329 Q_ASSERT( !mFetcher );
1330 mFetcher.reset( prov->getLegendGraphicFetcher( ms ) );
1331 if ( mFetcher )
1332 {
1333 connect( mFetcher.get(), &QgsImageFetcher::finish, this, &QgsWmsLegendNode::getLegendGraphicFinished );
1334 connect( mFetcher.get(), &QgsImageFetcher::error, this, &QgsWmsLegendNode::getLegendGraphicErrored );
1335 connect( mFetcher.get(), &QgsImageFetcher::progress, this, &QgsWmsLegendNode::getLegendGraphicProgress );
1336 mFetcher->start();
1337 if ( synchronous )
1338 {
1339 QEventLoop loop;
1340 // The slots getLegendGraphicFinished and getLegendGraphicErrored will destroy the fetcher
1341 connect( mFetcher.get(), &QObject::destroyed, &loop, &QEventLoop::quit );
1342 loop.exec();
1343 }
1344 }
1345 }
1346 else
1347 {
1348 QgsDebugError( u"Failed to download legend graphics: layer is not valid."_s );
1349 }
1350 }
1351
1352 return mImage;
1353}
1354
1355QVariant QgsWmsLegendNode::data( int role ) const
1356{
1357 if ( role == Qt::DecorationRole )
1358 {
1359 return QPixmap::fromImage( getLegendGraphic() );
1360 }
1361 else if ( role == Qt::SizeHintRole )
1362 {
1363 return getLegendGraphic().size();
1364 }
1365 else if ( role == static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::NodeType ) )
1366 {
1368 }
1369 return QVariant();
1370}
1371
1372QSizeF QgsWmsLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1373{
1374 Q_UNUSED( itemHeight )
1375
1376 const QImage image = getLegendGraphic( settings.synchronousLegendRequests() );
1377
1378 double px2mm = 1000. / image.dotsPerMeterX();
1379 double mmWidth = image.width() * px2mm;
1380 double mmHeight = image.height() * px2mm;
1381
1382 QSize targetSize = QSize( mmWidth, mmHeight );
1383 if ( settings.wmsLegendSize().width() < mmWidth )
1384 {
1385 double targetHeight = mmHeight * settings.wmsLegendSize().width() / mmWidth;
1386 targetSize = QSize( settings.wmsLegendSize().width(), targetHeight );
1387 }
1388 else if ( settings.wmsLegendSize().height() < mmHeight )
1389 {
1390 double targetWidth = mmWidth * settings.wmsLegendSize().height() / mmHeight;
1391 targetSize = QSize( targetWidth, settings.wmsLegendSize().height() );
1392 }
1393
1394 if ( ctx && ctx->painter )
1395 {
1396 QImage smoothImage = image.scaled( targetSize / px2mm, Qt::KeepAspectRatio, Qt::SmoothTransformation );
1397
1398 switch ( settings.symbolAlignment() )
1399 {
1400 case Qt::AlignLeft:
1401 default:
1402 ctx->painter->drawImage( QRectF( ctx->columnLeft, ctx->top, targetSize.width(), targetSize.height() ), smoothImage, QRectF( QPointF( 0, 0 ), smoothImage.size() ) );
1403 break;
1404
1405 case Qt::AlignRight:
1406 ctx->painter->drawImage( QRectF( ctx->columnRight - settings.wmsLegendSize().width(), ctx->top, targetSize.width(), targetSize.height() ), smoothImage, QRectF( QPointF( 0, 0 ), smoothImage.size() ) );
1407 break;
1408 }
1409 }
1410 return targetSize;
1411}
1412
1414{
1415 QByteArray byteArray;
1416 QBuffer buffer( &byteArray );
1417 mImage.save( &buffer, "PNG" );
1418 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1419
1420 QJsonObject json;
1421 json[u"icon"_s] = base64;
1422 return json;
1423}
1424
1425QImage QgsWmsLegendNode::renderMessage( const QString &msg ) const
1426{
1427 const int fontHeight = 10;
1428 const int margin = fontHeight / 2;
1429 const int nlines = 1;
1430
1431 const int w = 512, h = fontHeight * nlines + margin * ( nlines + 1 );
1432 QImage image( w, h, QImage::Format_ARGB32_Premultiplied );
1433 QPainter painter;
1434 painter.begin( &image );
1435 painter.setPen( QColor( 255, 0, 0 ) );
1436 painter.setFont( QFont( u"Chicago"_s, fontHeight ) );
1437 painter.fillRect( 0, 0, w, h, QColor( 255, 255, 255 ) );
1438 painter.drawText( 0, margin + fontHeight, msg );
1439 //painter.drawText(0,2*(margin+fontHeight),tr("retrying in 5 seconds…"));
1440 painter.end();
1441
1442 return image;
1443}
1444
1445void QgsWmsLegendNode::getLegendGraphicProgress( qint64 cur, qint64 tot )
1446{
1447 const QString msg = tot > 0 ? tr( "Downloading: %1% (%2)" ).arg( static_cast< int >( std::round( 100 * cur / tot ) ) ).arg( QgsFileUtils::representFileSize( tot ) )
1448 : tr( "Downloading: %1" ).arg( QgsFileUtils::representFileSize( cur ) );
1449 mImage = renderMessage( msg );
1450 emit dataChanged();
1451}
1452
1453void QgsWmsLegendNode::getLegendGraphicErrored( const QString & )
1454{
1455 if ( !mFetcher )
1456 return; // must be coming after finish
1457
1458 mImage = QImage();
1459 emit dataChanged();
1460
1461 mFetcher.reset();
1462
1463 mValid = true; // we consider it valid anyway
1464}
1465
1466void QgsWmsLegendNode::getLegendGraphicFinished( const QImage &image )
1467{
1468 if ( !mFetcher )
1469 return; // must be coming after error
1470
1471 if ( !image.isNull() )
1472 {
1473 if ( image != mImage )
1474 {
1475 mImage = image;
1476 setUserPatchSize( mImage.size() );
1477 emit dataChanged();
1478 }
1479 mValid = true; // only if not null I guess
1480 }
1481 mFetcher.reset();
1482}
1483
1485{
1486 // TODO: do this only if this extent != prev extent ?
1487 mValid = false;
1488 emit dataChanged();
1489}
1490
1492{
1493 return getLegendGraphic( true );
1494}
1495
1496// -------------------------------------------------------------------------
1497
1499 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1500 , mSettings( std::make_unique<QgsDataDefinedSizeLegend>( settings ) )
1501{}
1502
1505
1506QVariant QgsDataDefinedSizeLegendNode::data( int role ) const
1507{
1508 if ( role == Qt::DecorationRole )
1509 {
1510 cacheImage();
1511 return QPixmap::fromImage( mImage );
1512 }
1513 else if ( role == Qt::SizeHintRole )
1514 {
1515 cacheImage();
1516 return mImage.size();
1517 }
1518 else if ( role == static_cast< int >( QgsLayerTreeModelLegendNode::CustomRole::NodeType ) )
1519 {
1521 }
1522 return QVariant();
1523}
1524
1526{
1527 // setup temporary render context if none specified
1528 QgsRenderContext *context = nullptr;
1529 std::unique_ptr< QgsRenderContext > tempRenderContext;
1530 if ( ctx.context )
1531 context = ctx.context;
1532 else
1533 {
1534 tempRenderContext = std::make_unique< QgsRenderContext >();
1535 // QGIS 5.0 - make ItemContext compulsory, so we don't have to construct temporary render contexts here
1537 tempRenderContext->setScaleFactor( settings.dpi() / 25.4 );
1538 tempRenderContext->setRendererScale( settings.mapScale() );
1539 tempRenderContext->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
1540 tempRenderContext->setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * tempRenderContext->scaleFactor() ) ) );
1541 tempRenderContext->setForceVectorOutput( true );
1542 tempRenderContext->setPainter( ctx.painter );
1543 tempRenderContext->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
1545
1546 // setup a minimal expression context
1547 QgsExpressionContext expContext;
1549 tempRenderContext->setExpressionContext( expContext );
1550 context = tempRenderContext.get();
1551 }
1552
1553 if ( context->painter() )
1554 {
1555 context->painter()->save();
1556 context->painter()->translate( ctx.columnLeft, ctx.top );
1557
1558 // scale to pixels
1559 context->painter()->scale( 1 / context->scaleFactor(), 1 / context->scaleFactor() );
1560 }
1561
1562 QgsDataDefinedSizeLegend ddsLegend( *mSettings );
1565
1566 QSizeF contentSize;
1567 double labelXOffset;
1568 ddsLegend.drawCollapsedLegend( *context, &contentSize, &labelXOffset );
1569
1570 if ( context->painter() )
1571 context->painter()->restore();
1572
1573 ItemMetrics im;
1574 im.symbolSize = QSizeF( ( contentSize.width() - labelXOffset ) / context->scaleFactor(), contentSize.height() / context->scaleFactor() );
1575 im.labelSize = QSizeF( labelXOffset / context->scaleFactor(), contentSize.height() / context->scaleFactor() );
1576 return im;
1577}
1578
1579
1580void QgsDataDefinedSizeLegendNode::cacheImage() const
1581{
1582 if ( mImage.isNull() )
1583 {
1584 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1585 if ( !context )
1586 {
1587 context = std::make_unique<QgsRenderContext>();
1588 Q_ASSERT( context ); // to make cppcheck happy
1589 context->setScaleFactor( 96 / 25.4 );
1590 }
1591 mImage = mSettings->collapsedLegendImage( *context );
1592 }
1593}
1594
1596 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1597 , mLabelSettings( labelSettings )
1598{}
1599
1602
1603QVariant QgsVectorLabelLegendNode::data( int role ) const
1604{
1605 if ( role == Qt::DisplayRole )
1606 {
1607 return mUserLabel;
1608 }
1609 if ( role == Qt::DecorationRole )
1610 {
1611 const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 );
1612 return QgsPalLayerSettings::labelSettingsPreviewPixmap( mLabelSettings, QSize( iconSize, iconSize ), mLabelSettings.legendString() );
1613 }
1614 return QVariant();
1615}
1616
1617QSizeF QgsVectorLabelLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1618{
1619 Q_UNUSED( itemHeight );
1620 if ( !ctx )
1621 {
1622 return QSizeF( 0, 0 );
1623 }
1624
1625 const QgsRenderContext *renderContext = ctx->context;
1626 if ( renderContext )
1627 {
1628 return drawSymbol( settings, *renderContext, ctx->columnLeft, ctx->top );
1629 }
1630
1631 return QSizeF( 0, 0 );
1632}
1633
1634QSizeF QgsVectorLabelLegendNode::drawSymbol( const QgsLegendSettings &settings, const QgsRenderContext &renderContext, double xOffset, double yOffset ) const
1635{
1636 const QStringList textLines( mLabelSettings.legendString() );
1637 const QgsTextFormat textFormat = mLabelSettings.format();
1638 QgsRenderContext ctx( renderContext );
1639 double textWidth, textHeight;
1640 textWidthHeight( textWidth, textHeight, ctx, textFormat, textLines );
1641 textWidth /= renderContext.scaleFactor();
1642 textHeight /= renderContext.scaleFactor();
1643 const QPointF
1644 textPos( renderContext.scaleFactor() * ( xOffset + settings.symbolSize().width() / 2.0 - textWidth / 2.0 ), renderContext.scaleFactor() * ( yOffset + settings.symbolSize().height() / 2.0 + textHeight / 2.0 ) );
1645
1646 const QgsScopedRenderContextScaleToPixels scopedScaleToPixels( ctx );
1647 QgsTextRenderer::drawText( textPos, 0.0, Qgis::TextHorizontalAlignment::Left, textLines, ctx, textFormat );
1648
1649 const double symbolWidth = std::max( textWidth, settings.symbolSize().width() );
1650 const double symbolHeight = std::max( textHeight, settings.symbolSize().height() );
1651 return QSizeF( symbolWidth, symbolHeight );
1652}
1653
1654QJsonObject QgsVectorLabelLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const
1655{
1656 Q_UNUSED( settings );
1657
1658 const double mmToPixel = 96.0 / 25.4; //settings.dpi() is deprecated
1659
1660 const QStringList textLines( mLabelSettings.legendString() );
1661 const QgsTextFormat textFormat = mLabelSettings.format();
1662 QgsRenderContext ctx( context );
1663 ctx.setScaleFactor( mmToPixel );
1664
1665 double textWidth, textHeight;
1666 textWidthHeight( textWidth, textHeight, ctx, textFormat, textLines );
1667 const QPixmap previewPixmap = QgsPalLayerSettings::labelSettingsPreviewPixmap( mLabelSettings, QSize( textWidth, textHeight ), mLabelSettings.legendString() );
1668
1669 QByteArray byteArray;
1670 QBuffer buffer( &byteArray );
1671 previewPixmap.save( &buffer, "PNG" );
1672 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1673
1674 QJsonObject json;
1675 json[u"icon"_s] = base64;
1676 return json;
1677}
1678
1679void QgsVectorLabelLegendNode::textWidthHeight( double &width, double &height, QgsRenderContext &ctx, const QgsTextFormat &textFormat, const QStringList &textLines ) const
1680{
1681 QFontMetricsF fm = QgsTextRenderer::fontMetrics( ctx, textFormat );
1682 height = QgsTextRenderer::textHeight( ctx, textFormat, 'A', true );
1683 width = QgsTextRenderer::textWidth( ctx, textFormat, textLines, &fm );
1684}
@ PreferVector
Prefer vector-based rendering, when the result will still be visually near-identical to a raster-base...
Definition qgis.h:2868
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
Definition qgis.h:2869
@ Symbol
Symbol icon (excluding label).
Definition qgis.h:4992
@ SymbolLabel
Symbol label (excluding icon).
Definition qgis.h:4993
@ Point
Text at point of origin layout mode.
Definition qgis.h:3072
@ RectangleCapHeightBased
Similar to Rectangle mode, but uses cap height only when calculating font heights for the first line ...
Definition qgis.h:3074
@ Horizontal
Horizontally oriented text.
Definition qgis.h:3056
@ Millimeters
Millimeters.
Definition qgis.h:5608
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
Definition qgis.h:2925
@ RenderLayerTree
The render is for a layer tree display where map based properties are not available and where avoidan...
Definition qgis.h:2936
@ LosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
Definition qgis.h:2926
@ Antialiasing
Use antialiasing while drawing.
Definition qgis.h:2921
@ Top
Align to top.
Definition qgis.h:3131
@ Marker
Marker symbol.
Definition qgis.h:639
@ Line
Line symbol.
Definition qgis.h:640
TextHorizontalAlignment
Text horizontal alignment.
Definition qgis.h:3111
@ Center
Center align.
Definition qgis.h:3113
@ WrapLines
Automatically wrap long lines of text.
Definition qgis.h:3590
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 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 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:7938
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7937
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7340
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.