QGIS API Documentation  2.8.6-Wien
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 
79 void QgsLayerTreeMapCanvasBridge::setCustomLayerOrder( const QStringList& order )
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 {
118  QList<QgsMapCanvasLayer> layers;
119 
120  if ( mHasCustomLayerOrder )
121  {
122  foreach ( 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 
132  QList<QgsLayerTreeLayer*> layerNodes = mRoot->findLayers();
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  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  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  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 
200 void QgsLayerTreeMapCanvasBridge::readProject( const QDomDocument& doc )
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  foreach ( 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 
250 void QgsLayerTreeMapCanvasBridge::setCanvasLayers( QgsLayerTreeNode *node, QList<QgsMapCanvasLayer> &layers )
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  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;
277  QList<QgsLayerTreeNode*> children = node->children();
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  foreach ( QgsLayerTreeLayer* nodeL, QgsLayerTree::toGroup( child )->findLayers() )
288  layerIds << nodeL->layerId();
289  }
290  }
291 
292  foreach ( 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 
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)
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QgsMapLayer * layer() const
A class that stores visibility and presence in overview flags together with pointer to the layer...
Definition: qgsmapcanvas.h:75
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
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
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
void nodeCustomPropertyChanged(QgsLayerTreeNode *node, QString key)
void setLayerSet(QList< QgsMapCanvasLayer > &layers)
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)
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
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)
QgsCoordinateReferenceSystem mFirstCRS
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.