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