QGIS API Documentation  3.2.0-Bonn (bc43194)
qgsmaplayerlegend.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaplayerlegend.cpp
3  --------------------------------------
4  Date : July 2014
5  Copyright : (C) 2014 by Martin Dobias
6  Email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsmaplayerlegend.h"
17 
18 #include "qgssettings.h"
19 #include "qgslayertree.h"
21 #include "qgspluginlayer.h"
22 #include "qgsrasterlayer.h"
23 #include "qgsrenderer.h"
24 #include "qgsvectorlayer.h"
25 #include "qgsdiagramrenderer.h"
26 
27 
29  : QObject( parent )
30 {
31 }
32 
33 void QgsMapLayerLegend::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
34 {
35  Q_UNUSED( elem );
36  Q_UNUSED( context );
37 }
38 
39 QDomElement QgsMapLayerLegend::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
40 {
41  Q_UNUSED( doc );
42  Q_UNUSED( context );
43  return QDomElement();
44 }
45 
47 {
48  return new QgsDefaultVectorLayerLegend( vl );
49 }
50 
52 {
53  return new QgsDefaultRasterLayerLegend( rl );
54 }
55 
56 // -------------------------------------------------------------------------
57 
58 
59 void QgsMapLayerLegendUtils::setLegendNodeOrder( QgsLayerTreeLayer *nodeLayer, const QList<int> &order )
60 {
61  QStringList orderStr;
62  Q_FOREACH ( int id, order )
63  orderStr << QString::number( id );
64  QString str = orderStr.isEmpty() ? QStringLiteral( "empty" ) : orderStr.join( QStringLiteral( "," ) );
65 
66  nodeLayer->setCustomProperty( QStringLiteral( "legend/node-order" ), str );
67 }
68 
69 static int _originalLegendNodeCount( QgsLayerTreeLayer *nodeLayer )
70 {
71  // this is not particularly efficient way of finding out number of legend nodes
72  QList<QgsLayerTreeModelLegendNode *> lst = nodeLayer->layer()->legend()->createLayerTreeModelLegendNodes( nodeLayer );
73  int numNodes = lst.count();
74  qDeleteAll( lst );
75  return numNodes;
76 }
77 
78 static QList<int> _makeNodeOrder( QgsLayerTreeLayer *nodeLayer )
79 {
80  if ( !nodeLayer->layer() || !nodeLayer->layer()->legend() )
81  {
82  QgsDebugMsg( "Legend node order manipulation is invalid without existing legend" );
83  return QList<int>();
84  }
85 
86  int numNodes = _originalLegendNodeCount( nodeLayer );
87 
88  QList<int> order;
89  order.reserve( numNodes );
90  for ( int i = 0; i < numNodes; ++i )
91  order << i;
92  return order;
93 }
94 
96 {
97  QString orderStr = nodeLayer->customProperty( QStringLiteral( "legend/node-order" ) ).toString();
98 
99  if ( orderStr.isEmpty() )
100  return _makeNodeOrder( nodeLayer );
101 
102  if ( orderStr == QLatin1String( "empty" ) )
103  return QList<int>();
104 
105  int numNodes = _originalLegendNodeCount( nodeLayer );
106 
107  QList<int> lst;
108  Q_FOREACH ( const QString &item, orderStr.split( ',' ) )
109  {
110  bool ok;
111  int id = item.toInt( &ok );
112  if ( !ok || id < 0 || id >= numNodes )
113  return _makeNodeOrder( nodeLayer );
114 
115  lst << id;
116  }
117 
118  return lst;
119 }
120 
122 {
123  return nodeLayer->customProperties().contains( QStringLiteral( "legend/node-order" ) );
124 }
125 
126 void QgsMapLayerLegendUtils::setLegendNodeUserLabel( QgsLayerTreeLayer *nodeLayer, int originalIndex, const QString &newLabel )
127 {
128  nodeLayer->setCustomProperty( "legend/label-" + QString::number( originalIndex ), newLabel );
129 }
130 
131 QString QgsMapLayerLegendUtils::legendNodeUserLabel( QgsLayerTreeLayer *nodeLayer, int originalIndex )
132 {
133  return nodeLayer->customProperty( "legend/label-" + QString::number( originalIndex ) ).toString();
134 }
135 
137 {
138  return nodeLayer->customProperties().contains( "legend/label-" + QString::number( originalIndex ) );
139 }
140 
141 
142 void QgsMapLayerLegendUtils::applyLayerNodeProperties( QgsLayerTreeLayer *nodeLayer, QList<QgsLayerTreeModelLegendNode *> &nodes )
143 {
144  // handle user labels
145  int i = 0;
146  Q_FOREACH ( QgsLayerTreeModelLegendNode *legendNode, nodes )
147  {
148  QString userLabel = QgsMapLayerLegendUtils::legendNodeUserLabel( nodeLayer, i++ );
149  if ( !userLabel.isNull() )
150  legendNode->setUserLabel( userLabel );
151  }
152 
153  // handle user order of nodes
155  {
156  QList<int> order = QgsMapLayerLegendUtils::legendNodeOrder( nodeLayer );
157 
158  QList<QgsLayerTreeModelLegendNode *> newOrder;
159  QSet<int> usedIndices;
160  Q_FOREACH ( int idx, order )
161  {
162  if ( usedIndices.contains( idx ) )
163  {
164  QgsDebugMsg( "invalid node order. ignoring." );
165  return;
166  }
167 
168  newOrder << nodes[idx];
169  usedIndices << idx;
170  }
171 
172  // delete unused nodes
173  for ( int i = 0; i < nodes.count(); ++i )
174  {
175  if ( !usedIndices.contains( i ) )
176  delete nodes[i];
177  }
178 
179  nodes = newOrder;
180  }
181 
182 }
183 
184 // -------------------------------------------------------------------------
185 
186 
188  : mLayer( vl )
189 {
191 }
192 
193 QList<QgsLayerTreeModelLegendNode *> QgsDefaultVectorLayerLegend::createLayerTreeModelLegendNodes( QgsLayerTreeLayer *nodeLayer )
194 {
195  QList<QgsLayerTreeModelLegendNode *> nodes;
196 
197  QgsFeatureRenderer *r = mLayer->renderer();
198  if ( !r )
199  return nodes;
200 
201  if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toBool() )
202  mLayer->countSymbolFeatures();
203 
204  QgsSettings settings;
205  if ( settings.value( QStringLiteral( "qgis/showLegendClassifiers" ), false ).toBool() && !r->legendClassificationAttribute().isEmpty() )
206  {
207  nodes.append( new QgsSimpleLegendNode( nodeLayer, r->legendClassificationAttribute() ) );
208  }
209 
210  Q_FOREACH ( const QgsLegendSymbolItem &i, r->legendSymbolItems() )
211  {
213  nodes << new QgsDataDefinedSizeLegendNode( nodeLayer, *i.dataDefinedSizeLegendSettings() );
214  else
215  {
216  QgsSymbolLegendNode *legendNode = new QgsSymbolLegendNode( nodeLayer, i );
217  if ( mTextOnSymbolEnabled && mTextOnSymbolContent.contains( i.ruleKey() ) )
218  {
219  legendNode->setTextOnSymbolLabel( mTextOnSymbolContent.value( i.ruleKey() ) );
220  legendNode->setTextOnSymbolTextFormat( mTextOnSymbolTextFormat );
221  }
222  nodes << legendNode;
223  }
224  }
225 
226  if ( nodes.count() == 1 && nodes[0]->data( Qt::EditRole ).toString().isEmpty() )
227  nodes[0]->setEmbeddedInParent( true );
228 
229 
230  if ( mLayer->diagramsEnabled() )
231  {
232  Q_FOREACH ( QgsLayerTreeModelLegendNode *i, mLayer->diagramRenderer()->legendItems( nodeLayer ) )
233  {
234  nodes.append( i );
235  }
236  }
237 
238 
239  return nodes;
240 }
241 
242 void QgsDefaultVectorLayerLegend::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
243 {
244  mTextOnSymbolEnabled = false;
245  mTextOnSymbolTextFormat = QgsTextFormat();
246  mTextOnSymbolContent.clear();
247 
248  QDomElement tosElem = elem.firstChildElement( QStringLiteral( "text-on-symbol" ) );
249  if ( !tosElem.isNull() )
250  {
251  mTextOnSymbolEnabled = true;
252  QDomElement tosFormatElem = tosElem.firstChildElement( QStringLiteral( "text-style" ) );
253  mTextOnSymbolTextFormat.readXml( tosFormatElem, context );
254  QDomElement tosContentElem = tosElem.firstChildElement( QStringLiteral( "content" ) );
255  QDomElement tosContentItemElem = tosContentElem.firstChildElement( QStringLiteral( "item" ) );
256  while ( !tosContentItemElem.isNull() )
257  {
258  mTextOnSymbolContent.insert( tosContentItemElem.attribute( QStringLiteral( "key" ) ), tosContentItemElem.attribute( QStringLiteral( "value" ) ) );
259  tosContentItemElem = tosContentItemElem.nextSiblingElement( QStringLiteral( "item" ) );
260  }
261  }
262 }
263 
264 QDomElement QgsDefaultVectorLayerLegend::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
265 {
266  QDomElement elem = doc.createElement( QStringLiteral( "legend" ) );
267  elem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "default-vector" ) );
268 
269  if ( mTextOnSymbolEnabled )
270  {
271  QDomElement tosElem = doc.createElement( QStringLiteral( "text-on-symbol" ) );
272  QDomElement tosFormatElem = mTextOnSymbolTextFormat.writeXml( doc, context );
273  tosElem.appendChild( tosFormatElem );
274  QDomElement tosContentElem = doc.createElement( QStringLiteral( "content" ) );
275  for ( auto it = mTextOnSymbolContent.constBegin(); it != mTextOnSymbolContent.constEnd(); ++it )
276  {
277  QDomElement tosContentItemElem = doc.createElement( QStringLiteral( "item" ) );
278  tosContentItemElem.setAttribute( QStringLiteral( "key" ), it.key() );
279  tosContentItemElem.setAttribute( QStringLiteral( "value" ), it.value() );
280  tosContentElem.appendChild( tosContentItemElem );
281  }
282  tosElem.appendChild( tosContentElem );
283  elem.appendChild( tosElem );
284  }
285 
286  return elem;
287 }
288 
289 
290 // -------------------------------------------------------------------------
291 
292 
294  : mLayer( rl )
295 {
297 }
298 
299 QList<QgsLayerTreeModelLegendNode *> QgsDefaultRasterLayerLegend::createLayerTreeModelLegendNodes( QgsLayerTreeLayer *nodeLayer )
300 {
301  QList<QgsLayerTreeModelLegendNode *> nodes;
302 
303  // temporary solution for WMS. Ideally should be done with a delegate.
304  if ( mLayer->dataProvider()->supportsLegendGraphic() )
305  {
306  nodes << new QgsWmsLegendNode( nodeLayer );
307  }
308 
309  QgsLegendColorList rasterItemList = mLayer->legendSymbologyItems();
310  if ( rasterItemList.isEmpty() )
311  return nodes;
312 
313  // Paletted raster may have many colors, for example UInt16 may have 65536 colors
314  // and it is very slow, so we limit max count
315  int count = 0;
316  int max_count = 1000;
317 
318  for ( QgsLegendColorList::const_iterator itemIt = rasterItemList.constBegin();
319  itemIt != rasterItemList.constEnd(); ++itemIt, ++count )
320  {
321  nodes << new QgsRasterSymbolLegendNode( nodeLayer, itemIt->second, itemIt->first );
322 
323  if ( count == max_count )
324  {
325  QString label = tr( "following %1 items\nnot displayed" ).arg( rasterItemList.size() - max_count );
326  nodes << new QgsSimpleLegendNode( nodeLayer, label );
327  break;
328  }
329  }
330 
331  return nodes;
332 }
The class is used as a container of context for various read/write operations on other objects...
virtual QgsLegendSymbolList legendSymbolItems() const
Returns a list of symbology items for the legend.
static void setLegendNodeOrder(QgsLayerTreeLayer *nodeLayer, const QList< int > &order)
QgsVectorLayerFeatureCounter * countSymbolFeatures()
Count features for symbols.
virtual QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Writes configuration to a DOM element, to be used later with readXml()
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
virtual QString legendClassificationAttribute() const
If supported by the renderer, return classification attribute for the use in legend.
Definition: qgsrenderer.h:342
QString ruleKey() const
Returns unique identifier of the rule for identification of the item within renderer.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
QgsMapLayerLegend * legend() const
Can be null.
static void setLegendNodeUserLabel(QgsLayerTreeLayer *nodeLayer, int originalIndex, const QString &newLabel)
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
virtual QList< QgsLayerTreeModelLegendNode *> legendItems(QgsLayerTreeLayer *nodeLayer) const
Returns list of legend nodes for the diagram.
Implementation of legend node interface for displaying raster legend entries.
Produces legend node with a marker symbol.
static QList< int > legendNodeOrder(QgsLayerTreeLayer *nodeLayer)
void setTextOnSymbolLabel(const QString &label)
Sets label of text to be shown on top of the symbol.
Default legend implementation for raster layers.
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
virtual bool supportsLegendGraphic() const
Returns whether the provider supplies a legend graphic.
static bool hasLegendNodeOrder(QgsLayerTreeLayer *nodeLayer)
QgsDefaultVectorLayerLegend(QgsVectorLayer *vl)
Implementation of legend node interface for displaying WMS legend entries.
QStringList customProperties() const
Returns list of keys stored in custom properties.
QList< QgsLayerTreeModelLegendNode * > createLayerTreeModelLegendNodes(QgsLayerTreeLayer *nodeLayer) override
Returns list of legend nodes to be used for a particular layer tree layer node.
QgsRasterDataProvider * dataProvider() override
Returns the layer&#39;s data provider.
virtual QList< QgsLayerTreeModelLegendNode * > createLayerTreeModelLegendNodes(QgsLayerTreeLayer *nodeLayer)=0
Returns list of legend nodes to be used for a particular layer tree layer node.
static QString legendNodeUserLabel(QgsLayerTreeLayer *nodeLayer, int originalIndex)
QgsMapLayerLegend(QObject *parent=nullptr)
Constructor for QgsMapLayerLegend.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
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...
The QgsMapLayerLegend class is abstract interface for implementations of legends for one map layer...
QgsFeatureRenderer * renderer()
Returns renderer.
static bool hasLegendNodeUserLabel(QgsLayerTreeLayer *nodeLayer, int originalIndex)
Implementation of legend node interface for displaying arbitrary label with icon. ...
void rendererChanged()
Signal emitted when renderer is changed.
const QgsDiagramRenderer * diagramRenderer() const
void setTextOnSymbolTextFormat(const QgsTextFormat &format)
Sets format of text to be shown on top of the symbol.
static QgsMapLayerLegend * defaultVectorLegend(QgsVectorLayer *vl)
Create new legend implementation for vector layer.
QgsMapLayer * layer() const
QList< QPair< QString, QColor > > QgsLegendColorList
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
void itemsChanged()
Emitted when existing items/nodes got invalid and should be replaced by new ones. ...
static QgsMapLayerLegend * defaultRasterLegend(QgsRasterLayer *rl)
Create new legend implementation for raster layer.
static void applyLayerNodeProperties(QgsLayerTreeLayer *nodeLayer, QList< QgsLayerTreeModelLegendNode *> &nodes)
update according to layer node&#39;s custom properties (order of items, user labels for items) ...
QgsDataDefinedSizeLegend * dataDefinedSizeLegendSettings() const
Returns extra information for data-defined size legend rendering.
QgsDefaultRasterLayerLegend(QgsRasterLayer *rl)
QList< QgsLayerTreeModelLegendNode * > createLayerTreeModelLegendNodes(QgsLayerTreeLayer *nodeLayer) override
Returns list of legend nodes to be used for a particular layer tree layer node.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
void readXml(const QDomElement &elem, const QgsReadWriteContext &context) override
Reads configuration from a DOM element previously written by writeXml()
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const override
Writes configuration to a DOM element, to be used later with readXml()
Container for all settings relating to text rendering.
Default legend implementation for vector layers.
QgsLegendColorList legendSymbologyItems() const
Returns a list with classification items (Text and color)
Represents a vector layer which manages a vector based data sets.
virtual void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Reads configuration from a DOM element previously written by writeXml()
bool diagramsEnabled() const
Returns whether the layer contains diagrams which are enabled and should be drawn.
virtual void setUserLabel(const QString &userLabel)
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.