QGIS API Documentation  2.14.0-Essen
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  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerNode->layer() );
147  if ( vl && vl->geometryType() != QGis::NoGeometry )
148  {
149  mCanvas->setDestinationCrs( vl->crs() );
150  mCanvas->setMapUnits( vl->crs().mapUnits() );
151  break;
152  }
153  }
154  }
155  }
156 
157  mCanvas->setLayerSet( layers );
158 
159  if ( firstLayers )
160  {
161  // if we are moving from zero to non-zero layers, let's zoom to those data
163  }
164 
165  if ( !mFirstCRS.isValid() )
166  {
167  // find out what is the first used CRS in case we may need to turn on OTF projections later
168  Q_FOREACH ( QgsLayerTreeLayer* layerNode, layerNodes )
169  {
170  if ( layerNode->layer() && layerNode->layer()->crs().isValid() )
171  {
172  mFirstCRS = layerNode->layer()->crs();
173  break;
174  }
175  }
176  }
177 
179  {
180  // check whether all layers still have the same CRS
181  Q_FOREACH ( QgsLayerTreeLayer* layerNode, layerNodes )
182  {
183  if ( layerNode->layer() && layerNode->layer()->crs().isValid() && layerNode->layer()->crs() != mFirstCRS )
184  {
187  break;
188  }
189  }
190  }
191 
192  mLastLayerCount = currentLayerCount;
193  if ( currentLayerCount == 0 )
195 
196  mPendingCanvasUpdate = false;
197 }
198 
200 {
201  mFirstCRS = QgsCoordinateReferenceSystem(); // invalidate on project load
202 
203  QDomElement elem = doc.documentElement().firstChildElement( "layer-tree-canvas" );
204  if ( elem.isNull() )
205  {
206  bool oldEnabled;
207  QStringList oldOrder;
208  if ( QgsLayerTreeUtils::readOldLegendLayerOrder( doc.documentElement().firstChildElement( "legend" ), oldEnabled, oldOrder ) )
209  {
210  setHasCustomLayerOrder( oldEnabled );
211  setCustomLayerOrder( oldOrder );
212  }
213  return;
214  }
215 
216  QDomElement customOrderElem = elem.firstChildElement( "custom-order" );
217  if ( !customOrderElem.isNull() )
218  {
219  QStringList order;
220  QDomElement itemElem = customOrderElem.firstChildElement( "item" );
221  while ( !itemElem.isNull() )
222  {
223  order.append( itemElem.text() );
224  itemElem = itemElem.nextSiblingElement( "item" );
225  }
226 
227  setHasCustomLayerOrder( customOrderElem.attribute( "enabled", nullptr ).toInt() );
228  setCustomLayerOrder( order );
229  }
230 }
231 
233 {
234  QDomElement elem = doc.createElement( "layer-tree-canvas" );
235  QDomElement customOrderElem = doc.createElement( "custom-order" );
236  customOrderElem.setAttribute( "enabled", mHasCustomLayerOrder ? 1 : 0 );
237 
238  Q_FOREACH ( const QString& layerId, mCustomLayerOrder )
239  {
240  QDomElement itemElem = doc.createElement( "item" );
241  itemElem.appendChild( doc.createTextNode( layerId ) );
242  customOrderElem.appendChild( itemElem );
243  }
244  elem.appendChild( customOrderElem );
245 
246  doc.documentElement().appendChild( elem );
247 }
248 
250 {
251  if ( QgsLayerTree::isLayer( node ) )
252  {
253  QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
254  layers << QgsMapCanvasLayer( nodeLayer->layer(), nodeLayer->isVisible() == Qt::Checked, nodeLayer->customProperty( "overview", 0 ).toInt() );
255  }
256 
257  Q_FOREACH ( QgsLayerTreeNode* child, node->children() )
258  setCanvasLayers( child, layers );
259 }
260 
262 {
263  if ( mPendingCanvasUpdate )
264  return;
265 
266  mPendingCanvasUpdate = true;
267  QMetaObject::invokeMethod( this, "setCanvasLayers", Qt::QueuedConnection );
268 }
269 
270 void QgsLayerTreeMapCanvasBridge::nodeAddedChildren( QgsLayerTreeNode* node, int indexFrom, int indexTo )
271 {
272  Q_ASSERT( node );
273 
274  // collect layer IDs that have been added in order to put them into custom layer order
275  QStringList layerIds;
277  for ( int i = indexFrom; i <= indexTo; ++i )
278  {
279  QgsLayerTreeNode* child = children.at( i );
280  if ( QgsLayerTree::isLayer( child ) )
281  {
282  layerIds << QgsLayerTree::toLayer( child )->layerId();
283  }
284  else if ( QgsLayerTree::isGroup( child ) )
285  {
286  Q_FOREACH ( QgsLayerTreeLayer* nodeL, QgsLayerTree::toGroup( child )->findLayers() )
287  layerIds << nodeL->layerId();
288  }
289  }
290 
291  Q_FOREACH ( const QString& layerId, layerIds )
292  {
293  if ( !mCustomLayerOrder.contains( layerId ) )
294  mCustomLayerOrder.append( layerId );
295  }
296 
298 
300 }
301 
303 {
304  // no need to disconnect from removed nodes as they are deleted
305 
306  // check whether the layers are still there, if not, remove them from the layer order!
307  QList<int> toRemove;
308  for ( int i = 0; i < mCustomLayerOrder.count(); ++i )
309  {
311  if ( !node )
312  toRemove << i;
313  }
314  for ( int i = toRemove.count() - 1; i >= 0; --i )
315  mCustomLayerOrder.removeAt( toRemove[i] );
317 
319 }
320 
322 {
324 }
325 
327 {
328  Q_UNUSED( node );
329  if ( key == "overview" )
331 }
332 
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:74
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:105
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
QGis::GeometryType geometryType() const
Returns point, line or polygon.
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)
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
Returns the units for the projection used by the CRS.