QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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)
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
QDomNode appendChild(const QDomNode &newChild)
QString attribute(const QString &name, const QString &defValue) const
void nodeCustomPropertyChanged(QgsLayerTreeNode *node, const QString &key)
const T & at(int i) const
const QgsCoordinateReferenceSystem & crs() const
Returns layer&#39;s spatial reference system.
const QObjectList & children() const
Qt::CheckState isVisible() 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
bool hasCrsTransformEnabled() const
returns true if projections are enabled for this layer set
QDomElement nextSiblingElement(const QString &tagName) const
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...
QString layerId() const
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
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
int removeAll(const T &value)
QGis::UnitType mapUnits() const
Returns the units for the projection used by the CRS.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
void nodeAddedChildren(QgsLayerTreeNode *node, int indexFrom, int indexTo)
This class is a base class for nodes in a layer tree.
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)
QgsLayerTreeLayer * findLayer(const QString &layerId) const
Find layer node representing the map layer specified by its ID. Searches recursively the whole sub-tr...
bool isNull() const
QgsMapLayer * layer() const
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
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)
void customLayerOrderChanged(const QStringList &order)
void setMapUnits(QGis::UnitType mapUnits)
Set map units (needed by project properties dialog)
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes. Searches recursively the whole sub-tree.
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
Layer tree node points to a map layer.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.