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