QGIS API Documentation  2.11.0-Master
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  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 sortedNewOrder = order;
87  qSort( defOrder );
88  qSort( sortedNewOrder );
89 
90  if ( defOrder.size() < sortedNewOrder.size() )
91  {
92  // some bad layers in the list?
93  for ( int i = 0; i < sortedNewOrder.size(); i++ )
94  {
95  if ( i >= defOrder.size() || defOrder[i] != sortedNewOrder[i] )
96  sortedNewOrder.removeAt( i-- );
97  }
98  }
99 
100  if ( defOrder != sortedNewOrder )
101  return; // must be permutation of the default order
102 
103  mCustomLayerOrder = order;
105 
106  if ( mHasCustomLayerOrder )
108 }
109 
111 {
113 
114  if ( mHasCustomLayerOrder )
115  {
116  foreach ( QString layerId, mCustomLayerOrder )
117  {
118  QgsLayerTreeLayer* nodeLayer = mRoot->findLayer( layerId );
119  if ( nodeLayer )
120  layers << QgsMapCanvasLayer( nodeLayer->layer(), nodeLayer->isVisible() == Qt::Checked, nodeLayer->customProperty( "overview", 0 ).toInt() );
121  }
122  }
123  else
124  setCanvasLayers( mRoot, layers );
125 
127  int currentLayerCount = layerNodes.count();
128  bool firstLayers = mAutoSetupOnFirstLayer && mLastLayerCount == 0 && currentLayerCount != 0;
129 
130  if ( firstLayers )
131  {
132  // also setup destination CRS and map units if the OTF projections are not yet enabled
134  {
135  foreach ( QgsLayerTreeLayer* layerNode, layerNodes )
136  {
137  if ( layerNode->layer() &&
138  (
139  qobject_cast<QgsVectorLayer *>( layerNode->layer() ) == 0 ||
140  qobject_cast<QgsVectorLayer *>( layerNode->layer() )->geometryType() != QGis::NoGeometry
141  )
142  )
143  {
144  mCanvas->setDestinationCrs( layerNode->layer()->crs() );
145  mCanvas->setMapUnits( layerNode->layer()->crs().mapUnits() );
146  break;
147  }
148  }
149  }
150  }
151 
152  mCanvas->setLayerSet( layers );
153 
154  if ( firstLayers )
155  {
156  // if we are moving from zero to non-zero layers, let's zoom to those data
158  }
159 
160  if ( !mFirstCRS.isValid() )
161  {
162  // find out what is the first used CRS in case we may need to turn on OTF projections later
163  foreach ( QgsLayerTreeLayer* layerNode, layerNodes )
164  {
165  if ( layerNode->layer() && layerNode->layer()->crs().isValid() )
166  {
167  mFirstCRS = layerNode->layer()->crs();
168  break;
169  }
170  }
171  }
172 
174  {
175  // check whether all layers still have the same CRS
176  foreach ( QgsLayerTreeLayer* layerNode, layerNodes )
177  {
178  if ( layerNode->layer() && layerNode->layer()->crs().isValid() && layerNode->layer()->crs() != mFirstCRS )
179  {
182  break;
183  }
184  }
185  }
186 
187  mLastLayerCount = currentLayerCount;
188  if ( currentLayerCount == 0 )
190 
191  mPendingCanvasUpdate = false;
192 }
193 
195 {
196  mFirstCRS = QgsCoordinateReferenceSystem(); // invalidate on project load
197 
198  QDomElement elem = doc.documentElement().firstChildElement( "layer-tree-canvas" );
199  if ( elem.isNull() )
200  {
201  bool oldEnabled;
202  QStringList oldOrder;
203  if ( QgsLayerTreeUtils::readOldLegendLayerOrder( doc.documentElement().firstChildElement( "legend" ), oldEnabled, oldOrder ) )
204  {
205  setHasCustomLayerOrder( oldEnabled );
206  setCustomLayerOrder( oldOrder );
207  }
208  return;
209  }
210 
211  QDomElement customOrderElem = elem.firstChildElement( "custom-order" );
212  if ( !customOrderElem.isNull() )
213  {
214  QStringList order;
215  QDomElement itemElem = customOrderElem.firstChildElement( "item" );
216  while ( !itemElem.isNull() )
217  {
218  order.append( itemElem.text() );
219  itemElem = itemElem.nextSiblingElement( "item" );
220  }
221 
222  setHasCustomLayerOrder( customOrderElem.attribute( "enabled", 0 ).toInt() );
223  setCustomLayerOrder( order );
224  }
225 }
226 
228 {
229  QDomElement elem = doc.createElement( "layer-tree-canvas" );
230  QDomElement customOrderElem = doc.createElement( "custom-order" );
231  customOrderElem.setAttribute( "enabled", mHasCustomLayerOrder ? 1 : 0 );
232 
233  foreach ( QString layerId, mCustomLayerOrder )
234  {
235  QDomElement itemElem = doc.createElement( "item" );
236  itemElem.appendChild( doc.createTextNode( layerId ) );
237  customOrderElem.appendChild( itemElem );
238  }
239  elem.appendChild( customOrderElem );
240 
241  doc.documentElement().appendChild( elem );
242 }
243 
245 {
246  if ( QgsLayerTree::isLayer( node ) )
247  {
248  QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
249  layers << QgsMapCanvasLayer( nodeLayer->layer(), nodeLayer->isVisible() == Qt::Checked, nodeLayer->customProperty( "overview", 0 ).toInt() );
250  }
251 
252  foreach ( QgsLayerTreeNode* child, node->children() )
253  setCanvasLayers( child, layers );
254 }
255 
257 {
258  if ( mPendingCanvasUpdate )
259  return;
260 
261  mPendingCanvasUpdate = true;
262  QMetaObject::invokeMethod( this, "setCanvasLayers", Qt::QueuedConnection );
263 }
264 
265 void QgsLayerTreeMapCanvasBridge::nodeAddedChildren( QgsLayerTreeNode* node, int indexFrom, int indexTo )
266 {
267  Q_ASSERT( node );
268 
269  // collect layer IDs that have been added in order to put them into custom layer order
270  QStringList layerIds;
272  for ( int i = indexFrom; i <= indexTo; ++i )
273  {
274  QgsLayerTreeNode* child = children.at( i );
275  if ( QgsLayerTree::isLayer( child ) )
276  {
277  layerIds << QgsLayerTree::toLayer( child )->layerId();
278  }
279  else if ( QgsLayerTree::isGroup( child ) )
280  {
281  foreach ( QgsLayerTreeLayer* nodeL, QgsLayerTree::toGroup( child )->findLayers() )
282  layerIds << nodeL->layerId();
283  }
284  }
285 
286  foreach ( QString layerId, layerIds )
287  {
288  if ( !mCustomLayerOrder.contains( layerId ) )
289  mCustomLayerOrder.append( layerId );
290  }
291 
293 
295 }
296 
298 {
299  // no need to disconnect from removed nodes as they are deleted
300 
301  // check whether the layers are still there, if not, remove them from the layer order!
302  QList<int> toRemove;
303  for ( int i = 0; i < mCustomLayerOrder.count(); ++i )
304  {
306  if ( !node )
307  toRemove << i;
308  }
309  for ( int i = toRemove.count() - 1; i >= 0; --i )
310  mCustomLayerOrder.removeAt( toRemove[i] );
312 
314 }
315 
317 {
319 }
320 
322 {
323  Q_UNUSED( node );
324  if ( key == "overview" )
326 }
327 
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
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: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
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 nodeCustomPropertyChanged(QgsLayerTreeNode *node, QString key)
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.
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.
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.