QGIS API Documentation  2.9.0-Master
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 <QSettings>
19 
20 #include "qgslayertree.h"
22 #include "qgspluginlayer.h"
23 #include "qgsrasterlayer.h"
24 #include "qgsrendererv2.h"
25 #include "qgsvectorlayer.h"
26 #include "qgsdiagramrendererv2.h"
27 
28 
30  QObject( parent )
31 {
32 }
33 
35 {
36  return new QgsDefaultVectorLayerLegend( vl );
37 }
38 
40 {
41  return new QgsDefaultRasterLayerLegend( rl );
42 }
43 
45 {
46  return new QgsDefaultPluginLayerLegend( pl );
47 }
48 
49 // -------------------------------------------------------------------------
50 
51 
52 void QgsMapLayerLegendUtils::setLegendNodeOrder( QgsLayerTreeLayer* nodeLayer, const QList<int>& order )
53 {
54  QStringList orderStr;
55  foreach ( int id, order )
56  orderStr << QString::number( id );
57  QString str = orderStr.isEmpty() ? "empty" : orderStr.join( "," );
58 
59  nodeLayer->setCustomProperty( "legend/node-order", str );
60 }
61 
63 {
64  // this is not particularly efficient way of finding out number of legend nodes
65  QList<QgsLayerTreeModelLegendNode*> lst = nodeLayer->layer()->legend()->createLayerTreeModelLegendNodes( nodeLayer );
66  int numNodes = lst.count();
67  qDeleteAll( lst );
68  return numNodes;
69 }
70 
71 static QList<int> _makeNodeOrder( QgsLayerTreeLayer* nodeLayer )
72 {
73  if ( !nodeLayer->layer() || !nodeLayer->layer()->legend() )
74  {
75  QgsDebugMsg( "Legend node order manipulation is invalid without existing legend" );
76  return QList<int>();
77  }
78 
79  int numNodes = _originalLegendNodeCount( nodeLayer );
80 
81  QList<int> order;
82  for ( int i = 0; i < numNodes; ++i )
83  order << i;
84  return order;
85 }
86 
88 {
89  QString orderStr = nodeLayer->customProperty( "legend/node-order" ).toString();
90 
91  if ( orderStr.isEmpty() )
92  return _makeNodeOrder( nodeLayer );
93 
94  if ( orderStr == "empty" )
95  return QList<int>();
96 
97  int numNodes = _originalLegendNodeCount( nodeLayer );
98 
99  QList<int> lst;
100  foreach ( QString item, orderStr.split( "," ) )
101  {
102  bool ok;
103  int id = item.toInt( &ok );
104  if ( !ok || id < 0 || id >= numNodes )
105  return _makeNodeOrder( nodeLayer );
106 
107  lst << id;
108  }
109 
110  return lst;
111 }
112 
114 {
115  return nodeLayer->customProperties().contains( "legend/node-order" );
116 }
117 
118 void QgsMapLayerLegendUtils::setLegendNodeUserLabel( QgsLayerTreeLayer* nodeLayer, int originalIndex, const QString& newLabel )
119 {
120  nodeLayer->setCustomProperty( "legend/label-" + QString::number( originalIndex ), newLabel );
121 }
122 
123 QString QgsMapLayerLegendUtils::legendNodeUserLabel( QgsLayerTreeLayer* nodeLayer, int originalIndex )
124 {
125  return nodeLayer->customProperty( "legend/label-" + QString::number( originalIndex ) ).toString();
126 }
127 
129 {
130  return nodeLayer->customProperties().contains( "legend/label-" + QString::number( originalIndex ) );
131 }
132 
133 
134 void QgsMapLayerLegendUtils::applyLayerNodeProperties( QgsLayerTreeLayer* nodeLayer, QList<QgsLayerTreeModelLegendNode*>& nodes )
135 {
136  // handle user labels
137  int i = 0;
138  foreach ( QgsLayerTreeModelLegendNode* legendNode, nodes )
139  {
140  QString userLabel = QgsMapLayerLegendUtils::legendNodeUserLabel( nodeLayer, i++ );
141  if ( !userLabel.isNull() )
142  legendNode->setUserLabel( userLabel );
143  }
144 
145  // handle user order of nodes
147  {
148  QList<int> order = QgsMapLayerLegendUtils::legendNodeOrder( nodeLayer );
149 
150  QList<QgsLayerTreeModelLegendNode*> newOrder;
151  QSet<int> usedIndices;
152  foreach ( int idx, order )
153  {
154  if ( usedIndices.contains( idx ) )
155  {
156  QgsDebugMsg( "invalid node order. ignoring." );
157  return;
158  }
159 
160  newOrder << nodes[idx];
161  usedIndices << idx;
162  }
163 
164  // delete unused nodes
165  for ( int i = 0; i < nodes.count(); ++i )
166  {
167  if ( !usedIndices.contains( i ) )
168  delete nodes[i];
169  }
170 
171  nodes = newOrder;
172  }
173 
174 }
175 
176 // -------------------------------------------------------------------------
177 
178 
180  : mLayer( vl )
181 {
182  connect( mLayer, SIGNAL( rendererChanged() ), this, SIGNAL( itemsChanged() ) );
183 }
184 
186 {
187  QList<QgsLayerTreeModelLegendNode*> nodes;
188 
189  QgsFeatureRendererV2* r = mLayer->rendererV2();
190  if ( !r )
191  return nodes;
192 
193  if ( nodeLayer->customProperty( "showFeatureCount", 0 ).toBool() )
194  mLayer->countSymbolFeatures();
195 
196  QSettings settings;
197  if ( settings.value( "/qgis/showLegendClassifiers", false ).toBool() && !r->legendClassificationAttribute().isEmpty() )
198  {
199  nodes.append( new QgsSimpleLegendNode( nodeLayer, r->legendClassificationAttribute() ) );
200  }
201 
202  // we have varying icon sizes, and we want icon to be centered and
203  // text to be left aligned, so we have to compute the max width of icons
204  //
205  // we do that for nodes who share a common parent
206 
207  QList<QgsSymbolV2LegendNode*> symbolNodes;
208  QMap<QString, int> widthMax;
209  foreach ( const QgsLegendSymbolItemV2& i, r->legendSymbolItemsV2() )
210  {
211  QgsSymbolV2LegendNode * n = new QgsSymbolV2LegendNode( nodeLayer, i );
212  nodes.append( n );
213  if ( i.symbol() )
214  {
215  const QSize sz( n->minimumIconSize() );
216  const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
217  widthMax[parentKey] = qMax( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 );
218  n->setIconSize( sz );
219  symbolNodes.append( n );
220  }
221  }
222 
223  foreach ( QgsSymbolV2LegendNode* n, symbolNodes )
224  {
225  const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
226  Q_ASSERT( widthMax[parentKey] > 0 );
227  const int twiceMarginWidth = 2; // a one pixel margin avoids hugly rendering of icon
228  n->setIconSize( QSize( widthMax[parentKey] + twiceMarginWidth, n->iconSize().rheight() + twiceMarginWidth ) );
229  }
230 
231  if ( nodes.count() == 1 && nodes[0]->data( Qt::EditRole ).toString().isEmpty() )
232  nodes[0]->setEmbeddedInParent( true );
233 
234 
235  if ( mLayer->diagramsEnabled() )
236  {
237  foreach ( QgsLayerTreeModelLegendNode * i, mLayer->diagramRenderer()->legendItems( nodeLayer ) )
238  {
239  nodes.append( i );
240  }
241  }
242 
243 
244  return nodes;
245 }
246 
247 
248 
249 // -------------------------------------------------------------------------
250 
251 
253  : mLayer( rl )
254 {
255  connect( mLayer, SIGNAL( rendererChanged() ), this, SIGNAL( itemsChanged() ) );
256 }
257 
259 {
260  QList<QgsLayerTreeModelLegendNode*> nodes;
261 
262  // temporary solution for WMS. Ideally should be done with a delegate.
263  if ( mLayer->providerType() == "wms" )
264  {
265  nodes << new QgsWMSLegendNode( nodeLayer );
266  }
267 
268  QgsLegendColorList rasterItemList = mLayer->legendSymbologyItems();
269  if ( rasterItemList.count() == 0 )
270  return nodes;
271 
272  // Paletted raster may have many colors, for example UInt16 may have 65536 colors
273  // and it is very slow, so we limit max count
274  int count = 0;
275  int max_count = 1000;
276 
277  for ( QgsLegendColorList::const_iterator itemIt = rasterItemList.constBegin();
278  itemIt != rasterItemList.constEnd(); ++itemIt, ++count )
279  {
280  nodes << new QgsRasterSymbolLegendNode( nodeLayer, itemIt->second, itemIt->first );
281 
282  if ( count == max_count )
283  {
284  QString label = tr( "following %1 items\nnot displayed" ).arg( rasterItemList.size() - max_count );
285  nodes << new QgsSimpleLegendNode( nodeLayer, label );
286  break;
287  }
288  }
289 
290  return nodes;
291 }
292 
293 
294 // -------------------------------------------------------------------------
295 
296 
298  : mLayer( pl )
299 {
300 }
301 
303 {
304  QList<QgsLayerTreeModelLegendNode*> nodes;
305 
306  QSize iconSize( 16, 16 );
307  QgsLegendSymbologyList symbologyList = mLayer->legendSymbologyItems( iconSize );
308 
309  if ( symbologyList.count() == 0 )
310  return nodes;
311 
312  typedef QPair<QString, QPixmap> XY;
313  foreach ( XY item, symbologyList )
314  {
315  nodes << new QgsSimpleLegendNode( nodeLayer, item.first, QIcon( item.second ) );
316  }
317 
318  return nodes;
319 }
320 
QgsFeatureRendererV2 * rendererV2()
Return renderer V2.
static void setLegendNodeOrder(QgsLayerTreeLayer *nodeLayer, const QList< int > &order)
bool diagramsEnabled() const
Returns whether the layer contains diagrams which are enabled and should be drawn.
static QgsMapLayerLegend * defaultPluginLegend(QgsPluginLayer *pl)
Create new legend implementation for raster layer.
virtual QList< QgsLayerTreeModelLegendNode * > legendItems(QgsLayerTreeLayer *nodeLayer) const
Returns list of legend nodes for the diagram.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
static void setLegendNodeUserLabel(QgsLayerTreeLayer *nodeLayer, int originalIndex, const QString &newLabel)
virtual QgsLegendSymbologyList legendSymbologyItems(const QSize &iconSize)
return a list of symbology items for the legend (defult implementation returns nothing) ...
QgsMapLayer * layer() const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
Base class for plugin layers.
virtual QList< QgsLayerTreeModelLegendNode * > createLayerTreeModelLegendNodes(QgsLayerTreeLayer *nodeLayer) override
Return list of legend nodes to be used for a particular layer tree layer node.
QStringList customProperties() const
Return list of keys stored in custom properties.
Implementation of legend node interface for displaying raster legend entries.
static QList< int > legendNodeOrder(QgsLayerTreeLayer *nodeLayer)
QgsDefaultPluginLayerLegend(QgsPluginLayer *pl)
Default legend implementation for raster layers.
QSize minimumIconSize() const
Get the minimum icon size to prevent cropping.
static bool hasLegendNodeOrder(QgsLayerTreeLayer *nodeLayer)
QgsDefaultVectorLayerLegend(QgsVectorLayer *vl)
QgsSymbolV2 * symbol() const
Return associated symbol. May be null.
virtual QList< QgsLayerTreeModelLegendNode * > createLayerTreeModelLegendNodes(QgsLayerTreeLayer *nodeLayer) override
Return list of legend nodes to be used for a particular layer tree layer node.
rule key of the parent legend node - for legends with tree hierarchy (QString). Added in 2...
static int _originalLegendNodeCount(QgsLayerTreeLayer *nodeLayer)
virtual QString legendClassificationAttribute() const
If supported by the renderer, return classification attribute for the use in legend.
static QString legendNodeUserLabel(QgsLayerTreeLayer *nodeLayer, int originalIndex)
virtual QVariant data(int role) const override
Return data associated with the item.
The QgsMapLayerLegend class is abstract interface for implementations of legends for one map layer...
static bool hasLegendNodeUserLabel(QgsLayerTreeLayer *nodeLayer, int originalIndex)
Implementation of legend node interface for displaying arbitrary label with icon. ...
const QgsDiagramRendererV2 * diagramRenderer() const
QList< QPair< QString, QPixmap > > QgsLegendSymbologyList
static void applyLayerNodeProperties(QgsLayerTreeLayer *nodeLayer, QList< QgsLayerTreeModelLegendNode * > &nodes)
update according to layer node's custom properties (order of items, user labels for items) ...
bool countSymbolFeatures(bool showProgress=true)
Count features for symbols.
static QgsMapLayerLegend * defaultVectorLegend(QgsVectorLayer *vl)
Create new legend implementation for vector layer.
QgsMapLayerLegend * legend() const
Can be null.
Implementation of legend node interface for displaying WMS legend entries.
QList< QPair< QString, QColor > > QgsLegendColorList
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.
virtual QgsLegendSymbolListV2 legendSymbolItemsV2() const
Return a list of symbology items for the legend.
QgsDefaultRasterLayerLegend(QgsRasterLayer *rl)
Default legend implementation for plugin layers.
virtual QList< QgsLayerTreeModelLegendNode * > createLayerTreeModelLegendNodes(QgsLayerTreeLayer *nodeLayer) override
Return list of legend nodes to be used for a particular layer tree layer node.
static QList< int > _makeNodeOrder(QgsLayerTreeLayer *nodeLayer)
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
QgsMapLayerLegend(QObject *parent=0)
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
QString providerType() const
[ data provider interface ] Which provider is being used for this Raster Layer?
void setIconSize(const QSize &sz)
Set the icon size.
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.
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
virtual QList< QgsLayerTreeModelLegendNode * > createLayerTreeModelLegendNodes(QgsLayerTreeLayer *nodeLayer)=0
Return list of legend nodes to be used for a particular layer tree layer node.
virtual void setUserLabel(const QString &userLabel)
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for the node.
Layer tree node points to a map layer.
#define tr(sourceText)