48 , mEmbeddedInParent( false )
54 return qobject_cast<QgsLayerTreeModel *>( parent() );
59 return Qt::ItemIsEnabled;
94 const double itemHeight = std::max(
static_cast< double >( ctx && ctx->
patchSize.height() > 0 ? ctx->
patchSize.height() : settings.
symbolSize().height() ), textHeight );
105 const QString text =
data( Qt::DisplayRole ).toString();
106 json[ QStringLiteral(
"title" ) ] = text;
112 const QIcon symbolIcon =
data( Qt::DecorationRole ).value<QIcon>();
113 if ( symbolIcon.isNull() )
120 size.setWidth( ctx->
patchSize.width( ) );
122 size.setHeight( ctx->
patchSize.height( ) );
131 symbolIcon.paint( ctx->
painter,
133 static_cast< int >( ctx->
top + ( itemHeight - size.height() ) / 2 ),
134 static_cast< int >( size.width() ),
135 static_cast< int >( size.height() ) );
139 symbolIcon.paint( ctx->
painter,
140 static_cast< int >( ctx->
columnRight - size.width() ),
141 static_cast< int >( ctx->
top + ( itemHeight - size.height() ) / 2 ),
142 static_cast< int >( size.width() ),
143 static_cast< int >( size.height() ) );
152 const QIcon icon =
data( Qt::DecorationRole ).value<QIcon>();
154 return QJsonObject();
156 const QImage image( icon.pixmap( settings.
symbolSize().width(), settings.
symbolSize().height() ).toImage() );
157 QByteArray byteArray;
158 QBuffer buffer( &byteArray );
159 image.save( &buffer,
"PNG" );
160 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
163 json[ QStringLiteral(
"icon" ) ] = base64;
169 QSizeF labelSize( 0, 0 );
179 labelSize.rheight() = lines.count() * textHeight + ( lines.count() - 1 ) * ( settings.
lineSpacing() + textDescent );
181 double labelXMin = 0.0;
182 double labelXMax = 0.0;
211 if ( labelSize.height() < symbolSize.height() )
212 labelY += symbolSize.height() / 2 - labelSize.height() / 2;
214 labelY += textHeight;
217 for ( QStringList::ConstIterator itemPart = lines.constBegin(); itemPart != lines.constEnd(); ++itemPart )
220 labelSize.rwidth() = std::max( lineWidth,
double( labelSize.width() ) );
228 settings.
drawText( ctx->
painter, labelXMin, labelY, *itemPart, symbolLabelFont );
232 settings.
drawText( ctx->
painter, labelXMax - lineWidth, labelY, *itemPart, symbolLabelFont );
235 case Qt::AlignHCenter:
236 settings.
drawText( ctx->
painter, labelXMin + ( labelXMax - labelXMin - lineWidth ) / 2.0, labelY, *itemPart, symbolLabelFont );
240 if ( itemPart != ( lines.end() - 1 ) )
241 labelY += textDescent + settings.
lineSpacing() + textHeight;
262 if ( !vlayer->renderer() )
266 for (
const auto &item : symbolList )
268 vlayer->renderer()->checkLegendSymbolItem( item.ruleKey(), ! vlayer->renderer()->legendSymbolItemChecked( item.ruleKey() ) );
272 vlayer->emitStyleChanged();
273 vlayer->triggerRepaint();
277 if ( !pclayer->renderer() )
280 const QStringList ruleKeys = pclayer->renderer()->legendRuleKeys();
281 for (
const QString &rule : ruleKeys )
283 pclayer->renderer()->checkLegendItem( rule, !pclayer->renderer()->legendItemChecked( rule ) );
287 pclayer->emitStyleChanged();
288 pclayer->triggerRepaint();
300 , mSymbolUsesMapUnits( false )
310 MINIMUM_SIZE = settings.
value(
"/qgis/legendsymbolMinimumSize", 0.5 ).toDouble();
311 MAXIMUM_SIZE = settings.
value(
"/qgis/legendsymbolMaximumSize", 20.0 ).toDouble();
318 connect( nodeLayer, &QObject::destroyed,
this, [ = ]() {
mLayerNode =
nullptr; } );
329 return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
331 return Qt::ItemIsEnabled;
370 if ( !mTextOnSymbolLabel.isEmpty() && context )
374 int wInt = ceil( w ), hInt = ceil( h );
375 if ( wInt > minSz.width() ) minSz.setWidth( wInt );
376 if ( hInt > minSz.height() ) minSz.setHeight( hInt );
393 const QString layerName = legendlabel.isNull() ?
mLayerNode->
name() : legendlabel.toString();
420 return mCustomSymbol.get();
425 mCustomSymbol.reset(
symbol );
433 std::unique_ptr< QgsSymbol > s(
symbol );
435 if ( !vlayer || !vlayer->
renderer() )
452 if (
auto *lModel =
model() )
453 lModel->legendMapViewData( &mupp, &dpi, &scale );
459 std::unique_ptr<QgsRenderContext> context = std::make_unique<QgsRenderContext>( );
460 context->setScaleFactor( dpi / 25.4 );
461 context->setRendererScale( scale );
465 return context.release();
468 void QgsLayerTreeModelLegendNode::checkAll(
bool state )
472 if ( !vlayer->renderer() )
476 for (
const auto &item : symbolList )
478 vlayer->renderer()->checkLegendSymbolItem( item.ruleKey(), state );
482 vlayer->emitStyleChanged();
483 vlayer->triggerRepaint();
487 if ( !pclayer->renderer() )
490 const QStringList ruleKeys = pclayer->renderer()->legendRuleKeys();
491 for (
const QString &rule : ruleKeys )
493 pclayer->renderer()->checkLegendItem( rule, state );
497 pclayer->emitStyleChanged();
498 pclayer->triggerRepaint();
504 if ( role == Qt::DisplayRole )
508 else if ( role == Qt::EditRole )
512 else if ( role == Qt::DecorationRole )
514 if ( mPixmap.isNull() || mPixmap.size() != mIconSize )
527 if ( !mTextOnSymbolLabel.isEmpty() && context )
529 QPainter painter( &pix );
530 painter.setRenderHint( QPainter::Antialiasing );
531 context->setPainter( &painter );
532 bool isNullSize =
false;
533 const QFontMetricsF fm( mTextOnSymbolTextFormat.
scaledFont( *context, 1.0, &isNullSize ) );
536 const qreal yBaselineVCenter = ( mIconSize.height() + fm.ascent() - fm.descent() ) / 2;
538 QStringList() << mTextOnSymbolLabel, *context, mTextOnSymbolTextFormat );
544 pix = QPixmap( mIconSize );
545 pix.fill( Qt::transparent );
552 else if ( role == Qt::CheckStateRole )
559 if ( !vlayer->renderer() )
562 return vlayer->renderer()->legendSymbolItemChecked( mItem.
ruleKey() ) ? Qt::Checked : Qt::Unchecked;
583 if ( role != Qt::CheckStateRole )
590 if ( !vlayer || !vlayer->
renderer() )
615 std::unique_ptr< QgsRenderContext > tempRenderContext;
621 tempRenderContext = std::make_unique< QgsRenderContext >();
624 tempRenderContext->setScaleFactor( settings.
dpi() / 25.4 );
625 tempRenderContext->setRendererScale( settings.
mapScale() );
629 tempRenderContext->setForceVectorOutput(
true );
630 tempRenderContext->setPainter( ctx ? ctx->
painter :
nullptr );
635 tempRenderContext->setExpressionContext( expContext );
636 context = tempRenderContext.get();
642 double height = desiredHeight;
643 double width = desiredWidth;
646 double widthOffset = 0;
647 double heightOffset = 0;
654 const double size = markerSymbol->size( *context ) / context->
scaleFactor();
660 if ( minMaxSizeSymbol )
662 s = minMaxSizeSymbol.get();
667 if ( width < desiredWidth )
669 widthOffset = ( desiredWidth - width ) / 2.0;
671 if ( height < desiredHeight )
673 heightOffset = ( desiredHeight - height ) / 2.0;
678 const double currentYCoord = ctx->
top + ( itemHeight - desiredHeight ) / 2;
686 opacity =
static_cast<int >( std::round( 255 * layer->opacity() ) );
695 p->translate( ctx->
columnLeft + widthOffset, currentYCoord + heightOffset );
698 p->translate( ctx->
columnRight - widthOffset - width, currentYCoord + heightOffset );
702 p->scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
707 if ( opacity != 255 && useAdvancedEffects )
713 const QSize symbolSize(
static_cast< int >( std::round( width * dotsPerMM ) ),
static_cast<int >( std::round( height * dotsPerMM ) ) );
714 const QSize tempImageSize( symbolSize.width() + maxBleed * 2, symbolSize.height() + maxBleed * 2 );
715 QImage tempImage = QImage( tempImageSize, QImage::Format_ARGB32 );
716 tempImage.fill( Qt::transparent );
717 QPainter imagePainter( &tempImage );
721 imagePainter.translate( maxBleed, maxBleed );
723 imagePainter.translate( -maxBleed, -maxBleed );
726 imagePainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
727 imagePainter.fillRect( tempImage.rect(), QColor( 0, 0, 0, opacity ) );
730 p->drawImage( -maxBleed, -maxBleed, tempImage );
734 s->
drawPreviewIcon( p, QSize(
static_cast< int >( std::round( width * dotsPerMM ) ),
static_cast< int >( std::round( height * dotsPerMM ) ) ), context,
false,
nullptr, &
patchShape );
737 if ( !mTextOnSymbolLabel.isEmpty() )
739 bool isNullSize =
false;
740 const QFontMetricsF fm( mTextOnSymbolTextFormat.
scaledFont( *context, 1.0, &isNullSize ) );
743 const qreal yBaselineVCenter = ( height * dotsPerMM + fm.ascent() - fm.descent() ) / 2;
745 QStringList() << mTextOnSymbolLabel, *context, mTextOnSymbolTextFormat );
750 return QSizeF( std::max( width + 2 * widthOffset,
static_cast< double >( desiredWidth ) ),
751 std::max( height + 2 * heightOffset,
static_cast< double >( desiredHeight ) ) );
756 const QgsSymbol *s = mCustomSymbol ? mCustomSymbol.get() : mItem.
symbol();
759 return QJsonObject();
781 QImage img( pix.toImage().convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
785 opacity = ( 255 * layer->opacity() );
787 if ( opacity != 255 )
790 painter.begin( &img );
791 painter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
792 painter.fillRect( pix.rect(), QColor( 0, 0, 0, opacity ) );
796 QByteArray byteArray;
797 QBuffer buffer( &byteArray );
798 img.save( &buffer,
"PNG" );
799 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
802 json[ QStringLiteral(
"icon" ) ] = base64;
805 json[ QStringLiteral(
"scaleMaxDenom" ) ] = mItem.
scaleMaxDenom();
809 json[ QStringLiteral(
"scaleMinDenom" ) ] = mItem.
scaleMinDenom();
824 if ( mSymbolUsesMapUnits )
832 void QgsSymbolLegendNode::updateLabel()
841 if ( showFeatureCount && vl )
844 mLabel += QStringLiteral(
" [%1]" ).arg( count != -1 ? QLocale().toString( count ) : tr(
"N/A" ) );
864 if ( label.isEmpty() )
868 else if ( mLabel.contains(
"[%" ) )
880 else if ( label.contains(
"[%" ) )
915 if ( role == Qt::DisplayRole || role == Qt::EditRole )
917 else if ( role == Qt::DecorationRole )
938 if ( role == Qt::DecorationRole )
940 return QPixmap::fromImage( mImage );
942 else if ( role == Qt::SizeHintRole )
944 return mImage.size();
955 Q_UNUSED( itemHeight )
961 const double imgWidth = settings.
wmsLegendSize().width() * scaleFactor;
962 const double imgHeight = settings.
wmsLegendSize().height() * scaleFactor;
964 const QImage scaledImg = mImage.scaled( QSizeF( imgWidth, imgHeight ).toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation );
969 ctx->
painter->drawImage( QPointF( ctx->
columnLeft * scaleFactor, ctx->
top * scaleFactor ), scaledImg );
973 ctx->
painter->drawImage( QPointF( ctx->
columnRight * scaleFactor - imgWidth, ctx->
top * scaleFactor ), scaledImg );
982 QByteArray byteArray;
983 QBuffer buffer( &byteArray );
984 mImage.save( &buffer,
"PNG" );
985 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
988 json[ QStringLiteral(
"icon" ) ] = base64;
998 , mCheckable( isCheckable )
999 , mRuleKey( ruleKey )
1006 return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
1008 return Qt::ItemIsEnabled;
1015 case Qt::DecorationRole:
1020 return QIcon( pix );
1023 case Qt::DisplayRole:
1033 case Qt::CheckStateRole:
1040 if ( !pclayer->renderer() )
1043 return pclayer->renderer()->legendItemChecked( mRuleKey ) ? Qt::Checked : Qt::Unchecked;
1056 if ( role != Qt::CheckStateRole )
1064 if ( !pclayer->renderer() )
1067 pclayer->renderer()->checkLegendItem( mRuleKey, value == Qt::Checked );
1070 pclayer->emitStyleChanged();
1072 pclayer->triggerRepaint();
1090 if ( ctx->
patchSize.width() < size.width() )
1091 offsetX = ( size.width() - ctx->
patchSize.width() ) / 2.0;
1092 size.setWidth( ctx->
patchSize.width() );
1096 size.setHeight( ctx->
patchSize.height() );
1102 QColor itemColor = mColor;
1106 itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
1108 ctx->
painter->setBrush( itemColor );
1115 pen.setJoinStyle( Qt::MiterJoin );
1120 ctx->
painter->setPen( Qt::NoPen );
1127 ctx->
painter->drawRect( QRectF( ctx->
columnLeft + offsetX, ctx->
top + ( itemHeight - size.height() ) / 2,
1128 size.width(), size.height() ) );
1131 case Qt::AlignRight:
1132 ctx->
painter->drawRect( QRectF( ctx->
columnRight - size.width() - offsetX, ctx->
top + ( itemHeight - size.height() ) / 2,
1133 size.width(), size.height() ) );
1142 QImage img = QImage( settings.
symbolSize().toSize(), QImage::Format_ARGB32 );
1143 img.fill( Qt::transparent );
1145 QPainter painter( &img );
1146 painter.setRenderHint( QPainter::Antialiasing );
1148 QColor itemColor = mColor;
1152 itemColor.setAlpha( rasterRenderer->opacity() * 255.0 );
1154 painter.setBrush( itemColor );
1161 pen.setJoinStyle( Qt::MiterJoin );
1162 painter.setPen( pen );
1166 painter.setPen( Qt::NoPen );
1169 painter.drawRect( QRectF( 0, 0, settings.
symbolSize().width(), settings.
symbolSize().height() ) );
1171 QByteArray byteArray;
1172 QBuffer buffer( &byteArray );
1173 img.save( &buffer,
"PNG" );
1174 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1177 json[ QStringLiteral(
"icon" ) ] = base64;
1191 QImage QgsWmsLegendNode::getLegendGraphic()
const
1193 if ( ! mValid && ! mFetcher )
1200 if ( layer && layer->
isValid() )
1211 Q_ASSERT( ! mFetcher );
1223 QgsDebugMsg( tr(
"Failed to download legend graphics: layer is not valid." ) );
1232 if ( role == Qt::DecorationRole )
1234 return QPixmap::fromImage( getLegendGraphic() );
1236 else if ( role == Qt::SizeHintRole )
1238 return getLegendGraphic().size();
1249 Q_UNUSED( itemHeight )
1251 const QImage image = getLegendGraphic();
1253 double px2mm = 1000. / image.dotsPerMeterX();
1254 double mmWidth = image.width() * px2mm;
1255 double mmHeight = image.height() * px2mm;
1257 QSize targetSize = QSize( mmWidth, mmHeight );
1260 double targetHeight = mmHeight * settings.
wmsLegendSize().width() / mmWidth;
1261 targetSize = QSize( settings.
wmsLegendSize().width(), targetHeight );
1265 double targetWidth = mmWidth * settings.
wmsLegendSize().height() / mmHeight;
1266 targetSize = QSize( targetWidth, settings.
wmsLegendSize().height() );
1271 QImage smoothImage = image.scaled( targetSize / px2mm, Qt::KeepAspectRatio, Qt::SmoothTransformation );
1280 targetSize.height() ),
1282 QRectF( QPointF( 0, 0 ), smoothImage.size() ) );
1285 case Qt::AlignRight:
1289 targetSize.height() ),
1291 QRectF( QPointF( 0, 0 ), smoothImage.size() ) );
1300 QByteArray byteArray;
1301 QBuffer buffer( &byteArray );
1302 mImage.save( &buffer,
"PNG" );
1303 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1306 json[ QStringLiteral(
"icon" ) ] = base64;
1310 QImage QgsWmsLegendNode::renderMessage(
const QString &msg )
const
1312 const int fontHeight = 10;
1313 const int margin = fontHeight / 2;
1314 const int nlines = 1;
1316 const int w = 512, h = fontHeight * nlines + margin * ( nlines + 1 );
1317 QImage image( w, h, QImage::Format_ARGB32_Premultiplied );
1319 painter.begin( &image );
1320 painter.setPen( QColor( 255, 0, 0 ) );
1321 painter.setFont( QFont( QStringLiteral(
"Chicago" ), fontHeight ) );
1322 painter.fillRect( 0, 0, w, h, QColor( 255, 255, 255 ) );
1323 painter.drawText( 0, margin + fontHeight, msg );
1330 void QgsWmsLegendNode::getLegendGraphicProgress( qint64 cur, qint64 tot )
1332 const QString msg = tot > 0 ? tr(
"Downloading: %1% (%2)" ).arg(
static_cast< int >( std::round( 100 * cur / tot ) ) ).arg(
QgsFileUtils::representFileSize( tot ) )
1334 mImage = renderMessage( msg );
1338 void QgsWmsLegendNode::getLegendGraphicErrored(
const QString & )
1351 void QgsWmsLegendNode::getLegendGraphicFinished(
const QImage &image )
1356 if ( ! image.isNull() )
1358 if ( image != mImage )
1391 if ( role == Qt::DecorationRole )
1394 return QPixmap::fromImage( mImage );
1396 else if ( role == Qt::SizeHintRole )
1399 return mImage.size();
1412 std::unique_ptr< QgsRenderContext > tempRenderContext;
1417 tempRenderContext = std::make_unique< QgsRenderContext >();
1420 tempRenderContext->setScaleFactor( settings.
dpi() / 25.4 );
1421 tempRenderContext->setRendererScale( settings.
mapScale() );
1423 tempRenderContext->setMapToPixel(
QgsMapToPixel( 1 / ( settings.
mmPerMapUnit() * tempRenderContext->scaleFactor() ) ) );
1424 tempRenderContext->setForceVectorOutput(
true );
1425 tempRenderContext->setPainter( ctx ? ctx->
painter :
nullptr );
1432 tempRenderContext->setExpressionContext( expContext );
1433 context = tempRenderContext.get();
1450 double labelXOffset;
1454 context->
painter()->restore();
1463 void QgsDataDefinedSizeLegendNode::cacheImage()
const
1465 if ( mImage.isNull() )
1471 Q_ASSERT( context );
1472 context->setScaleFactor( 96 / 25.4 );
1488 if ( role == Qt::DisplayRole )
1492 if ( role == Qt::DecorationRole )
1502 Q_UNUSED( itemHeight );
1505 return QSizeF( 0, 0 );
1509 if ( renderContext )
1514 return QSizeF( 0, 0 );
1519 const QStringList textLines( mLabelSettings.
legendString() );
1522 double textWidth, textHeight;
1523 textWidthHeight( textWidth, textHeight, ctx, textFormat, textLines );
1526 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 ) );
1531 const double symbolWidth = std::max( textWidth, settings.
symbolSize().width() );
1532 const double symbolHeight = std::max( textHeight, settings.
symbolSize().height() );
1533 return QSizeF( symbolWidth, symbolHeight );
1538 Q_UNUSED( settings );
1540 const double mmToPixel = 96.0 / 25.4;
1542 const QStringList textLines( mLabelSettings.
legendString() );
1547 double textWidth, textHeight;
1548 textWidthHeight( textWidth, textHeight, ctx, textFormat, textLines );
1551 QByteArray byteArray;
1552 QBuffer buffer( &byteArray );
1553 previewPixmap.save( &buffer,
"PNG" );
1554 const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
1557 json[ QStringLiteral(
"icon" ) ] = base64;
1561 void QgsVectorLabelLegendNode::textWidthHeight(
double &width,
double &height,
QgsRenderContext &ctx,
const QgsTextFormat &textFormat,
const QStringList &textLines )
const