QGIS API Documentation  2.12.0-Lyon
qgslayertreemapcanvasbridge.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayertreemapcanvasbridge.cpp
3  --------------------------------------
4  Date : May 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 
17 
18 #include "qgslayertree.h"
19 #include "qgslayertreeutils.h"
20 #include "qgsmaplayer.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsmapcanvas.h"
23 
25  : QObject( parent )
26  , mRoot( root )
27  , mCanvas( canvas )
28  , mPendingCanvasUpdate( false )
29  , mHasCustomLayerOrder( false )
30  , mAutoSetupOnFirstLayer( true )
31  , mAutoEnableCrsTransform( true )
32  , mLastLayerCount( root->findLayers().count() )
33 {
34  connect( root, SIGNAL( addedChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeAddedChildren( QgsLayerTreeNode*, int, int ) ) );
35  connect( root, SIGNAL( customPropertyChanged( QgsLayerTreeNode*, QString ) ), this, SLOT( nodeCustomPropertyChanged( QgsLayerTreeNode*, QString ) ) );
36  connect( root, SIGNAL( removedChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeRemovedChildren() ) );
37  connect( root, SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ), this, SLOT( nodeVisibilityChanged() ) );
38 
40 }
41 
43 {
44  setHasCustomLayerOrder( false );
46 }
47 
49 {
50  QStringList order;
51  defaultLayerOrder( mRoot, order );
52  return order;
53 }
54 
56 {
57  if ( QgsLayerTree::isLayer( node ) )
58  {
59  QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
60  order << nodeLayer->layerId();
61  }
62 
63  Q_FOREACH ( QgsLayerTreeNode* child, node->children() )
64  defaultLayerOrder( child, order );
65 }
66 
67 
69 {
70  if ( mHasCustomLayerOrder == state )
71  return;
72 
73  mHasCustomLayerOrder = state;
75 
77 }
78 
80 {
81  if ( mCustomLayerOrder == order )
82  return;
83 
84  // verify that the new order is correct
85  QStringList defOrder( defaultLayerOrder() );
86  QStringList newOrder( order );
87  QStringList sortedNewOrder( order );
88  qSort( defOrder );
89  qSort( sortedNewOrder );
90 
91  if ( defOrder.size() < sortedNewOrder.size() )
92  {
93  // might contain bad layers, but also duplicates
94  QSet<QString> ids( defOrder.toSet() );
95 
96  for ( int i = 0; i < sortedNewOrder.size(); i++ )
97  {
98  if ( !ids.contains( sortedNewOrder[i] ) )
99  {
100  newOrder.removeAll( sortedNewOrder[i] );
101  sortedNewOrder.removeAt( i-- );
102  }
103  }
104  }
105 
106  if ( defOrder != sortedNewOrder )
107  return; // must be permutation of the default order
108 
109  mCustomLayerOrder = newOrder;
111 
112  if ( mHasCustomLayerOrder )
114 }
115 
117 {
119 
120  if ( mHasCustomLayerOrder )
121  {
122  Q_FOREACH ( const QString& layerId, mCustomLayerOrder )
123  {
124  QgsLayerTreeLayer* nodeLayer = mRoot->findLayer( layerId );
125  if ( nodeLayer )
126  layers << QgsMapCanvasLayer( nodeLayer->layer(), nodeLayer->isVisible() == Qt::Checked, nodeLayer->customProperty( "overview", 0 ).toInt() );
127  }
128  }
129  else
130  setCanvasLayers( mRoot, layers );
131 
133  int currentLayerCount = layerNodes.count();
134  bool firstLayers = mAutoSetupOnFirstLayer && mLastLayerCount == 0 && currentLayerCount != 0;
135 
136  if ( firstLayers )
137  {
138  // also setup destination CRS and map units if the OTF projections are not yet enabled
140  {
141  Q_FOREACH ( QgsLayerTreeLayer* layerNode, layerNodes )
142  {
143  if ( layerNode->layer() &&
144  (
145  qobject_cast<QgsVectorLayer *>( layerNode->layer() ) == 0 ||
146  qobject_cast<QgsVectorLayer *>( layerNode->layer() )->geometryType() != QGis::NoGeometry
147  )
148  )
149  {
150  mCanvas->setDestinationCrs( layerNode->layer()->crs() );
151  mCanvas->setMapUnits( layerNode->layer()->crs().mapUnits() );
152  break;
153  }
154  }
155  }
156  }
157 
158  mCanvas->setLayerSet( layers );
159 
160  if ( firstLayers )
161  {
162  // if we are moving from zero to non-zero layers, let's zoom to those data
164  }
165 
166  if ( !mFirstCRS.isValid() )
167  {
168  // find out what is the first used CRS in case we may need to turn on OTF projections later
169  Q_FOREACH ( QgsLayerTreeLayer* layerNode, layerNodes )
170  {
171  if ( layerNode->layer() && layerNode->layer()->crs().isValid() )
172  {
173  mFirstCRS = layerNode->layer()->crs();
174  break;
175  }
176  }
177  }
178 
180  {
181  // check whether all layers still have the same CRS
182  Q_FOREACH ( QgsLayerTreeLayer* layerNode, layerNodes )
183  {
184  if ( layerNode->layer() && layerNode->layer()->crs().isValid() && layerNode->layer()->crs() != mFirstCRS )
185  {
188  break;
189  }
190  }
191  }
192 
193  mLastLayerCount = currentLayerCount;
194  if ( currentLayerCount == 0 )
196 
197  mPendingCanvasUpdate = false;
198 }
199 
201 {
202  mFirstCRS = QgsCoordinateReferenceSystem(); // invalidate on project load
203 
204  QDomElement elem = doc.documentElement().firstChildElement( "layer-tree-canvas" );
205  if ( elem.isNull() )
206  {
207  bool oldEnabled;
208  QStringList oldOrder;
209  if ( QgsLayerTreeUtils::readOldLegendLayerOrder( doc.documentElement().firstChildElement( "legend" ), oldEnabled, oldOrder ) )
210  {
211  setHasCustomLayerOrder( oldEnabled );
212  setCustomLayerOrder( oldOrder );
213  }
214  return;
215  }
216 
217  QDomElement customOrderElem = elem.firstChildElement( "custom-order" );
218  if ( !customOrderElem.isNull() )
219  {
220  QStringList order;
221  QDomElement itemElem = customOrderElem.firstChildElement( "item" );
222  while ( !itemElem.isNull() )
223  {
224  order.append( itemElem.text() );
225  itemElem = itemElem.nextSiblingElement( "item" );
226  }
227 
228  setHasCustomLayerOrder( customOrderElem.attribute( "enabled", 0 ).toInt() );
229  setCustomLayerOrder( order );
230  }
231 }
232 
234 {
235  QDomElement elem = doc.createElement( "layer-tree-canvas" );
236  QDomElement customOrderElem = doc.createElement( "custom-order" );
237  customOrderElem.setAttribute( "enabled", mHasCustomLayerOrder ? 1 : 0 );
238 
239  Q_FOREACH ( const QString& layerId, mCustomLayerOrder )
240  {
241  QDomElement itemElem = doc.createElement( "item" );
242  itemElem.appendChild( doc.createTextNode( layerId ) );
243  customOrderElem.appendChild( itemElem );
244  }
245  elem.appendChild( customOrderElem );
246 
247  doc.documentElement().appendChild( elem );
248 }
249 
251 {
252  if ( QgsLayerTree::isLayer( node ) )
253  {
254  QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
255  layers << QgsMapCanvasLayer( nodeLayer->layer(), nodeLayer->isVisible() == Qt::Checked, nodeLayer->customProperty( "overview", 0 ).toInt() );
256  }
257 
258  Q_FOREACH ( QgsLayerTreeNode* child, node->children() )
259  setCanvasLayers( child, layers );
260 }
261 
263 {
264  if ( mPendingCanvasUpdate )
265  return;
266 
267  mPendingCanvasUpdate = true;
268  QMetaObject::invokeMethod( this, "setCanvasLayers", Qt::QueuedConnection );
269 }
270 
271 void QgsLayerTreeMapCanvasBridge::nodeAddedChildren( QgsLayerTreeNode* node, int indexFrom, int indexTo )
272 {
273  Q_ASSERT( node );
274 
275  // collect layer IDs that have been added in order to put them into custom layer order
276  QStringList layerIds;
278  for ( int i = indexFrom; i <= indexTo; ++i )
279  {
280  QgsLayerTreeNode* child = children.at( i );
281  if ( QgsLayerTree::isLayer( child ) )
282  {
283  layerIds << QgsLayerTree::toLayer( child )->layerId();
284  }
285  else if ( QgsLayerTree::isGroup( child ) )
286  {
287  Q_FOREACH ( QgsLayerTreeLayer* nodeL, QgsLayerTree::toGroup( child )->findLayers() )
288  layerIds << nodeL->layerId();
289  }
290  }
291 
292  Q_FOREACH ( const QString& layerId, layerIds )
293  {
294  if ( !mCustomLayerOrder.contains( layerId ) )
295  mCustomLayerOrder.append( layerId );
296  }
297 
299 
301 }
302 
304 {
305  // no need to disconnect from removed nodes as they are deleted
306 
307  // check whether the layers are still there, if not, remove them from the layer order!
308  QList<int> toRemove;
309  for ( int i = 0; i < mCustomLayerOrder.count(); ++i )
310  {
312  if ( !node )
313  toRemove << i;
314  }
315  for ( int i = toRemove.count() - 1; i >= 0; --i )
316  mCustomLayerOrder.removeAt( toRemove[i] );
318 
320 }
321 
323 {
325 }
326 
328 {
329  Q_UNUSED( node );
330  if ( key == "overview" )
332 }
333 
QObject * child(const char *objName, const char *inheritsClass, bool recursiveSearch) const
static bool readOldLegendLayerOrder(const QDomElement &legendElem, bool &hasCustomOrder, QStringList &order)
Try to load custom layer order from.
Layer tree group node serves as a container for layers and further groups.
void hasCustomLayerOrderChanged(bool)
void setCustomLayerOrder(const QStringList &order)
QDomNode appendChild(const QDomNode &newChild)
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QString attribute(const QString &name, const QString &defValue) const
QgsMapLayer * layer() const
void nodeCustomPropertyChanged(QgsLayerTreeNode *node, const QString &key)
const T & at(int i) const
const QObjectList & children() const
void removeAt(int i)
A class that stores visibility and presence in overview flags together with pointer to the layer...
Definition: qgsmapcanvas.h:76
bool contains(const QString &str, Qt::CaseSensitivity cs) const
QDomElement nextSiblingElement(const QString &tagName) const
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
bool hasCrsTransformEnabled() const
returns true if projections are enabled for this layer set
QSet< T > toSet() const
QDomElement documentElement() const
void setCanvasLayers()
force update of canvas layers from the layer tree. Normally this should not be needed to be called...
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:107
QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group. No type checking is done - use isGroup() to find out whether this operation is ...
Definition: qgslayertree.h:46
int size() const
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
sets destination coordinate reference system
void setCrsTransformEnabled(bool enabled)
sets whether to use projections for this layer set
Qt::CheckState isVisible() const
int count(const T &value) const
void append(const T &value)
QString text() const
int toInt(bool *ok) const
void setAttribute(const QString &name, const QString &value)
void setLayerSet(QList< QgsMapCanvasLayer > &layers)
int toInt(bool *ok, int base) const
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes. Searches recursively the whole sub-tree.
int removeAll(const T &value)
void nodeAddedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
This class is a base class for nodes in a layer tree.
QString layerId() const
QList< QgsLayerTreeNode * > children()
Get list of children of the node. Children are owned by the parent.
bool isLayer(QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:40
void readProject(const QDomDocument &doc)
QDomText createTextNode(const QString &value)
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
bool isNull() const
QgsLayerTreeMapCanvasBridge(QgsLayerTreeGroup *root, QgsMapCanvas *canvas, QObject *parent=0)
Constructor: does not take ownership of the layer tree nor canvas.
bool isValid() const
Find out whether this CRS is correctly initialised and usable.
QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer. No type checking is done - use isLayer() to find out whether this operation is ...
Definition: qgslayertree.h:52
QDomElement firstChildElement(const QString &tagName) const
void zoomToFullExtent()
Zoom to the full extent of all layers.
Class for storing a coordinate reference system (CRS)
QgsLayerTreeLayer * findLayer(const QString &layerId) const
Find layer node representing the map layer specified by its ID. Searches recursively the whole sub-tr...
const QgsCoordinateReferenceSystem & crs() const
Returns layer's spatial reference system.
void customLayerOrderChanged(const QStringList &order)
void setMapUnits(QGis::UnitType mapUnits)
Set map units (needed by project properties dialog)
QDomElement createElement(const QString &tagName)
QgsCoordinateReferenceSystem mFirstCRS
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Represents a vector layer which manages a vector based data sets.
bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:34
Layer tree node points to a map layer.
QGis::UnitType mapUnits() const
Get the units that the projection is in.