QGIS API Documentation  3.4.3-Madeira (2f64a3c)
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 "qgsmeshlayer.h"
22 #include "qgspluginlayer.h"
23 #include "qgsrasterlayer.h"
24 #include "qgsrenderer.h"
25 #include "qgsvectorlayer.h"
26 #include "qgsdiagramrenderer.h"
27 
28 
30  : QObject( parent )
31 {
32 }
33 
34 void QgsMapLayerLegend::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
35 {
36  Q_UNUSED( elem );
37  Q_UNUSED( context );
38 }
39 
40 QDomElement QgsMapLayerLegend::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
41 {
42  Q_UNUSED( doc );
43  Q_UNUSED( context );
44  return QDomElement();
45 }
46 
48 {
49  return new QgsDefaultVectorLayerLegend( vl );
50 }
51 
53 {
54  return new QgsDefaultRasterLayerLegend( rl );
55 }
56 
58 {
59  return new QgsDefaultMeshLayerLegend( ml );
60 }
61 
62 // -------------------------------------------------------------------------
63 
64 
65 void QgsMapLayerLegendUtils::setLegendNodeOrder( QgsLayerTreeLayer *nodeLayer, const QList<int> &order )
66 {
67  QStringList orderStr;
68  Q_FOREACH ( int id, order )
69  orderStr << QString::number( id );
70  QString str = orderStr.isEmpty() ? QStringLiteral( "empty" ) : orderStr.join( QStringLiteral( "," ) );
71 
72  nodeLayer->setCustomProperty( QStringLiteral( "legend/node-order" ), str );
73 }
74 
75 static int _originalLegendNodeCount( QgsLayerTreeLayer *nodeLayer )
76 {
77  // this is not particularly efficient way of finding out number of legend nodes
78  QList<QgsLayerTreeModelLegendNode *> lst = nodeLayer->layer()->legend()->createLayerTreeModelLegendNodes( nodeLayer );
79  int numNodes = lst.count();
80  qDeleteAll( lst );
81  return numNodes;
82 }
83 
84 static QList<int> _makeNodeOrder( QgsLayerTreeLayer *nodeLayer )
85 {
86  if ( !nodeLayer->layer() || !nodeLayer->layer()->legend() )
87  {
88  QgsDebugMsg( QStringLiteral( "Legend node order manipulation is invalid without existing legend" ) );
89  return QList<int>();
90  }
91 
92  int numNodes = _originalLegendNodeCount( nodeLayer );
93 
94  QList<int> order;
95  order.reserve( numNodes );
96  for ( int i = 0; i < numNodes; ++i )
97  order << i;
98  return order;
99 }
100 
102 {
103  QString orderStr = nodeLayer->customProperty( QStringLiteral( "legend/node-order" ) ).toString();
104 
105  if ( orderStr.isEmpty() )
106  return _makeNodeOrder( nodeLayer );
107 
108  if ( orderStr == QLatin1String( "empty" ) )
109  return QList<int>();
110 
111  int numNodes = _originalLegendNodeCount( nodeLayer );
112 
113  QList<int> lst;
114  Q_FOREACH ( const QString &item, orderStr.split( ',' ) )
115  {
116  bool ok;
117  int id = item.toInt( &ok );
118  if ( !ok || id < 0 || id >= numNodes )
119  return _makeNodeOrder( nodeLayer );
120 
121  lst << id;
122  }
123 
124  return lst;
125 }
126 
128 {
129  return nodeLayer->customProperties().contains( QStringLiteral( "legend/node-order" ) );
130 }
131 
132 void QgsMapLayerLegendUtils::setLegendNodeUserLabel( QgsLayerTreeLayer *nodeLayer, int originalIndex, const QString &newLabel )
133 {
134  nodeLayer->setCustomProperty( "legend/label-" + QString::number( originalIndex ), newLabel );
135 }
136 
137 QString QgsMapLayerLegendUtils::legendNodeUserLabel( QgsLayerTreeLayer *nodeLayer, int originalIndex )
138 {
139  return nodeLayer->customProperty( "legend/label-" + QString::number( originalIndex ) ).toString();
140 }
141 
143 {
144  return nodeLayer->customProperties().contains( "legend/label-" + QString::number( originalIndex ) );
145 }
146 
147 
148 void QgsMapLayerLegendUtils::applyLayerNodeProperties( QgsLayerTreeLayer *nodeLayer, QList<QgsLayerTreeModelLegendNode *> &nodes )
149 {
150  // handle user labels
151  int i = 0;
152  Q_FOREACH ( QgsLayerTreeModelLegendNode *legendNode, nodes )
153  {
154  QString userLabel = QgsMapLayerLegendUtils::legendNodeUserLabel( nodeLayer, i++ );
155  if ( !userLabel.isNull() )
156  legendNode->setUserLabel( userLabel );
157  }
158 
159  // handle user order of nodes
161  {
162  QList<int> order = QgsMapLayerLegendUtils::legendNodeOrder( nodeLayer );
163 
164  QList<QgsLayerTreeModelLegendNode *> newOrder;
165  QSet<int> usedIndices;
166  Q_FOREACH ( int idx, order )
167  {
168  if ( usedIndices.contains( idx ) )
169  {
170  QgsDebugMsg( QStringLiteral( "invalid node order. ignoring." ) );
171  return;
172  }
173 
174  newOrder << nodes[idx];
175  usedIndices << idx;
176  }
177 
178  // delete unused nodes
179  for ( int i = 0; i < nodes.count(); ++i )
180  {
181  if ( !usedIndices.contains( i ) )
182  delete nodes[i];
183  }
184 
185  nodes = newOrder;
186  }
187 
188 }
189 
190 // -------------------------------------------------------------------------
191 
192 
194  : mLayer( vl )
195 {
197 }
198 
199 QList<QgsLayerTreeModelLegendNode *> QgsDefaultVectorLayerLegend::createLayerTreeModelLegendNodes( QgsLayerTreeLayer *nodeLayer )
200 {
201  QList<QgsLayerTreeModelLegendNode *> nodes;
202 
203  QgsFeatureRenderer *r = mLayer->renderer();
204  if ( !r )
205  return nodes;
206 
207  if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toBool() )
208  mLayer->countSymbolFeatures();
209 
210  QgsSettings settings;
211  if ( settings.value( QStringLiteral( "qgis/showLegendClassifiers" ), false ).toBool() && !r->legendClassificationAttribute().isEmpty() )
212  {
213  nodes.append( new QgsSimpleLegendNode( nodeLayer, r->legendClassificationAttribute() ) );
214  }
215 
216  Q_FOREACH ( const QgsLegendSymbolItem &i, r->legendSymbolItems() )
217  {
219  nodes << new QgsDataDefinedSizeLegendNode( nodeLayer, *i.dataDefinedSizeLegendSettings() );
220  else
221  {
222  QgsSymbolLegendNode *legendNode = new QgsSymbolLegendNode( nodeLayer, i );
223  if ( mTextOnSymbolEnabled && mTextOnSymbolContent.contains( i.ruleKey() ) )
224  {
225  legendNode->setTextOnSymbolLabel( mTextOnSymbolContent.value( i.ruleKey() ) );
226  legendNode->setTextOnSymbolTextFormat( mTextOnSymbolTextFormat );
227  }
228  nodes << legendNode;
229  }
230  }
231 
232  if ( nodes.count() == 1 && nodes[0]->data( Qt::EditRole ).toString().isEmpty() )
233  nodes[0]->setEmbeddedInParent( true );
234 
235 
236  if ( mLayer->diagramsEnabled() )
237  {
238  Q_FOREACH ( QgsLayerTreeModelLegendNode *i, mLayer->diagramRenderer()->legendItems( nodeLayer ) )
239  {
240  nodes.append( i );
241  }
242  }
243 
244 
245  return nodes;
246 }
247 
248 void QgsDefaultVectorLayerLegend::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
249 {
250  mTextOnSymbolEnabled = false;
251  mTextOnSymbolTextFormat = QgsTextFormat();
252  mTextOnSymbolContent.clear();
253 
254  QDomElement tosElem = elem.firstChildElement( QStringLiteral( "text-on-symbol" ) );
255  if ( !tosElem.isNull() )
256  {
257  mTextOnSymbolEnabled = true;
258  QDomElement tosFormatElem = tosElem.firstChildElement( QStringLiteral( "text-style" ) );
259  mTextOnSymbolTextFormat.readXml( tosFormatElem, context );
260  QDomElement tosContentElem = tosElem.firstChildElement( QStringLiteral( "content" ) );
261  QDomElement tosContentItemElem = tosContentElem.firstChildElement( QStringLiteral( "item" ) );
262  while ( !tosContentItemElem.isNull() )
263  {
264  mTextOnSymbolContent.insert( tosContentItemElem.attribute( QStringLiteral( "key" ) ), tosContentItemElem.attribute( QStringLiteral( "value" ) ) );
265  tosContentItemElem = tosContentItemElem.nextSiblingElement( QStringLiteral( "item" ) );
266  }
267  }
268 }
269 
270 QDomElement QgsDefaultVectorLayerLegend::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
271 {
272  QDomElement elem = doc.createElement( QStringLiteral( "legend" ) );
273  elem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "default-vector" ) );
274 
275  if ( mTextOnSymbolEnabled )
276  {
277  QDomElement tosElem = doc.createElement( QStringLiteral( "text-on-symbol" ) );
278  QDomElement tosFormatElem = mTextOnSymbolTextFormat.writeXml( doc, context );
279  tosElem.appendChild( tosFormatElem );
280  QDomElement tosContentElem = doc.createElement( QStringLiteral( "content" ) );
281  for ( auto it = mTextOnSymbolContent.constBegin(); it != mTextOnSymbolContent.constEnd(); ++it )
282  {
283  QDomElement tosContentItemElem = doc.createElement( QStringLiteral( "item" ) );
284  tosContentItemElem.setAttribute( QStringLiteral( "key" ), it.key() );
285  tosContentItemElem.setAttribute( QStringLiteral( "value" ), it.value() );
286  tosContentElem.appendChild( tosContentItemElem );
287  }
288  tosElem.appendChild( tosContentElem );
289  elem.appendChild( tosElem );
290  }
291 
292  return elem;
293 }
294 
295 
296 // -------------------------------------------------------------------------
297 
298 
300  : mLayer( rl )
301 {
303 }
304 
305 QList<QgsLayerTreeModelLegendNode *> QgsDefaultRasterLayerLegend::createLayerTreeModelLegendNodes( QgsLayerTreeLayer *nodeLayer )
306 {
307  QList<QgsLayerTreeModelLegendNode *> nodes;
308 
309  // temporary solution for WMS. Ideally should be done with a delegate.
310  if ( mLayer->dataProvider()->supportsLegendGraphic() )
311  {
312  nodes << new QgsWmsLegendNode( nodeLayer );
313  }
314 
315  QgsLegendColorList rasterItemList = mLayer->legendSymbologyItems();
316  if ( rasterItemList.isEmpty() )
317  return nodes;
318 
319  // Paletted raster may have many colors, for example UInt16 may have 65536 colors
320  // and it is very slow, so we limit max count
321  int count = 0;
322  int max_count = 1000;
323 
324  for ( QgsLegendColorList::const_iterator itemIt = rasterItemList.constBegin();
325  itemIt != rasterItemList.constEnd(); ++itemIt, ++count )
326  {
327  nodes << new QgsRasterSymbolLegendNode( nodeLayer, itemIt->second, itemIt->first );
328 
329  if ( count == max_count )
330  {
331  QString label = tr( "following %1 items\nnot displayed" ).arg( rasterItemList.size() - max_count );
332  nodes << new QgsSimpleLegendNode( nodeLayer, label );
333  break;
334  }
335  }
336 
337  return nodes;
338 }
339 
340 // -------------------------------------------------------------------------
341 
343  : mLayer( ml )
344 {
346 }
347 
348 QList<QgsLayerTreeModelLegendNode *> QgsDefaultMeshLayerLegend::createLayerTreeModelLegendNodes( QgsLayerTreeLayer *nodeLayer )
349 {
350  QList<QgsLayerTreeModelLegendNode *> nodes;
351 
352  QgsMeshDataProvider *provider = mLayer->dataProvider();
353  if ( !provider )
354  return nodes;
355 
356  QgsMeshRendererSettings rendererSettings = mLayer->rendererSettings();
357 
358  QgsMeshDatasetIndex indexScalar = rendererSettings.activeScalarDataset();
359  QgsMeshDatasetIndex indexVector = rendererSettings.activeVectorDataset();
360 
361  QString name;
362  if ( indexScalar.isValid() && indexVector.isValid() && indexScalar.group() != indexVector.group() )
363  name = QString( "%1 / %2" ).arg( provider->datasetGroupMetadata( indexScalar.group() ).name(), provider->datasetGroupMetadata( indexVector.group() ).name() );
364  else if ( indexScalar.isValid() )
365  name = provider->datasetGroupMetadata( indexScalar.group() ).name();
366  else if ( indexVector.isValid() )
367  name = provider->datasetGroupMetadata( indexVector.group() ).name();
368  else
369  {
370  // neither contours nor vectors get rendered - no legend needed
371  return nodes;
372  }
373 
374  nodes << new QgsSimpleLegendNode( nodeLayer, name );
375 
376  if ( indexScalar.isValid() )
377  {
378  QgsMeshRendererScalarSettings settings = rendererSettings.scalarSettings( indexScalar.group() );
379  QgsLegendColorList items;
380  settings.colorRampShader().legendSymbologyItems( items );
381  for ( const QPair< QString, QColor > &item : qgis::as_const( items ) )
382  {
383  nodes << new QgsRasterSymbolLegendNode( nodeLayer, item.second, item.first );
384  }
385  }
386 
387  return nodes;
388 }
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.
Default legend implementation for mesh layers.
Represents a mesh renderer settings for scalar datasets.
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.
QgsMeshRendererScalarSettings scalarSettings(int groupIndex) const
Returns renderer settings.
int group() const
Returns a group index.
QStringList customProperties() const
Returns list of keys stored in custom properties.
static QgsMapLayerLegend * defaultMeshLegend(QgsMeshLayer *ml)
Create new legend implementation for mesh layer.
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.
QgsMeshDatasetIndex activeVectorDataset() const
Returns active vector dataset.
Represents all mesh renderer settings.
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.
QgsMeshRendererSettings rendererSettings() const
Returns renderer settings.
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...
void legendSymbologyItems(QList< QPair< QString, QColor > > &symbolItems) const override
Returns legend symbology items if provided by renderer.
QgsFeatureRenderer * renderer()
Returns renderer.
static bool hasLegendNodeUserLabel(QgsLayerTreeLayer *nodeLayer, int originalIndex)
Implementation of legend node interface for displaying arbitrary label with icon. ...
QgsMeshDataProvider * dataProvider() override
Returns the layer&#39;s data provider.
void rendererChanged()
Signal emitted when renderer is changed.
QList< QgsLayerTreeModelLegendNode * > createLayerTreeModelLegendNodes(QgsLayerTreeLayer *nodeLayer) override
Returns list of legend nodes to be used for a particular layer tree layer node.
const QgsDiagramRenderer * diagramRenderer() const
QgsColorRampShader colorRampShader() const
Returns color ramp shader function.
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.
Base class for providing data for QgsMeshLayer.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
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)
QgsMeshDatasetIndex is index that identifies the dataset group (e.g.
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...
bool isValid() const
Returns whether index is valid, ie at least groups is set.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context) override
Reads configuration from a DOM element previously written by writeXml()
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:89
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.
QgsDefaultMeshLayerLegend(QgsMeshLayer *ml)
Creates an instance for the given mesh layer.
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.
virtual QgsMeshDatasetGroupMetadata datasetGroupMetadata(int groupIndex) const =0
Returns dataset group metadata.
QgsMeshDatasetIndex activeScalarDataset() const
Returns active scalar dataset.