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