QGIS API Documentation  2.99.0-Master (c558d51)
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 #include "qgsproject.h"
24 
26  : QObject( parent )
27  , mRoot( root )
28  , mCanvas( canvas )
29  , mPendingCanvasUpdate( false )
30  , mHasCustomLayerOrder( false )
31  , mAutoSetupOnFirstLayer( true )
32  , mAutoEnableCrsTransform( true )
33  , mLastLayerCount( !root->findLayers().isEmpty() )
34 {
35  connect( root, SIGNAL( addedChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeAddedChildren( QgsLayerTreeNode*, int, int ) ) );
36  connect( root, SIGNAL( customPropertyChanged( QgsLayerTreeNode*, QString ) ), this, SLOT( nodeCustomPropertyChanged( QgsLayerTreeNode*, QString ) ) );
37  connect( root, SIGNAL( removedChildren( QgsLayerTreeNode*, int, int ) ), this, SLOT( nodeRemovedChildren() ) );
38  connect( root, SIGNAL( visibilityChanged( QgsLayerTreeNode*, Qt::CheckState ) ), this, SLOT( nodeVisibilityChanged() ) );
39 
41 }
42 
44 {
45  setHasCustomLayerOrder( false );
47 }
48 
50 {
51  QStringList order;
52  defaultLayerOrder( mRoot, order );
53  return order;
54 }
55 
57 {
58  if ( QgsLayerTree::isLayer( node ) )
59  {
60  QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
61  order << nodeLayer->layerId();
62  }
63 
64  Q_FOREACH ( QgsLayerTreeNode* child, node->children() )
65  defaultLayerOrder( child, order );
66 }
67 
68 
70 {
71  if ( mHasCustomLayerOrder == state )
72  return;
73 
74  mHasCustomLayerOrder = state;
76 
78 }
79 
80 void QgsLayerTreeMapCanvasBridge::setCustomLayerOrder( const QStringList& order )
81 {
82  if ( mCustomLayerOrder == order )
83  return;
84 
85  // verify that the new order is correct
86  QStringList defOrder( defaultLayerOrder() );
87  QStringList newOrder( order );
88  QStringList sortedNewOrder( order );
89  qSort( defOrder );
90  qSort( sortedNewOrder );
91 
92  if ( defOrder.size() < sortedNewOrder.size() )
93  {
94  // might contain bad layers, but also duplicates
95  QSet<QString> ids( defOrder.toSet() );
96 
97  for ( int i = 0; i < sortedNewOrder.size(); i++ )
98  {
99  if ( !ids.contains( sortedNewOrder[i] ) )
100  {
101  newOrder.removeAll( sortedNewOrder[i] );
102  sortedNewOrder.removeAt( i-- );
103  }
104  }
105  }
106 
107  if ( defOrder != sortedNewOrder )
108  return; // must be permutation of the default order
109 
110  mCustomLayerOrder = newOrder;
112 
113  if ( mHasCustomLayerOrder )
115 }
116 
118 {
119  QList<QgsMapCanvasLayer> layers;
120 
121  if ( mHasCustomLayerOrder )
122  {
123  Q_FOREACH ( const QString& layerId, mCustomLayerOrder )
124  {
125  QgsLayerTreeLayer* nodeLayer = mRoot->findLayer( layerId );
126  if ( nodeLayer )
127  layers << QgsMapCanvasLayer( nodeLayer->layer(), nodeLayer->isVisible() == Qt::Checked, nodeLayer->customProperty( QStringLiteral( "overview" ), 0 ).toInt() );
128  }
129  }
130  else
131  setCanvasLayers( mRoot, layers );
132 
133  QList<QgsLayerTreeLayer*> layerNodes = mRoot->findLayers();
134  int currentLayerCount = layerNodes.count();
135  bool firstLayers = mAutoSetupOnFirstLayer && mLastLayerCount == 0 && currentLayerCount != 0;
136 
137  if ( firstLayers )
138  {
139  // also setup destination CRS and map units if the OTF projections are not yet enabled
141  {
142  Q_FOREACH ( QgsLayerTreeLayer* layerNode, layerNodes )
143  {
144  if ( !layerNode->layer() )
145  continue;
146 
147  if ( layerNode->layer()->isSpatial() )
148  {
149  mCanvas->setDestinationCrs( layerNode->layer()->crs() );
150  QgsProject::instance()->setCrs( 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  {
189  break;
190  }
191  }
192  }
193 
194  mLastLayerCount = currentLayerCount;
195  if ( currentLayerCount == 0 )
197 
198  mPendingCanvasUpdate = false;
199 }
200 
201 void QgsLayerTreeMapCanvasBridge::readProject( const QDomDocument& doc )
202 {
203  mFirstCRS = QgsCoordinateReferenceSystem(); // invalidate on project load
204 
205  QDomElement elem = doc.documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
206  if ( elem.isNull() )
207  {
208  bool oldEnabled;
209  QStringList oldOrder;
210  if ( QgsLayerTreeUtils::readOldLegendLayerOrder( doc.documentElement().firstChildElement( QStringLiteral( "legend" ) ), oldEnabled, oldOrder ) )
211  {
212  setHasCustomLayerOrder( oldEnabled );
213  setCustomLayerOrder( oldOrder );
214  }
215  return;
216  }
217 
218  QDomElement customOrderElem = elem.firstChildElement( QStringLiteral( "custom-order" ) );
219  if ( !customOrderElem.isNull() )
220  {
221  QStringList order;
222  QDomElement itemElem = customOrderElem.firstChildElement( QStringLiteral( "item" ) );
223  while ( !itemElem.isNull() )
224  {
225  order.append( itemElem.text() );
226  itemElem = itemElem.nextSiblingElement( QStringLiteral( "item" ) );
227  }
228 
229  setHasCustomLayerOrder( customOrderElem.attribute( QStringLiteral( "enabled" ), QString() ).toInt() );
230  setCustomLayerOrder( order );
231  }
232 }
233 
235 {
236  QDomElement elem = doc.createElement( QStringLiteral( "layer-tree-canvas" ) );
237  QDomElement customOrderElem = doc.createElement( QStringLiteral( "custom-order" ) );
238  customOrderElem.setAttribute( QStringLiteral( "enabled" ), mHasCustomLayerOrder ? 1 : 0 );
239 
240  Q_FOREACH ( const QString& layerId, mCustomLayerOrder )
241  {
242  QDomElement itemElem = doc.createElement( QStringLiteral( "item" ) );
243  itemElem.appendChild( doc.createTextNode( layerId ) );
244  customOrderElem.appendChild( itemElem );
245  }
246  elem.appendChild( customOrderElem );
247 
248  doc.documentElement().appendChild( elem );
249 }
250 
251 void QgsLayerTreeMapCanvasBridge::setCanvasLayers( QgsLayerTreeNode *node, QList<QgsMapCanvasLayer> &layers )
252 {
253  if ( QgsLayerTree::isLayer( node ) )
254  {
255  QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
256  layers << QgsMapCanvasLayer( nodeLayer->layer(), nodeLayer->isVisible() == Qt::Checked, nodeLayer->customProperty( QStringLiteral( "overview" ), 0 ).toInt() );
257  }
258 
259  Q_FOREACH ( QgsLayerTreeNode* child, node->children() )
260  setCanvasLayers( child, layers );
261 }
262 
264 {
265  if ( mPendingCanvasUpdate )
266  return;
267 
268  mPendingCanvasUpdate = true;
269  QMetaObject::invokeMethod( this, "setCanvasLayers", Qt::QueuedConnection );
270 }
271 
272 void QgsLayerTreeMapCanvasBridge::nodeAddedChildren( QgsLayerTreeNode* node, int indexFrom, int indexTo )
273 {
274  Q_ASSERT( node );
275 
276  // collect layer IDs that have been added in order to put them into custom layer order
277  QStringList layerIds;
278  QList<QgsLayerTreeNode*> children = node->children();
279  for ( int i = indexFrom; i <= indexTo; ++i )
280  {
281  QgsLayerTreeNode* child = children.at( i );
282  if ( QgsLayerTree::isLayer( child ) )
283  {
284  layerIds << QgsLayerTree::toLayer( child )->layerId();
285  }
286  else if ( QgsLayerTree::isGroup( child ) )
287  {
288  Q_FOREACH ( QgsLayerTreeLayer* nodeL, QgsLayerTree::toGroup( child )->findLayers() )
289  layerIds << nodeL->layerId();
290  }
291  }
292 
293  Q_FOREACH ( const QString& layerId, layerIds )
294  {
295  if ( !mCustomLayerOrder.contains( layerId ) )
296  mCustomLayerOrder.append( layerId );
297  }
298 
300 
302 }
303 
305 {
306  // no need to disconnect from removed nodes as they are deleted
307 
308  // check whether the layers are still there, if not, remove them from the layer order!
309  QList<int> toRemove;
310  for ( int i = 0; i < mCustomLayerOrder.count(); ++i )
311  {
313  if ( !node )
314  toRemove << i;
315  }
316  for ( int i = toRemove.count() - 1; i >= 0; --i )
317  mCustomLayerOrder.removeAt( toRemove[i] );
319 
321 }
322 
324 {
326 }
327 
329 {
330  Q_UNUSED( node );
331  if ( key == QLatin1String( "overview" ) )
333 }
334 
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. Properties are stored in a map and saved in project file...
QgsMapLayer * layer() const
void nodeCustomPropertyChanged(QgsLayerTreeNode *node, const QString &key)
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:73
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:106
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 setMapUnits(QgsUnitTypes::DistanceUnit mapUnits)
Set map units (needed by project properties dialog)
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)
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the project&#39;s native coordinate reference system.
Definition: qgsproject.cpp:422
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)
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
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:348
void zoomToFullExtent()
Zoom to the full extent of all layers.
This class represents 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...
void customLayerOrderChanged(const QStringList &order)
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
QgsCoordinateReferenceSystem mFirstCRS
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:343
Layer tree node points to a map layer.