28 #include <QJsonObject> 34 : mLegendModel( legendModel )
35 , mSettings( settings )
41 return paintAndDetermineSize( renderContext );
46 paintAndDetermineSize( painter );
55 json[
"title"] = mSettings.
title();
62 for (
auto node : nodeGroup->
children() )
67 const QModelIndex idx = mLegendModel->
node2index( nodeGroup );
68 const QString text = mLegendModel->
data( idx, Qt::DisplayRole ).toString();
71 group[
"type" ] =
"group";
72 group[
"title" ] = text;
74 nodes.append( group );
79 group[
"type" ] =
"layer";
86 const QModelIndex idx = mLegendModel->
node2index( nodeLayer );
87 text = mLegendModel->
data( idx, Qt::DisplayRole ).toString();
90 QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->
layerLegendNodes( nodeLayer );
95 if ( legendNodes.count() == 1 )
97 legendNodes.at( 0 )->exportToJson( mSettings, context, group );
98 nodes.append( group );
100 else if ( legendNodes.count() > 1 )
103 for (
int j = 0; j < legendNodes.count(); j++ )
108 symbols.append( symbol );
110 group[
"title" ] = text;
111 group[
"symbols" ] = symbols;
112 nodes.append( group );
117 json[
"nodes"] = nodes;
120 QSizeF QgsLegendRenderer::paintAndDetermineSize( QPainter *painter )
122 return paintAndDetermineSizeInternal(
nullptr, painter );
125 QSizeF QgsLegendRenderer::paintAndDetermineSizeInternal(
QgsRenderContext *context, QPainter *painter )
132 QList<Atom> atomList = createAtomList( rootGroup, mSettings.
splitLayer() );
134 setColumns( atomList );
136 qreal maxColumnWidth = 0;
139 const auto constAtomList = atomList;
140 for (
const Atom &atom : constAtomList )
142 maxColumnWidth = std::max( atom.size.width(), maxColumnWidth );
147 QSizeF titleSize = drawTitle();
149 titleSize.rwidth() += mSettings.
boxSpace() * 2.0;
152 QPointF point( mSettings.
boxSpace(), columnTop );
153 bool firstInColumn =
true;
154 double columnMaxHeight = 0;
155 qreal columnWidth = 0;
157 const auto constAtomList = atomList;
158 for (
const Atom &atom : constAtomList )
160 if ( atom.column > column )
165 point.rx() += mSettings.
columnSpace() + maxColumnWidth;
169 point.rx() += mSettings.
columnSpace() + columnWidth;
171 point.ry() = columnTop;
174 firstInColumn =
true;
176 if ( !firstInColumn )
178 point.ry() += spaceAboveAtom( atom );
181 QSizeF atomSize = context ? drawAtom( atom, context, point )
182 : drawAtom( atom, painter, point );
183 columnWidth = std::max( atomSize.width(), columnWidth );
185 point.ry() += atom.size.height();
186 columnMaxHeight = std::max( point.y() - columnTop, columnMaxHeight );
188 firstInColumn =
false;
190 point.rx() += columnWidth + mSettings.
boxSpace();
192 size.rheight() = columnTop + columnMaxHeight + mSettings.
boxSpace();
193 size.rwidth() = point.x();
194 if ( !mSettings.
title().isEmpty() )
196 size.rwidth() = std::max( titleSize.width(), size.width() );
200 if ( mLegendSize.isValid() )
202 qreal w = std::max( size.width(), mLegendSize.width() );
203 qreal h = std::max( size.height(), mLegendSize.height() );
204 size = QSizeF( w, h );
208 if ( !mSettings.
title().isEmpty() )
216 point.rx() = size.width() / 2;
220 point.rx() = size.width() - mSettings.
boxSpace();
224 drawTitle( context, point, mSettings.
titleAlignment(), size.width() );
226 drawTitle( painter, point, mSettings.
titleAlignment(), size.width() );
233 QList<QgsLegendRenderer::Atom> QgsLegendRenderer::createAtomList(
QgsLayerTreeGroup *parentGroup,
bool splitLayer )
237 if ( !parentGroup )
return atoms;
239 const auto constChildren = parentGroup->
children();
247 QList<Atom> groupAtoms = createAtomList( nodeGroup, splitLayer );
248 bool hasSubItems = !groupAtoms.empty();
254 nucleon.size = drawGroupTitle( nodeGroup );
256 if ( !groupAtoms.isEmpty() )
259 groupAtoms[0].size.rheight() += spaceAboveAtom( groupAtoms[0] );
261 groupAtoms[0].nucleons.prepend( nucleon );
262 groupAtoms[0].size.rheight() += nucleon.size.height();
263 groupAtoms[0].size.rwidth() = std::max( nucleon.size.width(), groupAtoms[0].size.width() );
269 atom.nucleons.append( nucleon );
270 atom.size.rwidth() += nucleon.size.width();
271 atom.size.rheight() += nucleon.size.height();
272 atom.size.rwidth() = std::max( nucleon.size.width(), atom.size.width() );
273 groupAtoms.append( atom );
279 atoms.append( groupAtoms );
293 nucleon.size = drawLayerTitle( nodeLayer );
294 atom.nucleons.append( nucleon );
295 atom.size.rwidth() = nucleon.size.width();
296 atom.size.rheight() = nucleon.size.height();
299 QList<QgsLayerTreeModelLegendNode *> legendNodes = mLegendModel->
layerLegendNodes( nodeLayer );
307 QList<Atom> layerAtoms;
309 for (
int j = 0; j < legendNodes.count(); j++ )
313 Nucleon symbolNucleon = drawSymbolItem( legendNode );
319 atom.size.rwidth() = std::max( symbolNucleon.size.width(), atom.size.width() );
321 if ( !atom.nucleons.isEmpty() )
326 atom.size.rheight() += symbolNucleon.size.height();
327 atom.nucleons.append( symbolNucleon );
332 symbolAtom.nucleons.append( symbolNucleon );
333 symbolAtom.size.rwidth() = symbolNucleon.size.width();
334 symbolAtom.size.rheight() = symbolNucleon.size.height();
335 layerAtoms.append( symbolAtom );
338 layerAtoms.prepend( atom );
339 atoms.append( layerAtoms );
347 void QgsLegendRenderer::setColumns( QList<Atom> &atomList )
352 double totalHeight = 0;
353 qreal maxAtomHeight = 0;
354 const auto constAtomList = atomList;
355 for (
const Atom &atom : constAtomList )
357 totalHeight += spaceAboveAtom( atom );
358 totalHeight += atom.size.height();
359 maxAtomHeight = std::max( atom.size.height(), maxAtomHeight );
367 double maxColumnHeight = 0;
368 int currentColumn = 0;
369 int currentColumnAtomCount = 0;
370 double currentColumnHeight = 0;
371 double closedColumnsHeight = 0;
373 for (
int i = 0; i < atomList.size(); i++ )
376 double avgColumnHeight = ( totalHeight - closedColumnsHeight ) / ( mSettings.
columnCount() - currentColumn );
378 Atom atom = atomList.at( i );
379 double currentHeight = currentColumnHeight;
380 if ( currentColumnAtomCount > 0 )
381 currentHeight += spaceAboveAtom( atom );
382 currentHeight += atom.size.height();
384 bool canCreateNewColumn = ( currentColumnAtomCount > 0 )
385 && ( currentColumn < mSettings.
columnCount() - 1 );
387 bool shouldCreateNewColumn = ( currentHeight - avgColumnHeight ) > atom.size.height() / 2
388 && currentColumnAtomCount > 0
389 && currentHeight > maxAtomHeight
390 && currentHeight > maxColumnHeight;
394 shouldCreateNewColumn |= ( atomList.size() - i < mSettings.
columnCount() - currentColumn );
396 if ( canCreateNewColumn && shouldCreateNewColumn )
400 currentColumnAtomCount = 0;
401 closedColumnsHeight += currentColumnHeight;
402 currentColumnHeight = atom.size.height();
406 currentColumnHeight = currentHeight;
408 atomList[i].column = currentColumn;
409 currentColumnAtomCount++;
410 maxColumnHeight = std::max( currentColumnHeight, maxColumnHeight );
414 QMap<QString, qreal> maxSymbolWidth;
415 for (
int i = 0; i < atomList.size(); i++ )
417 Atom &atom = atomList[i];
418 for (
int j = 0; j < atom.nucleons.size(); j++ )
422 QString key = QStringLiteral(
"%1-%2" ).arg( reinterpret_cast< qulonglong >(
legendNode->
layerNode() ) ).arg( atom.column );
423 maxSymbolWidth[key] = std::max( atom.nucleons.at( j ).symbolSize.width(), maxSymbolWidth[key] );
427 for (
int i = 0; i < atomList.size(); i++ )
429 Atom &atom = atomList[i];
430 for (
int j = 0; j < atom.nucleons.size(); j++ )
434 QString key = QStringLiteral(
"%1-%2" ).arg( reinterpret_cast< qulonglong >(
legendNode->
layerNode() ) ).arg( atom.column );
437 atom.nucleons[j].labelXOffset = maxSymbolWidth[key] + space;
438 atom.nucleons[j].size.rwidth() = maxSymbolWidth[key] + space + atom.nucleons.at( j ).labelSize.width();
444 QSizeF QgsLegendRenderer::drawTitle( QPainter *painter, QPointF point, Qt::AlignmentFlag halignment,
double legendWidth )
446 return drawTitleInternal(
nullptr, painter, point, halignment, legendWidth );
449 QSizeF QgsLegendRenderer::drawTitleInternal(
QgsRenderContext *context, QPainter *painter, QPointF point, Qt::AlignmentFlag halignment,
double legendWidth )
452 if ( mSettings.
title().isEmpty() )
458 double y = point.y();
460 if ( context && context->
painter() )
466 painter->setPen( mSettings.
fontColor() );
472 switch ( halignment )
474 case Qt::AlignHCenter:
475 textBoxWidth = ( std::min( static_cast< double >( point.x() ), legendWidth - point.x() ) - mSettings.
boxSpace() ) * 2.0;
476 textBoxLeft = point.x() - textBoxWidth / 2.;
480 textBoxWidth = point.x() - mSettings.
boxSpace();
484 textBoxLeft = point.x();
485 textBoxWidth = legendWidth - point.x() - mSettings.
boxSpace();
491 for ( QStringList::Iterator titlePart = lines.begin(); titlePart != lines.end(); ++titlePart )
498 QRectF r( textBoxLeft, y, textBoxWidth, height );
500 if ( context && context->
painter() )
502 mSettings.
drawText( context->
painter(), r, *titlePart, titleFont, halignment, Qt::AlignVCenter, Qt::TextDontClip );
506 mSettings.
drawText( painter, r, *titlePart, titleFont, halignment, Qt::AlignVCenter, Qt::TextDontClip );
510 size.rwidth() = std::max( width, size.rwidth() );
513 if ( titlePart != ( lines.end() - 1 ) )
518 size.rheight() = y - point.y();
524 double QgsLegendRenderer::spaceAboveAtom(
const Atom &atom )
526 if ( atom.nucleons.isEmpty() )
return 0;
528 Nucleon nucleon = atom.nucleons.first();
530 if (
QgsLayerTreeGroup *nodeGroup = qobject_cast<QgsLayerTreeGroup *>( nucleon.item ) )
534 else if (
QgsLayerTreeLayer *nodeLayer = qobject_cast<QgsLayerTreeLayer *>( nucleon.item ) )
538 else if ( qobject_cast<QgsLayerTreeModelLegendNode *>( nucleon.item ) )
549 QSizeF QgsLegendRenderer::drawAtom(
const Atom &atom, QPainter *painter, QPointF point )
551 return drawAtomInternal( atom,
nullptr, painter, point );
554 QSizeF QgsLegendRenderer::drawAtomInternal(
const Atom &atom,
QgsRenderContext *context, QPainter *painter, QPointF point )
557 QSizeF size = QSizeF( atom.size );
558 for (
const Nucleon &nucleon : qgis::as_const( atom.nucleons ) )
560 if (
QgsLayerTreeGroup *groupItem = qobject_cast<QgsLayerTreeGroup *>( nucleon.item ) )
570 drawGroupTitle( groupItem, context, point );
572 drawGroupTitle( groupItem, painter, point );
575 else if (
QgsLayerTreeLayer *layerItem = qobject_cast<QgsLayerTreeLayer *>( nucleon.item ) )
585 drawLayerTitle( layerItem, context, point );
587 drawLayerTitle( layerItem, painter, point );
597 Nucleon symbolNucleon = context ? drawSymbolItem(
legendNode, context, point, nucleon.labelXOffset )
598 : drawSymbolItem(
legendNode, painter, point, nucleon.labelXOffset );
600 size.rwidth() = std::max( symbolNucleon.size.width(), size.width() );
602 point.ry() += nucleon.size.height();
608 QgsLegendRenderer::Nucleon QgsLegendRenderer::drawSymbolItem(
QgsLayerTreeModelLegendNode *symbolItem, QPainter *painter, QPointF point,
double labelXOffset )
610 return drawSymbolItemInternal( symbolItem,
nullptr, painter, point, labelXOffset );
631 : ( painter ? &ctx :
nullptr ) );
637 nucleon.item = symbolItem;
641 double width = std::max( static_cast< double >( im.
symbolSize.width() ), labelXOffset ) + im.
labelSize.width();
643 nucleon.size = QSizeF( width, height );
647 QSizeF QgsLegendRenderer::drawLayerTitle(
QgsLayerTreeLayer *nodeLayer, QPainter *painter, QPointF point )
649 return drawLayerTitleInternal( nodeLayer,
nullptr, painter, point );
655 QModelIndex idx = mLegendModel->
node2index( nodeLayer );
658 if ( mLegendModel->
data( idx, Qt::DisplayRole ).toString().isEmpty() )
661 double y = point.y();
663 if ( context && context->
painter() )
671 if ( context && nodeLayer->
layer() )
679 const QStringList lines = mSettings.
evaluateItemText( mLegendModel->
data( idx, Qt::DisplayRole ).toString(),
681 for ( QStringList::ConstIterator layerItemPart = lines.constBegin(); layerItemPart != lines.constEnd(); ++layerItemPart )
684 if ( context && context->
painter() )
685 mSettings.
drawText( context->
painter(), point.x(), y, *layerItemPart, layerFont );
687 mSettings.
drawText( painter, point.x(), y, *layerItemPart, layerFont );
689 size.rwidth() = std::max( width, size.width() );
690 if ( layerItemPart != ( lines.end() - 1 ) )
695 size.rheight() = y - point.y();
704 QSizeF QgsLegendRenderer::drawGroupTitle(
QgsLayerTreeGroup *nodeGroup, QPainter *painter, QPointF point )
706 return drawGroupTitleInternal( nodeGroup,
nullptr, painter, point );
712 QModelIndex idx = mLegendModel->
node2index( nodeGroup );
714 double y = point.y();
716 if ( context && context->
painter() )
719 painter->setPen( mSettings.
fontColor() );
725 const QStringList lines = mSettings.
evaluateItemText( mLegendModel->
data( idx, Qt::DisplayRole ).toString(),
727 for ( QStringList::ConstIterator groupPart = lines.constBegin(); groupPart != lines.constEnd(); ++groupPart )
730 if ( context && context->
painter() )
731 mSettings.
drawText( context->
painter(), point.x(), y, *groupPart, groupFont );
733 mSettings.
drawText( painter, point.x(), y, *groupPart, groupFont );
735 size.rwidth() = std::max( width, size.width() );
736 if ( groupPart != ( lines.end() - 1 ) )
741 size.rheight() = y - point.y();
747 QString style = node->
customProperty( QStringLiteral(
"legend/title-style" ) ).toString();
748 if ( style == QLatin1String(
"hidden" ) )
750 else if ( style == QLatin1String(
"group" ) )
752 else if ( style == QLatin1String(
"subgroup" ) )
779 str = QStringLiteral(
"hidden" );
782 str = QStringLiteral(
"group" );
785 str = QStringLiteral(
"subgroup" );
791 if ( !str.isEmpty() )
797 QSizeF QgsLegendRenderer::drawTitle(
QgsRenderContext *rendercontext, QPointF point, Qt::AlignmentFlag halignment,
double legendWidth )
799 return drawTitleInternal( rendercontext,
nullptr, point, halignment, legendWidth );
802 QSizeF QgsLegendRenderer::drawAtom(
const Atom &atom,
QgsRenderContext *rendercontext, QPointF point )
804 return drawAtomInternal( atom, rendercontext,
nullptr, point );
809 return drawSymbolItemInternal( symbolItem, rendercontext,
nullptr, point, labelXOffset );
814 return drawLayerTitleInternal( nodeLayer, rendercontext,
nullptr, point );
819 return drawGroupTitleInternal( nodeGroup, rendercontext,
nullptr, point );
824 paintAndDetermineSize( &context );
827 QSizeF QgsLegendRenderer::paintAndDetermineSize(
QgsRenderContext *context )
829 return paintAndDetermineSizeInternal( context,
nullptr );
static void setNodeLegendStyle(QgsLayerTreeNode *node, QgsLegendStyle::Style style)
Sets the style of a node.
Layer tree group node serves as a container for layers and further groups.
void drawText(QPainter *p, double x, double y, const QString &text, const QFont &font) const
Draws Text.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
QgsRenderContext * context
Render context, if available.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
double fontAscentMillimeters(const QFont &font) const
Returns the font ascent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCAL...
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...
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
void drawLegend(QPainter *painter)
Draws the legend with given painter.
QStringList splitStringForWrapping(const QString &stringToSplt) const
Splits a string using the wrap char taking into account handling empty wrap char which means no wrapp...
QgsLayerTreeModelLegendNode * legendNodeEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
Returns legend node that may be embedded in parent (i.e.
QFont font() const
The font for this style.
QColor layerFontColor() const
Returns layer font color, defaults to fontColor()
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
double textWidthMillimeters(const QFont &font, const QString &text) const
Returns the font width in millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCALE...
Should not happen, only if corrupted project file.
QModelIndex node2index(QgsLayerTreeNode *node) const
Returns index for a given node. If the node does not belong to the layer tree, the result is undefine...
QPainter * painter
Painter.
QgsLegendRenderer(QgsLayerTreeModel *legendModel, const QgsLegendSettings &settings)
Constructor for QgsLegendRenderer.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns style.
static QgsLegendStyle::Style nodeLegendStyle(QgsLayerTreeNode *node, QgsLayerTreeModel *model)
Returns the style for the given node, within the specified model.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
The QgsLegendSettings class stores the appearance and layout settings for legend drawing with QgsLege...
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...
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
This class is a base class for nodes in a layer tree.
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file...
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)
virtual ItemMetrics draw(const QgsLegendSettings &settings, ItemContext *ctx)
Entry point called from QgsLegendRenderer to do the rendering.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsMapSettings * legendFilterMapSettings() const
Returns the current map settings used for the current legend filter (or nullptr if none is enabled) ...
QgsMapLayer * layer() const
Returns the map layer associated with this node.
void exportToJson(const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json)
Entry point called from QgsLegendRenderer to do the rendering in a JSON object.
Special style, item is hidden including margins around.
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsLayerTreeModel * legendModel(const QgsWmsRenderContext &context, QgsLayerTree &tree)
QgsLayerTree * rootGroup() const
Returns pointer to the root node of the layer tree. Always a non nullptr value.
void exportLegendToJson(const QgsRenderContext &context, QJsonObject &json)
Renders the legend in a json object.
QPointF point
Top-left corner of the legend item.
bool equalColumnWidth() const
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
double columnSpace() const
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false)
Returns filtered list of active legend nodes attached to a particular layer node (by default it retur...
QSizeF minimumSize(QgsRenderContext *renderContext=nullptr)
Runs the layout algorithm and returns the minimum size required for the legend.
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
double lineSpacing() const
double labelXOffset
offset from the left side where label should start
double fontDescentMillimeters(const QFont &font) const
Returns the font descent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCA...
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file...
Layer tree node points to a map layer.