QGIS API Documentation  2.17.0-Master (3a3b9ab7)
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().isEmpty() )
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  continue;
145 
146  if ( layerNode->layer()->isSpatial() )
147  {
148  mCanvas->setDestinationCrs( layerNode->layer()->crs() );
149  mCanvas->setMapUnits( layerNode->layer()->crs().mapUnits() );
150  break;
151  }
152  }
153  }
154  }
155 
156  mCanvas->setLayerSet( layers );
157 
158  if ( firstLayers )
159  {
160  // if we are moving from zero to non-zero layers, let's zoom to those data
162  }
163 
164  if ( !mFirstCRS.isValid() )
165  {
166  // find out what is the first used CRS in case we may need to turn on OTF projections later
167  Q_FOREACH ( QgsLayerTreeLayer* layerNode, layerNodes )
168  {
169  if ( layerNode->layer() && layerNode->layer()->crs().isValid() )
170  {
171  mFirstCRS = layerNode->layer()->crs();
172  break;
173  }
174  }
175  }
176 
178  {
179  // check whether all layers still have the same CRS
180  Q_FOREACH ( QgsLayerTreeLayer* layerNode, layerNodes )
181  {
182  if ( layerNode->layer() && layerNode->layer()->crs().isValid() && layerNode->layer()->crs() != mFirstCRS )
183  {
186  break;
187  }
188  }
189  }
190 
191  mLastLayerCount = currentLayerCount;
192  if ( currentLayerCount == 0 )
194 
195  mPendingCanvasUpdate = false;
196 }
197 
199 {
200  mFirstCRS = QgsCoordinateReferenceSystem(); // invalidate on project load
201 
202  QDomElement elem = doc.documentElement().firstChildElement( "layer-tree-canvas" );
203  if ( elem.isNull() )
204  {
205  bool oldEnabled;
206  QStringList oldOrder;
207  if ( QgsLayerTreeUtils::readOldLegendLayerOrder( doc.documentElement().firstChildElement( "legend" ), oldEnabled, oldOrder ) )
208  {
209  setHasCustomLayerOrder( oldEnabled );
210  setCustomLayerOrder( oldOrder );
211  }
212  return;
213  }
214 
215  QDomElement customOrderElem = elem.firstChildElement( "custom-order" );
216  if ( !customOrderElem.isNull() )
217  {
218  QStringList order;
219  QDomElement itemElem = customOrderElem.firstChildElement( "item" );
220  while ( !itemElem.isNull() )
221  {
222  order.append( itemElem.text() );
223  itemElem = itemElem.nextSiblingElement( "item" );
224  }
225 
226  setHasCustomLayerOrder( customOrderElem.attribute( "enabled", nullptr ).toInt() );
227  setCustomLayerOrder( order );
228  }
229 }
230 
232 {
233  QDomElement elem = doc.createElement( "layer-tree-canvas" );
234  QDomElement customOrderElem = doc.createElement( "custom-order" );
235  customOrderElem.setAttribute( "enabled", mHasCustomLayerOrder ? 1 : 0 );
236 
237  Q_FOREACH ( const QString& layerId, mCustomLayerOrder )
238  {
239  QDomElement itemElem = doc.createElement( "item" );
240  itemElem.appendChild( doc.createTextNode( layerId ) );
241  customOrderElem.appendChild( itemElem );
242  }
243  elem.appendChild( customOrderElem );
244 
245  doc.documentElement().appendChild( elem );
246 }
247 
249 {
250  if ( QgsLayerTree::isLayer( node ) )
251  {
252  QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
253  layers << QgsMapCanvasLayer( nodeLayer->layer(), nodeLayer->isVisible() == Qt::Checked, nodeLayer->customProperty( "overview", 0 ).toInt() );
254  }
255 
256  Q_FOREACH ( QgsLayerTreeNode* child, node->children() )
257  setCanvasLayers( child, layers );
258 }
259 
261 {
262  if ( mPendingCanvasUpdate )
263  return;
264 
265  mPendingCanvasUpdate = true;
266  QMetaObject::invokeMethod( this, "setCanvasLayers", Qt::QueuedConnection );
267 }
268 
269 void QgsLayerTreeMapCanvasBridge::nodeAddedChildren( QgsLayerTreeNode* node, int indexFrom, int indexTo )
270 {
271  Q_ASSERT( node );
272 
273  // collect layer IDs that have been added in order to put them into custom layer order
274  QStringList layerIds;
276  for ( int i = indexFrom; i <= indexTo; ++i )
277  {
278  QgsLayerTreeNode* child = children.at( i );
279  if ( QgsLayerTree::isLayer( child ) )
280  {
281  layerIds << QgsLayerTree::toLayer( child )->layerId();
282  }
283  else if ( QgsLayerTree::isGroup( child ) )
284  {
285  Q_FOREACH ( QgsLayerTreeLayer* nodeL, QgsLayerTree::toGroup( child )->findLayers() )
286  layerIds << nodeL->layerId();
287  }
288  }
289 
290  Q_FOREACH ( const QString& layerId, layerIds )
291  {
292  if ( !mCustomLayerOrder.contains( layerId ) )
293  mCustomLayerOrder.append( layerId );
294  }
295 
297 
299 }
300 
302 {
303  // no need to disconnect from removed nodes as they are deleted
304 
305  // check whether the layers are still there, if not, remove them from the layer order!
306  QList<int> toRemove;
307  for ( int i = 0; i < mCustomLayerOrder.count(); ++i )
308  {
310  if ( !node )
311  toRemove << i;
312  }
313  for ( int i = toRemove.count() - 1; i >= 0; --i )
314  mCustomLayerOrder.removeAt( toRemove[i] );
316 
318 }
319 
321 {
323 }
324 
326 {
327  Q_UNUSED( node );
328  if ( key == "overview" )
330 }
331 
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)
QgsLayerTreeMapCanvasBridge(QgsLayerTreeGroup *root, QgsMapCanvas *canvas, QObject *parent=nullptr)
Constructor: does not take ownership of the layer tree nor canvas.
A class that stores visibility and presence in overview flags together with pointer to the layer...
Definition: qgsmapcanvas.h:75
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:109
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
bool isValid() const
Returns whether this CRS is correctly initialized 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&#39;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)
bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:34
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
Definition: qgsmaplayer.h:309
Layer tree node points to a map layer.
QGis::UnitType mapUnits() const
Returns the units for the projection used by the CRS.