QGIS API Documentation  3.20.0-Odense (decaadbb31)
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 "qgsmapoverviewcanvas.h"
24 #include "qgsproject.h"
25 #include "qgssettings.h"
26 #include "qgsgui.h"
27 
29  : QObject( parent )
30  , mRoot( root )
31  , mCanvas( canvas )
32  , mPendingCanvasUpdate( false )
33  , mAutoSetupOnFirstLayer( true )
34  , mHasLayersLoaded( !root->findLayers().isEmpty() )
35 {
36  connect( root, &QgsLayerTreeGroup::customPropertyChanged, this, &QgsLayerTreeMapCanvasBridge::nodeCustomPropertyChanged );
37  connect( root, &QgsLayerTreeNode::visibilityChanged, this, &QgsLayerTreeMapCanvasBridge::nodeVisibilityChanged );
38  connect( root, &QgsLayerTree::layerOrderChanged, this, &QgsLayerTreeMapCanvasBridge::deferredSetCanvasLayers );
39 
40  connect( QgsProject::instance(), &QgsProject::layersAdded, this, &QgsLayerTreeMapCanvasBridge::layersAdded );
41 
43 }
44 
46 {
47  QList<QgsMapLayer *> canvasLayers, overviewLayers, allLayerOrder;
48 
49  if ( mRoot->hasCustomLayerOrder() )
50  {
51  const QList<QgsMapLayer *> customOrderLayers = mRoot->customLayerOrder();
52  for ( const QgsMapLayer *layer : customOrderLayers )
53  {
54  QgsLayerTreeLayer *nodeLayer = mRoot->findLayer( layer->id() );
55  if ( nodeLayer )
56  {
57  if ( !nodeLayer->layer()->isSpatial() )
58  continue;
59 
60  allLayerOrder << nodeLayer->layer();
61  if ( nodeLayer->isVisible() )
62  canvasLayers << nodeLayer->layer();
63  if ( nodeLayer->customProperty( QStringLiteral( "overview" ), 0 ).toInt() )
64  overviewLayers << nodeLayer->layer();
65  }
66  }
67  }
68  else
69  {
70  setCanvasLayers( mRoot, canvasLayers, overviewLayers, allLayerOrder );
71  }
72 
73  const QList<QgsLayerTreeLayer *> layerNodes = mRoot->findLayers();
74  int currentSpatialLayerCount = 0;
75  int currentValidSpatialLayerCount = 0;
76  for ( QgsLayerTreeLayer *layerNode : layerNodes )
77  {
78  if ( layerNode->layer() && layerNode->layer()->isSpatial() )
79  {
80  currentSpatialLayerCount++;
81  if ( layerNode->layer()->isValid() )
82  currentValidSpatialLayerCount++;
83  }
84  }
85 
86  bool firstLayers = mAutoSetupOnFirstLayer && !mHasLayersLoaded && currentSpatialLayerCount != 0;
87  bool firstValidLayers = mAutoSetupOnFirstLayer && !mHasValidLayersLoaded && currentValidSpatialLayerCount != 0;
88 
89  mCanvas->setLayers( canvasLayers );
90  if ( mOverviewCanvas )
91  mOverviewCanvas->setLayers( overviewLayers );
92 
93  if ( firstValidLayers )
94  {
95  // if we are moving from zero to non-zero layers, let's zoom to those data (only consider valid layers here!)
96  mCanvas->zoomToProjectExtent();
97  }
98 
99  if ( !mFirstCRS.isValid() )
100  {
101  // find out what is the first used CRS in case we may need to turn on OTF projections later
102  for ( const QgsLayerTreeLayer *layerNode : layerNodes )
103  {
104  if ( layerNode->layer() && layerNode->layer()->crs().isValid() )
105  {
106  mFirstCRS = layerNode->layer()->crs();
107  break;
108  }
109  }
110  }
111 
112  if ( mFirstCRS.isValid() && firstLayers )
113  {
114  const QgsGui::ProjectCrsBehavior projectCrsBehavior = QgsSettings().enumValue( QStringLiteral( "/projections/newProjectCrsBehavior" ), QgsGui::UseCrsOfFirstLayerAdded, QgsSettings::App );
115  switch ( projectCrsBehavior )
116  {
118  {
119  const bool planimetric = QgsSettings().value( QStringLiteral( "measure/planimetric" ), true, QgsSettings::Core ).toBool();
120  // Only adjust ellipsoid to CRS if it's not set to planimetric
121  QgsProject::instance()->setCrs( mFirstCRS, !planimetric );
122  break;
123  }
124 
126  break;
127  }
128  }
129 
130  mHasLayersLoaded = currentSpatialLayerCount;
131  mHasValidLayersLoaded = currentValidSpatialLayerCount;
132  if ( currentSpatialLayerCount == 0 )
133  mFirstCRS = QgsCoordinateReferenceSystem();
134 
135  mPendingCanvasUpdate = false;
136 
137  emit canvasLayersChanged( canvasLayers );
138 }
139 
140 void QgsLayerTreeMapCanvasBridge::setCanvasLayers( QgsLayerTreeNode *node, QList<QgsMapLayer *> &canvasLayers, QList<QgsMapLayer *> &overviewLayers, QList<QgsMapLayer *> &allLayers )
141 {
142  if ( QgsLayerTree::isLayer( node ) )
143  {
144  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
145  if ( nodeLayer->layer() && nodeLayer->layer()->isSpatial() )
146  {
147  allLayers << nodeLayer->layer();
148  if ( nodeLayer->isVisible() )
149  canvasLayers << nodeLayer->layer();
150  if ( nodeLayer->customProperty( QStringLiteral( "overview" ), 0 ).toInt() )
151  overviewLayers << nodeLayer->layer();
152  }
153  }
154 
155  const QList<QgsLayerTreeNode *> children = node->children();
156  for ( QgsLayerTreeNode *child : children )
157  setCanvasLayers( child, canvasLayers, overviewLayers, allLayers );
158 }
159 
160 void QgsLayerTreeMapCanvasBridge::deferredSetCanvasLayers()
161 {
162  if ( mPendingCanvasUpdate )
163  return;
164 
165  mPendingCanvasUpdate = true;
166  QMetaObject::invokeMethod( this, "setCanvasLayers", Qt::QueuedConnection );
167 }
168 
169 void QgsLayerTreeMapCanvasBridge::nodeVisibilityChanged()
170 {
171  deferredSetCanvasLayers();
172 }
173 
174 void QgsLayerTreeMapCanvasBridge::nodeCustomPropertyChanged( QgsLayerTreeNode *node, const QString &key )
175 {
176  Q_UNUSED( node )
177  if ( key == QLatin1String( "overview" ) )
178  deferredSetCanvasLayers();
179 }
180 
181 void QgsLayerTreeMapCanvasBridge::layersAdded( const QList<QgsMapLayer *> &layers )
182 {
183  for ( QgsMapLayer *l : layers )
184  {
185  if ( l )
186  {
187  connect( l, &QgsMapLayer::dataSourceChanged, this, [ this, l ]
188  {
189  if ( l->isValid() && l->isSpatial() && mAutoSetupOnFirstLayer && !mHasValidLayersLoaded )
190  {
191  mHasValidLayersLoaded = true;
192  // if we are moving from zero valid layers to non-zero VALID layers, let's zoom to those data
193  mCanvas->zoomToProjectExtent();
194  }
195  } );
196  }
197  }
198 }
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
ProjectCrsBehavior
Defines the behavior to use when setting the CRS for a newly created project.
Definition: qgsgui.h:63
@ UseCrsOfFirstLayerAdded
Set the project CRS to the CRS of the first layer added to a new project.
Definition: qgsgui.h:64
@ UsePresetCrs
Always set new projects to use a preset default CRS.
Definition: qgsgui.h:65
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
Layer tree node points to a map layer.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
Q_INVOKABLE void setCanvasLayers()
force update of canvas layers from the layer tree. Normally this should not be needed to be called.
void canvasLayersChanged(const QList< QgsMapLayer * > &layers)
Emitted when the set of layers (or order of layers) visible in the canvas changes.
QgsLayerTreeMapCanvasBridge(QgsLayerTree *root, QgsMapCanvas *canvas, QObject *parent SIP_TRANSFERTHIS=nullptr)
Constructor: does not take ownership of the layer tree nor canvas.
This class is a base class for nodes in a layer tree.
bool isVisible() const
Returns whether a node is really visible (ie checked and all its ancestors checked as well)
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.
void customPropertyChanged(QgsLayerTreeNode *node, const QString &key)
Emitted when a custom property of a node within the tree has been changed or removed.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void visibilityChanged(QgsLayerTreeNode *node)
Emitted when check state of a node within the tree has been changed.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:33
bool hasCustomLayerOrder() const
Determines if the layer order should be derived from the layer tree or if a custom override order sha...
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
void layerOrderChanged()
Emitted when the layer order has changed.
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:86
void zoomToProjectExtent()
Zoom to the full extent the project associated with this canvas.
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers that should be shown in the canvas.
Base class for all map layer types.
Definition: qgsmaplayer.h:70
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
void dataSourceChanged()
Emitted whenever the layer's data source has been changed.
void setLayers(const QList< QgsMapLayer * > &layers)
updates layer set for overview
void setCrs(const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid=false)
Sets the project's native coordinate reference system.
Definition: qgsproject.cpp:728
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
void layersAdded(const QList< QgsMapLayer * > &layers)
Emitted when one or more layers were added to the registry.