QGIS API Documentation  3.25.0-Master (6b426f5f8a)
qgsgrouplayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgrouplayer.cpp
3  ----------------
4  Date : September 2021
5  Copyright : (C) 2021 by Nyall Dawson
6  Email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsgrouplayer.h"
19 #include "qgsmaplayerfactory.h"
20 #include "qgspainting.h"
21 #include "qgsmaplayerlistutils_p.h"
22 #include "qgsgrouplayerrenderer.h"
23 #include "qgsmaplayerref.h"
24 #include "qgsvectorlayer.h"
25 #include "qgscoordinatetransform.h"
26 #include "qgspainteffect.h"
27 #include "qgsmessagelog.h"
28 #include "qgspainteffectregistry.h"
29 #include "qgsapplication.h"
30 #include "qgsmaplayerutils.h"
31 
32 QgsGroupLayer::QgsGroupLayer( const QString &name, const LayerOptions &options )
34  , mTransformContext( options.transformContext )
35 {
36  mShouldValidateCrs = false;
37  mValid = true;
38 
39  mPaintEffect.reset( QgsPaintEffectRegistry::defaultStack() );
40  mPaintEffect->setEnabled( false );
41 
42  QgsDataProvider::ProviderOptions providerOptions;
43  providerOptions.transformContext = options.transformContext;
44  mDataProvider = new QgsGroupLayerDataProvider( providerOptions, QgsDataProvider::ReadFlags() );
45 }
46 
48 {
49  emit willBeDeleted();
50  delete mDataProvider;
51 }
52 
54 {
55  const QgsGroupLayer::LayerOptions options( mTransformContext );
56  std::unique_ptr< QgsGroupLayer > layer = std::make_unique< QgsGroupLayer >( name(), options );
57  QgsMapLayer::clone( layer.get() );
58  layer->setChildLayers( _qgis_listRefToRaw( mChildren ) );
59  layer->setPaintEffect( mPaintEffect ? mPaintEffect->clone() : nullptr );
60  return layer.release();
61 }
62 
64 {
65  return new QgsGroupLayerRenderer( this, context );
66 }
67 
69 {
70  return QgsMapLayerUtils::combinedExtent( childLayers(), crs(), mTransformContext );
71 }
72 
74 {
75  if ( mDataProvider )
76  mDataProvider->setTransformContext( context );
77 
78  mTransformContext = context;
80 }
81 
82 bool QgsGroupLayer::readXml( const QDomNode &layerNode, QgsReadWriteContext &context )
83 {
85  {
86  return false;
87  }
88 
89  const QList< QgsMapLayer * > currentLayers = _qgis_listRefToRaw( mChildren );
90  for ( QgsMapLayer *layer : currentLayers )
91  {
92  disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapLayer::triggerRepaint );
93  }
94 
95  mChildren.clear();
96  const QDomNodeList childLayersElements = layerNode.toElement().elementsByTagName( QStringLiteral( "childLayers" ) );
97  const QDomNodeList children = childLayersElements.at( 0 ).childNodes();
98  for ( int i = 0; i < children.size(); ++i )
99  {
100  const QDomElement childElement = children.at( i ).toElement();
101  const QString id = childElement.attribute( QStringLiteral( "layerid" ) );
102  mChildren.append( QgsMapLayerRef( id ) );
103  }
105 
106  QString errorMsg;
107  readSymbology( layerNode, errorMsg, context );
108 
109  triggerRepaint();
110 
111  return mValid;
112 }
113 
114 bool QgsGroupLayer::writeXml( QDomNode &layer_node, QDomDocument &doc, const QgsReadWriteContext &context ) const
115 {
116  // first get the layer element so that we can append the type attribute
117  QDomElement mapLayerNode = layer_node.toElement();
118 
119  if ( mapLayerNode.isNull() )
120  {
121  QgsDebugMsgLevel( QStringLiteral( "can't find maplayer node" ), 2 );
122  return false;
123  }
124 
125  mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( QgsMapLayerType::GroupLayer ) );
126 
127  QDomElement childLayersElement = doc.createElement( QStringLiteral( "childLayers" ) );
128  for ( auto it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
129  {
130  QDomElement childElement = doc.createElement( QStringLiteral( "child" ) );
131  childElement.setAttribute( QStringLiteral( "layerid" ), it->layerId );
132  childLayersElement.appendChild( childElement );
133  }
134  mapLayerNode.appendChild( childLayersElement );
135 
136  // renderer specific settings
137  QString errorMsg;
138  return writeSymbology( layer_node, doc, errorMsg, context );
139 }
140 
141 bool QgsGroupLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &, const QgsReadWriteContext &, QgsMapLayer::StyleCategories categories ) const
142 {
143  // add the layer opacity
144  if ( categories.testFlag( Rendering ) )
145  {
146  QDomElement layerOpacityElem = doc.createElement( QStringLiteral( "layerOpacity" ) );
147  const QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
148  layerOpacityElem.appendChild( layerOpacityText );
149  node.appendChild( layerOpacityElem );
150 
151  if ( mPaintEffect && !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect.get() ) )
152  {
153  QDomElement paintEffectElement = doc.createElement( QStringLiteral( "paintEffect" ) );
154  mPaintEffect->saveProperties( doc, paintEffectElement );
155  node.appendChild( paintEffectElement );
156  }
157  }
158 
159  if ( categories.testFlag( Symbology ) )
160  {
161  // add the blend mode field
162  QDomElement blendModeElem = doc.createElement( QStringLiteral( "blendMode" ) );
163  const QDomText blendModeText = doc.createTextNode( QString::number( QgsPainting::getBlendModeEnum( blendMode() ) ) );
164  blendModeElem.appendChild( blendModeText );
165  node.appendChild( blendModeElem );
166  }
167 
168  return true;
169 }
170 
171 bool QgsGroupLayer::readSymbology( const QDomNode &node, QString &, QgsReadWriteContext &, QgsMapLayer::StyleCategories categories )
172 {
173  if ( categories.testFlag( Rendering ) )
174  {
175  const QDomNode layerOpacityNode = node.namedItem( QStringLiteral( "layerOpacity" ) );
176  if ( !layerOpacityNode.isNull() )
177  {
178  const QDomElement e = layerOpacityNode.toElement();
179  setOpacity( e.text().toDouble() );
180  }
181 
182  //restore layer effect
183  const QDomElement effectElem = node.namedItem( QStringLiteral( "paintEffect" ) ).toElement();
184  if ( !effectElem.isNull() )
185  {
186  const QDomElement effectPropertiesElem = effectElem.firstChildElement( QStringLiteral( "effect" ) ).toElement();
187  mPaintEffect.reset( QgsApplication::paintEffectRegistry()->createEffect( effectPropertiesElem ) );
188  }
189  else
190  {
191  mPaintEffect.reset( QgsPaintEffectRegistry::defaultStack() );
192  mPaintEffect->setEnabled( false );
193  }
194  }
195 
196  if ( categories.testFlag( Symbology ) )
197  {
198  // get and set the blend mode if it exists
199  const QDomNode blendModeNode = node.namedItem( QStringLiteral( "blendMode" ) );
200  if ( !blendModeNode.isNull() )
201  {
202  const QDomElement e = blendModeNode.toElement();
203  setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( e.text().toInt() ) ) );
204  }
205  }
206 
207  return true;
208 }
209 
211 {
212  return mDataProvider;
213 }
214 
216 {
217  return mDataProvider;
218 }
219 
221 {
222  QString metadata = QStringLiteral( "<html>\n<body>\n<h1>" ) + tr( "General" ) + QStringLiteral( "</h1>\n<hr>\n" ) + QStringLiteral( "<table class=\"list-view\">\n" );
223 
224  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Name" ) + QStringLiteral( "</td><td>" ) + name() + QStringLiteral( "</td></tr>\n" );
225 
226  // Extent
227  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Extent" ) + QStringLiteral( "</td><td>" ) + extent().toString() + QStringLiteral( "</td></tr>\n" );
228 
229 
230  metadata += QLatin1String( "\n</body>\n</html>\n" );
231  return metadata;
232 }
233 
235 {
237  for ( int i = 0; i < mChildren.size(); ++i )
238  {
239  mChildren[i].resolve( project );
240 
241  if ( mChildren[i].layer )
242  {
243  connect( mChildren[i].layer, &QgsMapLayer::repaintRequested, this, &QgsMapLayer::triggerRepaint, Qt::UniqueConnection );
244 
245  // group layer inherits first valid child layer's crs
246  if ( !crs().isValid() )
247  {
248  setCrs( mChildren[i].layer->crs() );
249  mDataProvider->setCrs( crs() );
250  }
251  }
252  }
254 }
255 
256 void QgsGroupLayer::setChildLayers( const QList< QgsMapLayer * > &layers )
257 {
258  const QList< QgsMapLayer * > currentLayers = _qgis_listRefToRaw( mChildren );
259  for ( QgsMapLayer *layer : layers )
260  {
261  if ( !currentLayers.contains( layer ) )
262  {
263  connect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapLayer::triggerRepaint, Qt::UniqueConnection );
264  }
265  }
266  for ( QgsMapLayer *layer : currentLayers )
267  {
268  if ( !layers.contains( layer ) )
269  {
270  disconnect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapLayer::triggerRepaint );
271  }
272  }
273  mChildren = _qgis_listRawToRef( layers );
274 
275  // group layer inherits first valid child layer's crs
276  for ( const QgsMapLayer *layer : layers )
277  {
278  if ( layer->isValid() && layer->crs().isValid( ) )
279  {
280  setCrs( layer->crs() );
281  mDataProvider->setCrs( crs() );
282  break;
283  }
284  }
285 
286  triggerRepaint();
287 }
288 
289 QList< QgsMapLayer * > QgsGroupLayer::childLayers() const
290 {
291  return _qgis_listRefToRaw( mChildren );
292 }
293 
295 {
296  return mPaintEffect.get();
297 }
298 
300 {
301  mPaintEffect.reset( effect );
302 }
303 
304 //
305 // QgsGroupLayerDataProvider
306 //
308 QgsGroupLayerDataProvider::QgsGroupLayerDataProvider(
309  const ProviderOptions &options,
310  QgsDataProvider::ReadFlags flags )
311  : QgsDataProvider( QString(), options, flags )
312 {}
313 
314 void QgsGroupLayerDataProvider::setCrs( const QgsCoordinateReferenceSystem &crs )
315 {
316  mCrs = crs;
317 }
318 
320 {
321  return mCrs;
322 }
323 
324 QString QgsGroupLayerDataProvider::name() const
325 {
326  return QStringLiteral( "annotation" );
327 }
328 
329 QString QgsGroupLayerDataProvider::description() const
330 {
331  return QString();
332 }
333 
334 QgsRectangle QgsGroupLayerDataProvider::extent() const
335 {
336  return QgsRectangle();
337 }
338 
339 bool QgsGroupLayerDataProvider::isValid() const
340 {
341  return true;
342 }
344 
static QgsPaintEffectRegistry * paintEffectRegistry()
Returns the application's paint effect registry, used for managing paint effects.
This class represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Abstract base class for spatial data provider implementations.
Implementation of threaded rendering for group layers.
A map layer which consists of a set of child layers, where all component layers are rendered as a sin...
Definition: qgsgrouplayer.h:42
void setPaintEffect(QgsPaintEffect *effect)
Sets the current paint effect for the renderer.
void resolveReferences(QgsProject *project) override
Resolve references to other layers (kept as layer IDs after reading XML) into layer objects.
bool readSymbology(const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) override
Read the symbology for the current layer from the DOM node supplied.
QgsGroupLayer(const QString &name, const QgsGroupLayer::LayerOptions &options)
Constructor for a new QgsGroupLayer with the specified layer name.
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the group layer.
QgsGroupLayer * clone() const override
Returns a new instance equivalent to this one except for the id which is still unique.
QList< QgsMapLayer * > childLayers() const
Returns the child layers contained by the group.
~QgsGroupLayer() override
QgsMapLayerRenderer * createMapRenderer(QgsRenderContext &rendererContext) override
Returns new instance of QgsMapLayerRenderer that will be used for rendering of given context.
QgsDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
void setChildLayers(const QList< QgsMapLayer * > &layers)
Sets the child layers contained by the group.
bool writeXml(QDomNode &layer_node, QDomDocument &doc, const QgsReadWriteContext &context) const override
Called by writeLayerXML(), used by children to write state specific to them to project files.
void setTransformContext(const QgsCoordinateTransformContext &context) override
Sets the coordinate transform context to transformContext.
bool readXml(const QDomNode &layerNode, QgsReadWriteContext &context) override
Called by readLayerXML(), used by children to read state specific to them from project files.
bool writeSymbology(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &, StyleCategories categories=AllStyleCategories) const override
Write the style for the layer into the document provided.
QString htmlMetadata() const override
Obtain a formatted HTML string containing assorted metadata for this layer.
QgsRectangle extent() const override
Returns the extent of the layer.
static QString typeToString(QgsMapLayerType type)
Converts a map layer type to a string value.
Base class for utility classes that encapsulate information necessary for rendering of map layers.
static QgsRectangle combinedExtent(const QList< QgsMapLayer * > &layers, const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &transformContext)
Returns the combined extent of a list of layers.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QString name
Definition: qgsmaplayer.h:76
void setBlendMode(QPainter::CompositionMode blendMode)
Set the blending mode used for rendering a layer.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted.
QgsLayerMetadata metadata
Definition: qgsmaplayer.h:78
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
virtual void setOpacity(double opacity)
Sets the opacity for the layer, where opacity is a value between 0 (totally transparent) and 1....
virtual QgsMapLayer * clone() const =0
Returns a new instance equivalent to this one except for the id which is still unique.
void willBeDeleted()
Emitted in the destructor when the layer is about to be deleted, but it is still in a perfectly valid...
virtual void resolveReferences(QgsProject *project)
Resolve references to other layers (kept as layer IDs after reading XML) into layer objects.
bool isValid
Definition: qgsmaplayer.h:81
@ FlagDontResolveLayers
Don't resolve layer paths or create data providers for layers.
Definition: qgsmaplayer.h:640
QgsMapLayer::ReadFlags mReadFlags
Read flags. It's up to the subclass to respect these when restoring state from XML.
Definition: qgsmaplayer.h:1974
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
QgsProject * project() const
Returns the parent project if this map layer is added to a project.
double opacity
Definition: qgsmaplayer.h:82
bool mValid
Indicates if the layer is valid and can be drawn.
Definition: qgsmaplayer.h:1925
@ Symbology
Symbology.
Definition: qgsmaplayer.h:161
@ Rendering
Rendering: scale visibility, simplify method, opacity.
Definition: qgsmaplayer.h:170
void invalidateWgs84Extent()
Invalidates the WGS84 extent.
bool mShouldValidateCrs
true if the layer's CRS should be validated and invalid CRSes are not permitted.
Definition: qgsmaplayer.h:1981
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer's spatial reference system.
static QgsPaintEffect * defaultStack()
Returns a new effect stack consisting of a sensible selection of default effects.
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
Base class for visual effects which can be applied to QPicture drawings.
static QgsPainting::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode.
Definition: qgspainting.cpp:80
static QPainter::CompositionMode getCompositionMode(QgsPainting::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:20
BlendMode
Blending modes enum defining the available composition modes that can be used when rendering a layer.
Definition: qgspainting.h:37
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:103
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
Contains information about the context of a rendering operation.
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgis.h:47
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
_LayerRef< QgsMapLayer > QgsMapLayerRef
const QgsCoordinateReferenceSystem & crs
Setting options for creating vector data providers.
QgsCoordinateTransformContext transformContext
Coordinate transform context.
Setting options for loading group layers.
Definition: qgsgrouplayer.h:52
QgsCoordinateTransformContext transformContext
Coordinate transform context.
Definition: qgsgrouplayer.h:64