QGIS API Documentation 3.28.14-Firenze (exported)
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
22#include "qgslayertreemodel.h"
23#include "qgslegendsettings.h"
24#include "qgsrasterlayer.h"
25#include "qgsrenderer.h"
26#include "qgssymbollayerutils.h"
27#include "qgsimageoperation.h"
28#include "qgsvectorlayer.h"
29#include "qgspointcloudlayer.h"
31#include "qgsrasterrenderer.h"
33#include "qgsexpression.h"
34#include "qgstextrenderer.h"
35#include "qgssettings.h"
36#include "qgsfileutils.h"
37#include "qgsmarkersymbol.h"
38#include "qgsvariantutils.h"
39#include "qgslayertreelayer.h"
41
42#include <QBuffer>
43
45 : QObject( parent )
46 , mLayerNode( nodeL )
47 , mEmbeddedInParent( false )
48{
49}
50
52{
53 return qobject_cast<QgsLayerTreeModel *>( parent() );
54}
55
57{
58 return Qt::ItemIsEnabled;
59}
60
61bool QgsLayerTreeModelLegendNode::setData( const QVariant &value, int role )
62{
63 Q_UNUSED( value )
64 Q_UNUSED( role )
65 return false;
66}
67
69{
71 return mLayerNode->patchSize();
72
73 return mUserSize;
74}
75
77{
78 if ( mUserSize == size )
79 return;
80
81 mUserSize = size;
82 emit sizeChanged();
83}
84
86{
87 const QFont symbolLabelFont = settings.style( QgsLegendStyle::SymbolLabel ).font();
88
89 const double textHeight = settings.fontHeightCharacterMM( symbolLabelFont, QChar( '0' ) );
90 // itemHeight here is not really item height, it is only for symbol
91 // vertical alignment purpose, i.e. OK take single line height
92 // if there are more lines, those run under the symbol
93 const double itemHeight = std::max( static_cast< double >( ctx && ctx->patchSize.height() > 0 ? ctx->patchSize.height() : settings.symbolSize().height() ), textHeight );
94
95 ItemMetrics im;
96 im.symbolSize = drawSymbol( settings, ctx, itemHeight );
97 im.labelSize = drawSymbolText( settings, ctx, im.symbolSize );
98 return im;
99}
100
102{
103 QJsonObject json = exportSymbolToJson( settings, context );
104 const QString text = data( Qt::DisplayRole ).toString();
105 json[ QStringLiteral( "title" ) ] = text;
106 return json;
107}
108
109QSizeF QgsLayerTreeModelLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
110{
111 const QIcon symbolIcon = data( Qt::DecorationRole ).value<QIcon>();
112 if ( symbolIcon.isNull() )
113 return QSizeF();
114
115 QSizeF size = settings.symbolSize();
116 if ( ctx )
117 {
118 if ( ctx->patchSize.width() > 0 )
119 size.setWidth( ctx->patchSize.width( ) );
120 if ( ctx->patchSize.height() > 0 )
121 size.setHeight( ctx->patchSize.height( ) );
122 }
123
124 if ( ctx && ctx->painter )
125 {
126 switch ( settings.symbolAlignment() )
127 {
128 case Qt::AlignLeft:
129 default:
130 symbolIcon.paint( ctx->painter,
131 static_cast< int >( ctx->columnLeft ),
132 static_cast< int >( ctx->top + ( itemHeight - size.height() ) / 2 ),
133 static_cast< int >( size.width() ),
134 static_cast< int >( size.height() ) );
135 break;
136
137 case Qt::AlignRight:
138 symbolIcon.paint( ctx->painter,
139 static_cast< int >( ctx->columnRight - size.width() ),
140 static_cast< int >( ctx->top + ( itemHeight - size.height() ) / 2 ),
141 static_cast< int >( size.width() ),
142 static_cast< int >( size.height() ) );
143 break;
144 }
145 }
146 return size;
147}
148
150{
151 const QIcon icon = data( Qt::DecorationRole ).value<QIcon>();
152 if ( icon.isNull() )
153 return QJsonObject();
154
155 const QImage image( icon.pixmap( settings.symbolSize().width(), settings.symbolSize().height() ).toImage() );
156 QByteArray byteArray;
157 QBuffer buffer( &byteArray );
158 image.save( &buffer, "PNG" );
159 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
160
161 QJsonObject json;
162 json[ QStringLiteral( "icon" ) ] = base64;
163 return json;
164}
165
166QSizeF QgsLayerTreeModelLegendNode::drawSymbolText( const QgsLegendSettings &settings, ItemContext *ctx, QSizeF symbolSize ) const
167{
168 QSizeF labelSize( 0, 0 );
169
170 const QFont symbolLabelFont = settings.style( QgsLegendStyle::SymbolLabel ).font();
171 const double textHeight = settings.fontHeightCharacterMM( symbolLabelFont, QChar( '0' ) );
172 const double textDescent = settings.fontDescentMillimeters( symbolLabelFont );
173
174 const QgsExpressionContext tempContext;
175
176 const QStringList lines = settings.evaluateItemText( data( Qt::DisplayRole ).toString(), ctx && ctx->context ? ctx->context->expressionContext() : tempContext );
177
178 labelSize.rheight() = lines.count() * textHeight + ( lines.count() - 1 ) * ( settings.lineSpacing() + textDescent );
179
180 double labelXMin = 0.0;
181 double labelXMax = 0.0;
182 double labelY = 0.0;
183 if ( ctx && ctx->painter )
184 {
185 ctx->painter->setPen( settings.fontColor() );
186 switch ( settings.symbolAlignment() )
187 {
188 case Qt::AlignLeft:
189 default:
190 labelXMin = ctx->columnLeft + std::max( static_cast< double >( symbolSize.width() ), ctx->maxSiblingSymbolWidth )
193 labelXMax = ctx->columnRight;
194 break;
195
196 case Qt::AlignRight:
197 labelXMin = ctx->columnLeft;
198 // NOTE -- while the below calculations use the flipped margins from the style, that's only done because
199 // those are the only margins we expose and use for now! (and we expose them as generic margins, not side-specific
200 // ones) TODO when/if we expose other margin settings, these should be reversed...
201 labelXMax = ctx->columnRight - std::max( static_cast< double >( symbolSize.width() ), ctx->maxSiblingSymbolWidth )
204 break;
205 }
206
207 labelY = ctx->top;
208
209 // Vertical alignment of label with symbol
210 if ( labelSize.height() < symbolSize.height() )
211 labelY += symbolSize.height() / 2 - labelSize.height() / 2; // label centered with symbol
212
213 labelY += textHeight;
214 }
215
216 for ( QStringList::ConstIterator itemPart = lines.constBegin(); itemPart != lines.constEnd(); ++itemPart )
217 {
218 const double lineWidth = settings.textWidthMillimeters( symbolLabelFont, *itemPart );
219 labelSize.rwidth() = std::max( lineWidth, double( labelSize.width() ) );
220
221 if ( ctx && ctx->painter )
222 {
223 switch ( settings.style( QgsLegendStyle::SymbolLabel ).alignment() )
224 {
225 case Qt::AlignLeft:
226 default:
227 settings.drawText( ctx->painter, labelXMin, labelY, *itemPart, symbolLabelFont );
228 break;
229
230 case Qt::AlignRight:
231 settings.drawText( ctx->painter, labelXMax - lineWidth, labelY, *itemPart, symbolLabelFont );
232 break;
233
234 case Qt::AlignHCenter:
235 settings.drawText( ctx->painter, labelXMin + ( labelXMax - labelXMin - lineWidth ) / 2.0, labelY, *itemPart, symbolLabelFont );
236 break;
237 }
238
239 if ( itemPart != ( lines.end() - 1 ) )
240 labelY += textDescent + settings.lineSpacing() + textHeight;
241 }
242 }
243
244 return labelSize;
245}
246
248{
249 checkAll( true );
250}
251
253{
254 checkAll( false );
255}
256
258{
259 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ) )
260 {
261 if ( !vlayer->renderer() )
262 return;
263
264 const QgsLegendSymbolList symbolList = vlayer->renderer()->legendSymbolItems();
265 for ( const auto &item : symbolList )
266 {
267 vlayer->renderer()->checkLegendSymbolItem( item.ruleKey(), ! vlayer->renderer()->legendSymbolItemChecked( item.ruleKey() ) );
268 }
269
270 emit dataChanged();
271 vlayer->emitStyleChanged();
272 vlayer->triggerRepaint();
273 }
274 else if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
275 {
276 if ( !pclayer->renderer() )
277 return;
278
279 const QStringList ruleKeys = pclayer->renderer()->legendRuleKeys();
280 for ( const QString &rule : ruleKeys )
281 {
282 pclayer->renderer()->checkLegendItem( rule, !pclayer->renderer()->legendItemChecked( rule ) );
283 }
284
285 emit dataChanged();
286 pclayer->emitStyleChanged();
287 pclayer->triggerRepaint();
288 }
289}
290
291// -------------------------------------------------------------------------
292
295
297 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
298 , mItem( item )
299 , mSymbolUsesMapUnits( false )
300{
302 mIconSize = QSize( iconSize, iconSize );
303
304 if ( MINIMUM_SIZE < 0 )
305 {
306 // it's FAR too expensive to construct a QgsSettings object for every symbol node, especially for complex
307 // projects. So only read the valid size ranges once, and store them for subsequent use
308 const QgsSettings settings;
309 MINIMUM_SIZE = settings.value( "/qgis/legendsymbolMinimumSize", 0.5 ).toDouble();
310 MAXIMUM_SIZE = settings.value( "/qgis/legendsymbolMaximumSize", 20.0 ).toDouble();
311 }
312
313 updateLabel();
314 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() ) )
315 connect( vl, &QgsVectorLayer::symbolFeatureCountMapChanged, this, &QgsSymbolLegendNode::updateLabel );
316
317 connect( nodeLayer, &QObject::destroyed, this, [ = ]() { mLayerNode = nullptr; } );
318
319 if ( const QgsSymbol *symbol = mItem.symbol() )
320 {
321 mSymbolUsesMapUnits = symbol->usesMapUnits();
322 }
323}
324
326
327Qt::ItemFlags QgsSymbolLegendNode::flags() const
328{
329 if ( mItem.isCheckable() )
330 return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
331 else
332 return Qt::ItemIsEnabled;
333}
334
335
337{
338 const std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
339 return minimumIconSize( context.get() );
340}
341
343{
345 const int largeIconSize = QgsLayerTreeModel::scaleIconSize( 512 );
346 QSize minSz( iconSize, iconSize );
347 if ( mItem.symbol() && ( mItem.symbol()->type() == Qgis::SymbolType::Marker
348 || mItem.symbol()->type() == Qgis::SymbolType::Line ) )
349 {
350 int maxSize = largeIconSize;
351
352 // unusued width, height variables
353 double width = 0.0;
354 double height = 0.0;
355 bool ok;
356 std::unique_ptr<QgsSymbol> symbol( QgsSymbolLayerUtils::restrictedSizeSymbol( mItem.symbol(), MINIMUM_SIZE, MAXIMUM_SIZE, context, width, height, &ok ) );
357
358 if ( !ok && context )
359 {
360 // It's not possible to get a restricted size symbol, so we restrict
361 // pixmap target size to be sure it would fit MAXIMUM_SIZE
362 maxSize = static_cast<int>( std::round( MAXIMUM_SIZE * context->scaleFactor() ) );
363 }
364
365 const QSize size( mItem.symbol()->type() == Qgis::SymbolType::Marker ? maxSize : minSz.width(),
366 maxSize );
367
370 context ).toImage(),
371 minSz,
372 true ).size();
373 }
374
375 if ( !mTextOnSymbolLabel.isEmpty() && context )
376 {
377 const double w = QgsTextRenderer::textWidth( *context, mTextOnSymbolTextFormat, QStringList() << mTextOnSymbolLabel );
378 const double h = QgsTextRenderer::textHeight( *context, mTextOnSymbolTextFormat, QStringList() << mTextOnSymbolLabel, Qgis::TextLayoutMode::Point );
379 int wInt = ceil( w ), hInt = ceil( h );
380 if ( wInt > minSz.width() ) minSz.setWidth( wInt );
381 if ( hInt > minSz.height() ) minSz.setHeight( hInt );
382 }
383
384 return minSz;
385}
386
388{
389 return mItem.symbol();
390}
391
393{
394 QString label;
395 if ( mEmbeddedInParent )
396 {
397 const QVariant legendlabel = mLayerNode->customProperty( QStringLiteral( "legend/title-label" ) );
398 const QString layerName = QgsVariantUtils::isNull( legendlabel ) ? mLayerNode->name() : legendlabel.toString();
399 label = mUserLabel.isEmpty() ? layerName : mUserLabel;
400 }
401 else
402 label = mUserLabel.isEmpty() ? mItem.label() : mUserLabel;
403 return label;
404}
405
407{
408 if ( mEmbeddedInParent )
409 {
410 return mLayerNode->patchShape();
411 }
412 else
413 {
414 return mPatchShape;
415 }
416}
417
419{
420 mPatchShape = shape;
421}
422
424{
425 return mCustomSymbol.get();
426}
427
429{
430 mCustomSymbol.reset( symbol );
431}
432
434{
435 if ( !symbol )
436 return;
437
438 std::unique_ptr< QgsSymbol > s( symbol ); // this method takes ownership of symbol
439 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
440 if ( !vlayer || !vlayer->renderer() )
441 return;
442
443 mItem.setSymbol( s.get() ); // doesn't transfer ownership
444 vlayer->renderer()->setLegendSymbolItem( mItem.ruleKey(), s.release() ); // DOES transfer ownership!
445
446 mPixmap = QPixmap();
447
448 emit dataChanged();
449 vlayer->triggerRepaint();
450}
451
453{
454 double scale = 0.0;
455 double mupp = 0.0;
456 int dpi = 0;
457 if ( auto *lModel = model() )
458 lModel->legendMapViewData( &mupp, &dpi, &scale );
459
460 if ( qgsDoubleNear( mupp, 0.0 ) || dpi == 0 || qgsDoubleNear( scale, 0.0 ) )
461 return nullptr;
462
463 // setup temporary render context
464 std::unique_ptr<QgsRenderContext> context = std::make_unique<QgsRenderContext>( );
465 context->setScaleFactor( dpi / 25.4 );
466 context->setRendererScale( scale );
467 context->setMapToPixel( QgsMapToPixel( mupp ) );
468 context->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
469 context->setFlag( Qgis::RenderContextFlag::RenderSymbolPreview, true );
470
471 QgsExpressionContext expContext;
473 context->setExpressionContext( expContext );
474
475 return context.release();
476}
477
478void QgsLayerTreeModelLegendNode::checkAll( bool state )
479{
480 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ) )
481 {
482 if ( !vlayer->renderer() )
483 return;
484
485 const QgsLegendSymbolList symbolList = vlayer->renderer()->legendSymbolItems();
486 for ( const auto &item : symbolList )
487 {
488 vlayer->renderer()->checkLegendSymbolItem( item.ruleKey(), state );
489 }
490
491 emit dataChanged();
492 vlayer->emitStyleChanged();
493 vlayer->triggerRepaint();
494 }
495 else if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
496 {
497 if ( !pclayer->renderer() )
498 return;
499
500 const QStringList ruleKeys = pclayer->renderer()->legendRuleKeys();
501 for ( const QString &rule : ruleKeys )
502 {
503 pclayer->renderer()->checkLegendItem( rule, state );
504 }
505
506 emit dataChanged();
507 pclayer->emitStyleChanged();
508 pclayer->triggerRepaint();
509 }
510}
511
512QVariant QgsSymbolLegendNode::data( int role ) const
513{
514 if ( role == Qt::DisplayRole )
515 {
516 return mLabel;
517 }
518 else if ( role == Qt::EditRole )
519 {
520 return mUserLabel.isEmpty() ? mItem.label() : mUserLabel;
521 }
522 else if ( role == Qt::DecorationRole )
523 {
524 if ( mPixmap.isNull() || mPixmap.size() != mIconSize )
525 {
526 QPixmap pix;
527 if ( mItem.symbol() )
528 {
529 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
530
531 // unusued width, height variables
532 double width = 0.0;
533 double height = 0.0;
534 const std::unique_ptr<QgsSymbol> symbol( QgsSymbolLayerUtils::restrictedSizeSymbol( mItem.symbol(), MINIMUM_SIZE, MAXIMUM_SIZE, context.get(), width, height ) );
535 pix = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol ? symbol.get() : mItem.symbol(), mIconSize, 0, context.get() );
536
537 if ( !mTextOnSymbolLabel.isEmpty() && context )
538 {
539 QPainter painter( &pix );
540 painter.setRenderHint( QPainter::Antialiasing );
541 context->setPainter( &painter );
542 bool isNullSize = false;
543 const QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( *context, 1.0, &isNullSize ) );
544 if ( !isNullSize )
545 {
546 const qreal yBaselineVCenter = ( mIconSize.height() + fm.ascent() - fm.descent() ) / 2;
547 QgsTextRenderer::drawText( QPointF( mIconSize.width() / 2, yBaselineVCenter ), 0, Qgis::TextHorizontalAlignment::Center,
548 QStringList() << mTextOnSymbolLabel, *context, mTextOnSymbolTextFormat );
549 }
550 }
551 }
552 else
553 {
554 pix = QPixmap( mIconSize );
555 pix.fill( Qt::transparent );
556 }
557
558 mPixmap = pix;
559 }
560 return mPixmap;
561 }
562 else if ( role == Qt::CheckStateRole )
563 {
564 if ( !mItem.isCheckable() )
565 return QVariant();
566
567 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() ) )
568 {
569 if ( !vlayer->renderer() )
570 return QVariant();
571
572 return vlayer->renderer()->legendSymbolItemChecked( mItem.ruleKey() ) ? Qt::Checked : Qt::Unchecked;
573 }
574 }
575 else if ( role == RuleKeyRole )
576 {
577 return mItem.ruleKey();
578 }
579 else if ( role == ParentRuleKeyRole )
580 {
581 return mItem.parentRuleKey();
582 }
584 {
586 }
587
588 return QVariant();
589}
590
591bool QgsSymbolLegendNode::setData( const QVariant &value, int role )
592{
593 if ( role != Qt::CheckStateRole )
594 return false;
595
596 if ( !mItem.isCheckable() )
597 return false;
598
599 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
600 if ( !vlayer || !vlayer->renderer() )
601 return false;
602
603 vlayer->renderer()->checkLegendSymbolItem( mItem.ruleKey(), value == Qt::Checked );
604
605 emit dataChanged();
606 vlayer->emitStyleChanged();
607
608 vlayer->triggerRepaint();
609
610 return true;
611}
612
613
614
615QSizeF QgsSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
616{
617 QgsSymbol *s = mCustomSymbol ? mCustomSymbol.get() : mItem.symbol();
618 if ( !s )
619 {
620 return QSizeF();
621 }
622
623 // setup temporary render context
624 QgsRenderContext *context = nullptr;
625 std::unique_ptr< QgsRenderContext > tempRenderContext;
627 if ( ctx && ctx->context )
628 context = ctx->context;
629 else
630 {
631 tempRenderContext = std::make_unique< QgsRenderContext >();
632 // QGIS 4.0 - make ItemContext compulsory, so we don't have to construct temporary render contexts here
634 tempRenderContext->setScaleFactor( settings.dpi() / 25.4 );
635 tempRenderContext->setRendererScale( settings.mapScale() );
636 tempRenderContext->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
637 tempRenderContext->setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * tempRenderContext->scaleFactor() ) ) );
639 tempRenderContext->setForceVectorOutput( true );
640 tempRenderContext->setPainter( ctx ? ctx->painter : nullptr );
641
642 // setup a minimal expression context
643 QgsExpressionContext expContext;
645 tempRenderContext->setExpressionContext( expContext );
646 context = tempRenderContext.get();
647 }
648
649 //Consider symbol size for point markers
650 const double desiredHeight = ctx && ctx->patchSize.height() > 0 ? ctx->patchSize.height() : settings.symbolSize().height();
651 const double desiredWidth = ctx && ctx->patchSize.width() > 0 ? ctx->patchSize.width() : settings.symbolSize().width();
652 double height = desiredHeight;
653 double width = desiredWidth;
654
655 //Center small marker symbols
656 double widthOffset = 0;
657 double heightOffset = 0;
658
659 const double maxSymbolSize = settings.maximumSymbolSize();
660 const double minSymbolSize = settings.minimumSymbolSize();
661
662 if ( QgsMarkerSymbol *markerSymbol = dynamic_cast<QgsMarkerSymbol *>( s ) )
663 {
664 const double size = markerSymbol->size( *context ) / context->scaleFactor();
665 if ( size > 0 )
666 {
667 height = size;
668 width = size;
669 }
670 }
671
672 bool restrictedSizeSymbolOK;
673 const std::unique_ptr<QgsSymbol> minMaxSizeSymbol( QgsSymbolLayerUtils::restrictedSizeSymbol( s, minSymbolSize, maxSymbolSize, context, width, height, &restrictedSizeSymbolOK ) );
674 if ( minMaxSizeSymbol )
675 {
676 s = minMaxSizeSymbol.get();
677 }
678
679 if ( s->type() == Qgis::SymbolType::Marker )
680 {
681 if ( width < desiredWidth )
682 {
683 widthOffset = ( desiredWidth - width ) / 2.0;
684 }
685 if ( height < desiredHeight )
686 {
687 heightOffset = ( desiredHeight - height ) / 2.0;
688 }
689 }
690 if ( ctx && ctx->painter )
691 {
692 const double currentYCoord = ctx->top + ( itemHeight - desiredHeight ) / 2;
693 QPainter *p = ctx->painter;
694
695 //setup painter scaling to dots so that raster symbology is drawn to scale
696 const double dotsPerMM = context->scaleFactor();
697
698 int opacity = 255;
699 if ( QgsMapLayer *layer = layerNode()->layer() )
700 opacity = static_cast<int >( std::round( 255 * layer->opacity() ) );
701
702 const QgsScopedQPainterState painterState( p );
703 context->setPainterFlagsUsingContext( p );
704
705 switch ( settings.symbolAlignment() )
706 {
707 case Qt::AlignLeft:
708 default:
709 p->translate( ctx->columnLeft + widthOffset, currentYCoord + heightOffset );
710 break;
711 case Qt::AlignRight:
712 p->translate( ctx->columnRight - widthOffset - width, currentYCoord + heightOffset );
713 break;
714 }
715
716 p->scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
718 // QGIS 4.0 -- ctx->context will be mandatory
719 const bool useAdvancedEffects = ctx->context ? ctx->context->flags() & Qgis::RenderContextFlag::UseAdvancedEffects : settings.useAdvancedEffects();
721
722 if ( opacity != 255 && useAdvancedEffects )
723 {
724 // if this is a semi transparent layer, we need to draw symbol to an image (to flatten it first)
725
726 const int maxBleed = static_cast< int >( std::ceil( QgsSymbolLayerUtils::estimateMaxSymbolBleed( s, *context ) ) );
727
728 // create image which is same size as legend rect, in case symbol bleeds outside its allotted space
729 const QSize symbolSize( static_cast< int >( std::round( width * dotsPerMM ) ), static_cast<int >( std::round( height * dotsPerMM ) ) );
730 const QSize tempImageSize( symbolSize.width() + maxBleed * 2, symbolSize.height() + maxBleed * 2 );
731 QImage tempImage = QImage( tempImageSize, QImage::Format_ARGB32 );
732 tempImage.fill( Qt::transparent );
733 QPainter imagePainter( &tempImage );
734 context->setPainterFlagsUsingContext( &imagePainter );
735
736 context->setPainter( &imagePainter );
737 imagePainter.translate( maxBleed, maxBleed );
738 s->drawPreviewIcon( &imagePainter, symbolSize, context, false, nullptr, &patchShape );
739 imagePainter.translate( -maxBleed, -maxBleed );
740 context->setPainter( ctx->painter );
741 //reduce opacity of image
742 imagePainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
743 imagePainter.fillRect( tempImage.rect(), QColor( 0, 0, 0, opacity ) );
744 imagePainter.end();
745 //draw rendered symbol image
746 p->drawImage( -maxBleed, -maxBleed, tempImage );
747 }
748 else if ( !restrictedSizeSymbolOK )
749 {
750 // if there is no restricted symbol size (because of geometry generator mainly) we need to ensure
751 // that there is no drawing outside the given size
752 const int maxBleed = static_cast< int >( std::ceil( QgsSymbolLayerUtils::estimateMaxSymbolBleed( s, *context ) ) );
753 const QSize symbolSize( static_cast< int >( std::round( width * dotsPerMM ) ), static_cast<int >( std::round( height * dotsPerMM ) ) );
754 const QSize maxSize( symbolSize.width() + maxBleed * 2, symbolSize.height() + maxBleed * 2 );
755 p->save();
756 p->setClipRect( -maxBleed, -maxBleed, maxSize.width(), maxSize.height(), Qt::IntersectClip );
757 s->drawPreviewIcon( p, symbolSize, context, false, nullptr, &patchShape );
758 p->restore();
759 }
760 else
761 {
762 s->drawPreviewIcon( p, QSize( static_cast< int >( std::round( width * dotsPerMM ) ), static_cast< int >( std::round( height * dotsPerMM ) ) ), context, false, nullptr, &patchShape );
763 }
764
765 if ( !mTextOnSymbolLabel.isEmpty() )
766 {
767 bool isNullSize = false;
768 const QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( *context, 1.0, &isNullSize ) );
769 if ( !isNullSize )
770 {
771 const qreal yBaselineVCenter = ( height * dotsPerMM + fm.ascent() - fm.descent() ) / 2;
772 QgsTextRenderer::drawText( QPointF( width * dotsPerMM / 2, yBaselineVCenter ), 0, Qgis::TextHorizontalAlignment::Center,
773 QStringList() << mTextOnSymbolLabel, *context, mTextOnSymbolTextFormat );
774 }
775 }
776 }
777
778 return QSizeF( std::max( width + 2 * widthOffset, static_cast< double >( desiredWidth ) ),
779 std::max( height + 2 * heightOffset, static_cast< double >( desiredHeight ) ) );
780}
781
782QJsonObject QgsSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const
783{
784 QJsonObject json;
785 if ( mItem.scaleMaxDenom() > 0 )
786 {
787 json[ QStringLiteral( "scaleMaxDenom" ) ] = mItem.scaleMaxDenom();
788 }
789 if ( mItem.scaleMinDenom() > 0 )
790 {
791 json[ QStringLiteral( "scaleMinDenom" ) ] = mItem.scaleMinDenom();
792 }
793 mItem.scaleMaxDenom();
794
795 const QgsSymbol *s = mCustomSymbol ? mCustomSymbol.get() : mItem.symbol();
796 if ( !s )
797 {
798 return json;
799 }
800
801
803 // QGIS 4.0 - use render context directly here, and note in the dox that the context must be correctly setup
805 ctx.setScaleFactor( settings.dpi() / 25.4 );
806 ctx.setRendererScale( settings.mapScale() );
807 ctx.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * ctx.scaleFactor() ) ) );
808 ctx.setForceVectorOutput( true );
811
813
814 // ensure that a minimal expression context is available
815 QgsExpressionContext expContext = context.expressionContext();
817 ctx.setExpressionContext( expContext );
818
819 const QPixmap pix = QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), minimumIconSize(), 0, &ctx );
820 QImage img( pix.toImage().convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
821
822 int opacity = 255;
823 if ( QgsMapLayer *layer = layerNode()->layer() )
824 opacity = ( 255 * layer->opacity() );
825
826 if ( opacity != 255 )
827 {
828 QPainter painter;
829 painter.begin( &img );
830 painter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
831 painter.fillRect( pix.rect(), QColor( 0, 0, 0, opacity ) );
832 painter.end();
833 }
834
835 QByteArray byteArray;
836 QBuffer buffer( &byteArray );
837 img.save( &buffer, "PNG" );
838 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
839
840 json[ QStringLiteral( "icon" ) ] = base64;
841 return json;
842}
843
845{
847 updateLabel();
848}
849
850
852{
853 if ( mSymbolUsesMapUnits )
854 {
855 mPixmap = QPixmap();
856 emit dataChanged();
857 }
858}
859
860
861void QgsSymbolLegendNode::updateLabel()
862{
863 if ( !mLayerNode )
864 return;
865
866 const bool showFeatureCount = mLayerNode->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toBool();
867 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
868 mLabel = symbolLabel();
869
870 if ( showFeatureCount && vl )
871 {
872 const bool estimatedCount = vl->dataProvider() ? QgsDataSourceUri( vl->dataProvider()->dataSourceUri() ).useEstimatedMetadata() : false;
873 const qlonglong count = mEmbeddedInParent ? vl->featureCount() : vl->featureCount( mItem.ruleKey() ) ;
874
875 // if you modify this line, please update QgsLayerTreeModel::data (DisplayRole)
876 mLabel += QStringLiteral( " [%1%2]" ).arg(
877 estimatedCount ? QStringLiteral( "≈" ) : QString(),
878 count != -1 ? QLocale().toString( count ) : tr( "N/A" ) );
879 }
880
881 emit dataChanged();
882}
883
884QString QgsSymbolLegendNode::evaluateLabel( const QgsExpressionContext &context, const QString &label )
885{
886 if ( !mLayerNode )
887 return QString();
888
889 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
890
891 if ( vl )
892 {
893 QgsExpressionContext contextCopy = QgsExpressionContext( context );
894 QgsExpressionContextScope *symbolScope = createSymbolScope();
895 contextCopy.appendScope( symbolScope );
896 contextCopy.appendScope( vl->createExpressionContextScope() );
897
898 if ( label.isEmpty() )
899 {
900 if ( ! mLayerNode->labelExpression().isEmpty() )
901 mLabel = QgsExpression::replaceExpressionText( "[%" + mLayerNode->labelExpression() + "%]", &contextCopy );
902 else if ( mLabel.contains( "[%" ) )
903 {
904 const QString symLabel = symbolLabel();
905 mLabel = QgsExpression::replaceExpressionText( symLabel, &contextCopy );
906 }
907 return mLabel;
908 }
909 else
910 {
911 QString eLabel;
912 if ( ! mLayerNode->labelExpression().isEmpty() )
913 eLabel = QgsExpression::replaceExpressionText( label + "[%" + mLayerNode->labelExpression() + "%]", &contextCopy );
914 else if ( label.contains( "[%" ) )
915 eLabel = QgsExpression::replaceExpressionText( label, &contextCopy );
916 return eLabel;
917 }
918 }
919 return mLabel;
920}
921
922QgsExpressionContextScope *QgsSymbolLegendNode::createSymbolScope() const
923{
924 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mLayerNode->layer() );
925
926 QgsExpressionContextScope *scope = new QgsExpressionContextScope( tr( "Symbol scope" ) );
927 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_label" ), symbolLabel().remove( "[%" ).remove( "%]" ), true ) );
928 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_id" ), mItem.ruleKey(), true ) );
929 if ( vl )
930 {
931 scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_count" ), QVariant::fromValue( vl->featureCount( mItem.ruleKey() ) ), true ) );
932 }
933 return scope;
934}
935
936// -------------------------------------------------------------------------
937
938
939QgsSimpleLegendNode::QgsSimpleLegendNode( QgsLayerTreeLayer *nodeLayer, const QString &label, const QIcon &icon, QObject *parent, const QString &key )
940 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
941 , mLabel( label )
942 , mIcon( icon )
943 , mKey( key )
944{
945}
946
947QVariant QgsSimpleLegendNode::data( int role ) const
948{
949 if ( role == Qt::DisplayRole || role == Qt::EditRole )
950 return mUserLabel.isEmpty() ? mLabel : mUserLabel;
951 else if ( role == Qt::DecorationRole )
952 return mIcon;
953 else if ( role == RuleKeyRole && !mKey.isEmpty() )
954 return mKey;
957 else
958 return QVariant();
959}
960
961
962// -------------------------------------------------------------------------
963
964QgsImageLegendNode::QgsImageLegendNode( QgsLayerTreeLayer *nodeLayer, const QImage &img, QObject *parent )
965 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
966 , mImage( img )
967{
968}
969
970QVariant QgsImageLegendNode::data( int role ) const
971{
972 if ( role == Qt::DecorationRole )
973 {
974 return QPixmap::fromImage( mImage );
975 }
976 else if ( role == Qt::SizeHintRole )
977 {
978 return mImage.size();
979 }
981 {
983 }
984 return QVariant();
985}
986
987QSizeF QgsImageLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
988{
989 Q_UNUSED( itemHeight )
990
991 if ( ctx && ctx->painter && ctx->context )
992 {
993 const QgsScopedRenderContextScaleToPixels scopedScaleToPixels( *( ctx->context ) );
994 const double scaleFactor = ctx->context->scaleFactor();
995 const double imgWidth = settings.wmsLegendSize().width() * scaleFactor;
996 const double imgHeight = settings.wmsLegendSize().height() * scaleFactor;
997
998 const QImage scaledImg = mImage.scaled( QSizeF( imgWidth, imgHeight ).toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
999 switch ( settings.symbolAlignment() )
1000 {
1001 case Qt::AlignLeft:
1002 default:
1003 ctx->painter->drawImage( QPointF( ctx->columnLeft * scaleFactor, ctx->top * scaleFactor ), scaledImg );
1004 break;
1005
1006 case Qt::AlignRight:
1007 ctx->painter->drawImage( QPointF( ctx->columnRight * scaleFactor - imgWidth, ctx->top * scaleFactor ), scaledImg );
1008 break;
1009 }
1010 }
1011 return settings.wmsLegendSize();
1012}
1013
1015{
1016 QByteArray byteArray;
1017 QBuffer buffer( &byteArray );
1018 mImage.save( &buffer, "PNG" );
1019 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1020
1021 QJsonObject json;
1022 json[ QStringLiteral( "icon" ) ] = base64;
1023 return json;
1024}
1025
1026// -------------------------------------------------------------------------
1027
1028QgsRasterSymbolLegendNode::QgsRasterSymbolLegendNode( QgsLayerTreeLayer *nodeLayer, const QColor &color, const QString &label, QObject *parent, bool isCheckable, const QString &ruleKey )
1029 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1030 , mColor( color )
1031 , mLabel( label )
1032 , mCheckable( isCheckable )
1033 , mRuleKey( ruleKey )
1034{
1035}
1036
1038{
1039 if ( mCheckable )
1040 return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
1041 else
1042 return Qt::ItemIsEnabled;
1043}
1044
1045QVariant QgsRasterSymbolLegendNode::data( int role ) const
1046{
1047 switch ( role )
1048 {
1049 case Qt::DecorationRole:
1050 {
1051 const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 ); // TODO: configurable?
1052 QPixmap pix( iconSize, iconSize );
1053 pix.fill( mColor );
1054 return QIcon( pix );
1055 }
1056
1057 case Qt::DisplayRole:
1058 case Qt::EditRole:
1059 return mUserLabel.isEmpty() ? mLabel : mUserLabel;
1060
1063
1065 return mRuleKey;
1066
1067 case Qt::CheckStateRole:
1068 {
1069 if ( !mCheckable )
1070 return QVariant();
1071
1072 if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
1073 {
1074 if ( !pclayer->renderer() )
1075 return QVariant();
1076
1077 return pclayer->renderer()->legendItemChecked( mRuleKey ) ? Qt::Checked : Qt::Unchecked;
1078 }
1079
1080 return QVariant();
1081 }
1082
1083 default:
1084 return QVariant();
1085 }
1086}
1087
1088bool QgsRasterSymbolLegendNode::setData( const QVariant &value, int role )
1089{
1090 if ( role != Qt::CheckStateRole )
1091 return false;
1092
1093 if ( !mCheckable )
1094 return false;
1095
1096 if ( QgsPointCloudLayer *pclayer = qobject_cast<QgsPointCloudLayer *>( mLayerNode->layer() ) )
1097 {
1098 if ( !pclayer->renderer() )
1099 return false;
1100
1101 pclayer->renderer()->checkLegendItem( mRuleKey, value == Qt::Checked );
1102
1103 emit dataChanged();
1104 pclayer->emitStyleChanged();
1105
1106 pclayer->triggerRepaint();
1107 return true;
1108 }
1109 else
1110 {
1111 return false;
1112 }
1113}
1114
1115
1116QSizeF QgsRasterSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1117{
1118 QSizeF size = settings.symbolSize();
1119 double offsetX = 0;
1120 if ( ctx )
1121 {
1122 if ( ctx->patchSize.width() > 0 )
1123 {
1124 if ( ctx->patchSize.width() < size.width() )
1125 offsetX = ( size.width() - ctx->patchSize.width() ) / 2.0;
1126 size.setWidth( ctx->patchSize.width() );
1127 }
1128 if ( ctx->patchSize.height() > 0 )
1129 {
1130 size.setHeight( ctx->patchSize.height() );
1131 }
1132 }
1133
1134 if ( ctx && ctx->painter )
1135 {
1136 QColor itemColor = mColor;
1137 if ( QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layerNode()->layer() ) )
1138 {
1139 if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
1140 itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
1141 }
1142 ctx->painter->setBrush( itemColor );
1143
1144 if ( settings.drawRasterStroke() )
1145 {
1146 QPen pen;
1147 pen.setColor( settings.rasterStrokeColor() );
1148 pen.setWidthF( settings.rasterStrokeWidth() );
1149 pen.setJoinStyle( Qt::MiterJoin );
1150 ctx->painter->setPen( pen );
1151 }
1152 else
1153 {
1154 ctx->painter->setPen( Qt::NoPen );
1155 }
1156
1157 switch ( settings.symbolAlignment() )
1158 {
1159 case Qt::AlignLeft:
1160 default:
1161 ctx->painter->drawRect( QRectF( ctx->columnLeft + offsetX, ctx->top + ( itemHeight - size.height() ) / 2,
1162 size.width(), size.height() ) );
1163 break;
1164
1165 case Qt::AlignRight:
1166 ctx->painter->drawRect( QRectF( ctx->columnRight - size.width() - offsetX, ctx->top + ( itemHeight - size.height() ) / 2,
1167 size.width(), size.height() ) );
1168 break;
1169 }
1170 }
1171 return size;
1172}
1173
1175{
1176 QImage img = QImage( settings.symbolSize().toSize(), QImage::Format_ARGB32 );
1177 img.fill( Qt::transparent );
1178
1179 QPainter painter( &img );
1180 painter.setRenderHint( QPainter::Antialiasing );
1181
1182 QColor itemColor = mColor;
1183 if ( QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layerNode()->layer() ) )
1184 {
1185 if ( QgsRasterRenderer *rasterRenderer = rasterLayer->renderer() )
1186 itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
1187 }
1188 painter.setBrush( itemColor );
1189
1190 if ( settings.drawRasterStroke() )
1191 {
1192 QPen pen;
1193 pen.setColor( settings.rasterStrokeColor() );
1194 pen.setWidthF( settings.rasterStrokeWidth() );
1195 pen.setJoinStyle( Qt::MiterJoin );
1196 painter.setPen( pen );
1197 }
1198 else
1199 {
1200 painter.setPen( Qt::NoPen );
1201 }
1202
1203 painter.drawRect( QRectF( 0, 0, settings.symbolSize().width(), settings.symbolSize().height() ) );
1204
1205 QByteArray byteArray;
1206 QBuffer buffer( &byteArray );
1207 img.save( &buffer, "PNG" );
1208 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1209
1210 QJsonObject json;
1211 json[ QStringLiteral( "icon" ) ] = base64;
1212 return json;
1213}
1214
1215// -------------------------------------------------------------------------
1216
1218 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1219 , mValid( false )
1220{
1221}
1222
1224
1225QImage QgsWmsLegendNode::getLegendGraphic() const
1226{
1227 if ( ! mValid && ! mFetcher )
1228 {
1229 // or maybe in presence of a downloader we should just delete it
1230 // and start a new one ?
1231
1232 QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( mLayerNode->layer() );
1233
1234 if ( layer && layer->isValid() )
1235 {
1236 const QgsLayerTreeModel *mod = model();
1237 if ( ! mod )
1238 return mImage;
1239 const QgsMapSettings *ms = mod->legendFilterMapSettings();
1240
1241 QgsRasterDataProvider *prov = layer->dataProvider();
1242 if ( ! prov )
1243 return mImage;
1244
1245 Q_ASSERT( ! mFetcher );
1246 mFetcher.reset( prov->getLegendGraphicFetcher( ms ) );
1247 if ( mFetcher )
1248 {
1249 connect( mFetcher.get(), &QgsImageFetcher::finish, this, &QgsWmsLegendNode::getLegendGraphicFinished );
1250 connect( mFetcher.get(), &QgsImageFetcher::error, this, &QgsWmsLegendNode::getLegendGraphicErrored );
1251 connect( mFetcher.get(), &QgsImageFetcher::progress, this, &QgsWmsLegendNode::getLegendGraphicProgress );
1252 mFetcher->start();
1253 }
1254 }
1255 else
1256 {
1257 QgsDebugMsg( tr( "Failed to download legend graphics: layer is not valid." ) );
1258 }
1259 }
1260
1261 return mImage;
1262}
1263
1264QVariant QgsWmsLegendNode::data( int role ) const
1265{
1266 if ( role == Qt::DecorationRole )
1267 {
1268 return QPixmap::fromImage( getLegendGraphic() );
1269 }
1270 else if ( role == Qt::SizeHintRole )
1271 {
1272 return getLegendGraphic().size();
1273 }
1275 {
1277 }
1278 return QVariant();
1279}
1280
1281QSizeF QgsWmsLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1282{
1283 Q_UNUSED( itemHeight )
1284
1285 const QImage image = getLegendGraphic();
1286
1287 double px2mm = 1000. / image.dotsPerMeterX();
1288 double mmWidth = image.width() * px2mm;
1289 double mmHeight = image.height() * px2mm;
1290
1291 QSize targetSize = QSize( mmWidth, mmHeight );
1292 if ( settings.wmsLegendSize().width() < mmWidth )
1293 {
1294 double targetHeight = mmHeight * settings.wmsLegendSize().width() / mmWidth;
1295 targetSize = QSize( settings.wmsLegendSize().width(), targetHeight );
1296 }
1297 else if ( settings.wmsLegendSize().height() < mmHeight )
1298 {
1299 double targetWidth = mmWidth * settings.wmsLegendSize().height() / mmHeight;
1300 targetSize = QSize( targetWidth, settings.wmsLegendSize().height() );
1301 }
1302
1303 if ( ctx && ctx->painter )
1304 {
1305 QImage smoothImage = image.scaled( targetSize / px2mm, Qt::KeepAspectRatio, Qt::SmoothTransformation );
1306
1307 switch ( settings.symbolAlignment() )
1308 {
1309 case Qt::AlignLeft:
1310 default:
1311 ctx->painter->drawImage( QRectF( ctx->columnLeft,
1312 ctx->top,
1313 targetSize.width(),
1314 targetSize.height() ),
1315 smoothImage,
1316 QRectF( QPointF( 0, 0 ), smoothImage.size() ) );
1317 break;
1318
1319 case Qt::AlignRight:
1320 ctx->painter->drawImage( QRectF( ctx->columnRight - settings.wmsLegendSize().width(),
1321 ctx->top,
1322 targetSize.width(),
1323 targetSize.height() ),
1324 smoothImage,
1325 QRectF( QPointF( 0, 0 ), smoothImage.size() ) );
1326 break;
1327 }
1328 }
1329 return targetSize;
1330}
1331
1333{
1334 QByteArray byteArray;
1335 QBuffer buffer( &byteArray );
1336 mImage.save( &buffer, "PNG" );
1337 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1338
1339 QJsonObject json;
1340 json[ QStringLiteral( "icon" ) ] = base64;
1341 return json;
1342}
1343
1344QImage QgsWmsLegendNode::renderMessage( const QString &msg ) const
1345{
1346 const int fontHeight = 10;
1347 const int margin = fontHeight / 2;
1348 const int nlines = 1;
1349
1350 const int w = 512, h = fontHeight * nlines + margin * ( nlines + 1 );
1351 QImage image( w, h, QImage::Format_ARGB32_Premultiplied );
1352 QPainter painter;
1353 painter.begin( &image );
1354 painter.setPen( QColor( 255, 0, 0 ) );
1355 painter.setFont( QFont( QStringLiteral( "Chicago" ), fontHeight ) );
1356 painter.fillRect( 0, 0, w, h, QColor( 255, 255, 255 ) );
1357 painter.drawText( 0, margin + fontHeight, msg );
1358 //painter.drawText(0,2*(margin+fontHeight),tr("retrying in 5 seconds…"));
1359 painter.end();
1360
1361 return image;
1362}
1363
1364void QgsWmsLegendNode::getLegendGraphicProgress( qint64 cur, qint64 tot )
1365{
1366 const QString msg = tot > 0 ? tr( "Downloading: %1% (%2)" ).arg( static_cast< int >( std::round( 100 * cur / tot ) ) ).arg( QgsFileUtils::representFileSize( tot ) )
1367 : tr( "Downloading: %1" ).arg( QgsFileUtils::representFileSize( cur ) );
1368 mImage = renderMessage( msg );
1369 emit dataChanged();
1370}
1371
1372void QgsWmsLegendNode::getLegendGraphicErrored( const QString & )
1373{
1374 if ( ! mFetcher )
1375 return; // must be coming after finish
1376
1377 mImage = QImage();
1378 emit dataChanged();
1379
1380 mFetcher.reset();
1381
1382 mValid = true; // we consider it valid anyway
1383}
1384
1385void QgsWmsLegendNode::getLegendGraphicFinished( const QImage &image )
1386{
1387 if ( ! mFetcher )
1388 return; // must be coming after error
1389
1390 if ( ! image.isNull() )
1391 {
1392 if ( image != mImage )
1393 {
1394 mImage = image;
1395 setUserPatchSize( mImage.size() );
1396 emit dataChanged();
1397 }
1398 mValid = true; // only if not null I guess
1399 }
1400 mFetcher.reset();
1401}
1402
1404{
1405 // TODO: do this only if this extent != prev extent ?
1406 mValid = false;
1407 emit dataChanged();
1408}
1409
1410// -------------------------------------------------------------------------
1411
1413 : QgsLayerTreeModelLegendNode( nodeLayer, parent )
1414 , mSettings( new QgsDataDefinedSizeLegend( settings ) )
1415{
1416}
1417
1422
1423QVariant QgsDataDefinedSizeLegendNode::data( int role ) const
1424{
1425 if ( role == Qt::DecorationRole )
1426 {
1427 cacheImage();
1428 return QPixmap::fromImage( mImage );
1429 }
1430 else if ( role == Qt::SizeHintRole )
1431 {
1432 cacheImage();
1433 return mImage.size();
1434 }
1436 {
1438 }
1439 return QVariant();
1440}
1441
1443{
1444 // setup temporary render context if none specified
1445 QgsRenderContext *context = nullptr;
1446 std::unique_ptr< QgsRenderContext > tempRenderContext;
1447 if ( ctx && ctx->context )
1448 context = ctx->context;
1449 else
1450 {
1451 tempRenderContext = std::make_unique< QgsRenderContext >();
1452 // QGIS 4.0 - make ItemContext compulsory, so we don't have to construct temporary render contexts here
1454 tempRenderContext->setScaleFactor( settings.dpi() / 25.4 );
1455 tempRenderContext->setRendererScale( settings.mapScale() );
1456 tempRenderContext->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
1457 tempRenderContext->setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * tempRenderContext->scaleFactor() ) ) );
1458 tempRenderContext->setForceVectorOutput( true );
1459 tempRenderContext->setPainter( ctx ? ctx->painter : nullptr );
1460 tempRenderContext->setFlag( Qgis::RenderContextFlag::Antialiasing, true );
1462
1463 // setup a minimal expression context
1464 QgsExpressionContext expContext;
1466 tempRenderContext->setExpressionContext( expContext );
1467 context = tempRenderContext.get();
1468 }
1469
1470 if ( context->painter() )
1471 {
1472 context->painter()->save();
1473 context->painter()->translate( ctx->columnLeft, ctx->top );
1474
1475 // scale to pixels
1476 context->painter()->scale( 1 / context->scaleFactor(), 1 / context->scaleFactor() );
1477 }
1478
1479 QgsDataDefinedSizeLegend ddsLegend( *mSettings );
1480 ddsLegend.setFont( settings.style( QgsLegendStyle::SymbolLabel ).font() );
1481 ddsLegend.setTextColor( settings.fontColor() );
1482
1483 QSizeF contentSize;
1484 double labelXOffset;
1485 ddsLegend.drawCollapsedLegend( *context, &contentSize, &labelXOffset );
1486
1487 if ( context->painter() )
1488 context->painter()->restore();
1489
1490 ItemMetrics im;
1491 im.symbolSize = QSizeF( ( contentSize.width() - labelXOffset ) / context->scaleFactor(), contentSize.height() / context->scaleFactor() );
1492 im.labelSize = QSizeF( labelXOffset / context->scaleFactor(), contentSize.height() / context->scaleFactor() );
1493 return im;
1494}
1495
1496
1497void QgsDataDefinedSizeLegendNode::cacheImage() const
1498{
1499 if ( mImage.isNull() )
1500 {
1501 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
1502 if ( !context )
1503 {
1504 context.reset( new QgsRenderContext );
1505 Q_ASSERT( context ); // to make cppcheck happy
1506 context->setScaleFactor( 96 / 25.4 );
1507 }
1508 mImage = mSettings->collapsedLegendImage( *context );
1509 }
1510}
1511
1512QgsVectorLabelLegendNode::QgsVectorLabelLegendNode( QgsLayerTreeLayer *nodeLayer, const QgsPalLayerSettings &labelSettings, QObject *parent ): QgsLayerTreeModelLegendNode( nodeLayer, parent ), mLabelSettings( labelSettings )
1513{
1514}
1515
1519
1520QVariant QgsVectorLabelLegendNode::data( int role ) const
1521{
1522 if ( role == Qt::DisplayRole )
1523 {
1524 return mUserLabel;
1525 }
1526 if ( role == Qt::DecorationRole )
1527 {
1528 const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 );
1529 return QgsPalLayerSettings::labelSettingsPreviewPixmap( mLabelSettings, QSize( iconSize, iconSize ), mLabelSettings.legendString() );
1530 }
1531 return QVariant();
1532}
1533
1534QSizeF QgsVectorLabelLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
1535{
1536 Q_UNUSED( itemHeight );
1537 if ( !ctx )
1538 {
1539 return QSizeF( 0, 0 );
1540 }
1541
1542 const QgsRenderContext *renderContext = ctx->context;
1543 if ( renderContext )
1544 {
1545 return drawSymbol( settings, *renderContext, ctx->columnLeft, ctx->top );
1546 }
1547
1548 return QSizeF( 0, 0 );
1549}
1550
1551QSizeF QgsVectorLabelLegendNode::drawSymbol( const QgsLegendSettings &settings, const QgsRenderContext &renderContext, double xOffset, double yOffset ) const
1552{
1553 const QStringList textLines( mLabelSettings.legendString() );
1554 const QgsTextFormat textFormat = mLabelSettings.format();
1555 QgsRenderContext ctx( renderContext );
1556 double textWidth, textHeight;
1557 textWidthHeight( textWidth, textHeight, ctx, textFormat, textLines );
1558 textWidth /= renderContext.scaleFactor();
1559 textHeight /= renderContext.scaleFactor();
1560 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 ) );
1561
1562 const QgsScopedRenderContextScaleToPixels scopedScaleToPixels( ctx );
1563 QgsTextRenderer::drawText( textPos, 0.0, Qgis::TextHorizontalAlignment::Left, textLines, ctx, textFormat );
1564
1565 const double symbolWidth = std::max( textWidth, settings.symbolSize().width() );
1566 const double symbolHeight = std::max( textHeight, settings.symbolSize().height() );
1567 return QSizeF( symbolWidth, symbolHeight );
1568}
1569
1570QJsonObject QgsVectorLabelLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const
1571{
1572 Q_UNUSED( settings );
1573
1574 const double mmToPixel = 96.0 / 25.4; //settings.dpi() is deprecated
1575
1576 const QStringList textLines( mLabelSettings.legendString() );
1577 const QgsTextFormat textFormat = mLabelSettings.format();
1578 QgsRenderContext ctx( context );
1579 ctx.setScaleFactor( mmToPixel );
1580
1581 double textWidth, textHeight;
1582 textWidthHeight( textWidth, textHeight, ctx, textFormat, textLines );
1583 const QPixmap previewPixmap = QgsPalLayerSettings::labelSettingsPreviewPixmap( mLabelSettings, QSize( textWidth, textHeight ), mLabelSettings.legendString() );
1584
1585 QByteArray byteArray;
1586 QBuffer buffer( &byteArray );
1587 previewPixmap.save( &buffer, "PNG" );
1588 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1589
1590 QJsonObject json;
1591 json[ QStringLiteral( "icon" ) ] = base64;
1592 return json;
1593}
1594
1595void QgsVectorLabelLegendNode::textWidthHeight( double &width, double &height, QgsRenderContext &ctx, const QgsTextFormat &textFormat, const QStringList &textLines ) const
1596{
1597 QFontMetricsF fm = QgsTextRenderer::fontMetrics( ctx, textFormat );
1598 height = QgsTextRenderer::textHeight( ctx, textFormat, 'A', true );
1599 width = QgsTextRenderer::textWidth( ctx, textFormat, textLines, &fm );
1600}
@ Point
Text at point of origin layout mode.
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
@ LosslessImageRendering
Render images losslessly whenever possible, instead of the default lossy jpeg rendering used for some...
@ Antialiasing
Use antialiasing while drawing.
@ UseAdvancedEffects
Enable layer opacity and blending effects.
@ Marker
Marker symbol.
@ Line
Line symbol.
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.
QImage collapsedLegendImage(QgsRenderContext &context, const QColor &backgroundColor=Qt::transparent, double paddingMM=1) const
Returns output image that would be shown in the legend. Returns invalid image if legend is not config...
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.
Class for storing the component parts of a RDBMS 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)
item in symbology was 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.
QgsLegendPatchShape patchShape() const
Returns the symbol patch shape to use when rendering the legend node symbol.
QString name() const override
Returns the layer's name.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
QSizeF patchSize() const
Returns the user (overridden) size for the legend node.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
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.
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.
@ ParentRuleKeyRole
Rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2....
@ RuleKeyRole
Rule key of the node (QString)
@ NodeTypeRole
Type of node. Added in 3.16.
void sizeChanged()
Emitted when the size of this node changes.
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 ItemMetrics draw(const QgsLegendSettings &settings, ItemContext *ctx)
Entry point called from QgsLegendRenderer to do the rendering.
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.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
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.
The QgsLegendSettings class stores the appearance and layout settings for legend drawing with QgsLege...
void drawText(QPainter *p, double x, double y, const QString &text, const QFont &font) const
Draws Text.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns the style for a legend component.
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 rasterStrokeWidth() const
Returns the stroke width (in millimeters) for the stroke drawn around raster symbol items.
double fontDescentMillimeters(const QFont &font) const
Returns the font descent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCA...
QSizeF symbolSize() const
Returns the default symbol size (in millimeters) used for legend items.
QColor fontColor() const
Returns the font color 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.
double textWidthMillimeters(const QFont &font, const QString &text) const
Returns the font width in millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE...
Q_DECL_DEPRECATED bool useAdvancedEffects() const
double fontHeightCharacterMM(const QFont &font, QChar c) const
Returns the font height of a character in millimeters.
Q_DECL_DEPRECATED int dpi() const
double lineSpacing() const
Returns the line spacing to use between lines of legend text.
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.
double margin(Side side)
Returns the margin (in mm) for the specified side of the component.
Qt::Alignment alignment() const
Returns the alignment for the legend component.
QFont font() const
Returns the font used for rendering this legend component.
@ Right
Right side.
@ Left
Left side.
@ Symbol
Symbol icon (excluding label)
@ SymbolLabel
Symbol label (excluding icon)
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
QString parentRuleKey() const
Key of the parent legend node.
int scaleMaxDenom() const
Max scale denominator of the scale range.
void setSymbol(QgsSymbol *s)
Sets the symbol of the item.
int scaleMinDenom() const
Min scale denominator of the scale range.
QgsSymbol * symbol() const
Returns associated symbol. May be nullptr.
QString ruleKey() const
Returns unique identifier of the rule for identification of the item within renderer.
bool isCheckable() const
Returns whether the item is user-checkable - whether renderer supports enabling/disabling it.
QString label() const
Returns text label.
Base class for all map layer types.
Definition qgsmaplayer.h:73
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.
The QgsMapSettings class contains configuration for rendering of the map.
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)
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.
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.
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())
Constructor for QgsRasterSymbolLegendNode.
QVariant data(int role) const override
Returns data associated with the item. Must be implemented in derived class.
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.
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.
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...
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
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.
This class is a composition of two QSettings instances:
Definition qgssettings.h:62
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 QPixmap symbolPreviewPixmap(const QgsSymbol *symbol, QSize size, int padding=0, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *shape=nullptr)
Returns a pixmap preview for a color ramp.
static 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 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.
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.
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:93
void drawPreviewIcon(QPainter *painter, QSize size, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *patchShape=nullptr)
Draws an icon of the symbol that occupies an area given by size using the specified painter.
bool usesMapUnits() const
Returns true if the symbol has any components which use map unit based sizes.
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:152
Container for all settings relating to text rendering.
QFont scaledFont(const QgsRenderContext &context, double scaleFactor=1.0, bool *isZeroSize=nullptr) const
Returns a font with the size scaled to match the format's size settings (including units and map unit...
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 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 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 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())
Draws text within a rectangle using the specified settings.
static bool isNull(const QVariant &variant)
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 data sets.
QgsExpressionContextScope * createExpressionContextScope() const FINAL
This method needs to be reimplemented in all classes which implement this interface and return an exp...
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.
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.
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:3061
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:3060
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:2527
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsg(str)
Definition qgslogger.h:38
Single variable definition for use within a QgsExpressionContextScope.
QgsLegendPatchShape patchShape
The patch shape to render for the node.
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.
double columnLeft
Left side of current legend column.
double columnRight
Right side of current legend column.
Q_NOWARN_DEPRECATED_POP QgsRenderContext * context
Render context, if available.