QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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"
23#include "qgsmaplayerref.h"
24#include "qgspainteffect.h"
26#include "qgsapplication.h"
27#include "qgsmaplayerutils.h"
28#include "qgsthreadingutils.h"
29
30QgsGroupLayer::QgsGroupLayer( const QString &name, const LayerOptions &options )
31 : QgsMapLayer( Qgis::LayerType::Group, name )
32 , mTransformContext( options.transformContext )
33{
34 mShouldValidateCrs = false;
35 mValid = true;
36
37 mPaintEffect.reset( QgsPaintEffectRegistry::defaultStack() );
38 mPaintEffect->setEnabled( false );
39
41 providerOptions.transformContext = options.transformContext;
42 mDataProvider = new QgsGroupLayerDataProvider( providerOptions, QgsDataProvider::ReadFlags() );
43}
44
46{
47 emit willBeDeleted();
48 delete mDataProvider;
49}
50
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{
66
67 return new QgsGroupLayerRenderer( this, context );
68}
69
71{
73
74 return QgsMapLayerUtils::combinedExtent( childLayers(), crs(), mTransformContext );
75}
76
78{
80
81 if ( mDataProvider )
82 mDataProvider->setTransformContext( context );
83
84 mTransformContext = context;
86}
87
88bool QgsGroupLayer::readXml( const QDomNode &layerNode, QgsReadWriteContext &context )
89{
91
93 {
94 return false;
95 }
96
97 const QList< QgsMapLayer * > currentLayers = _qgis_listRefToRaw( mChildren );
98 for ( QgsMapLayer *layer : currentLayers )
99 {
101 }
102
103 mChildren.clear();
104 const QDomNodeList childLayersElements = layerNode.toElement().elementsByTagName( QStringLiteral( "childLayers" ) );
105 const QDomNodeList children = childLayersElements.at( 0 ).childNodes();
106 for ( int i = 0; i < children.size(); ++i )
107 {
108 const QDomElement childElement = children.at( i ).toElement();
109 const QString id = childElement.attribute( QStringLiteral( "layerid" ) );
110 mChildren.append( QgsMapLayerRef( id ) );
111 }
113
114 QString errorMsg;
115 readSymbology( layerNode, errorMsg, context );
116
118
119 return mValid;
120}
121
122bool QgsGroupLayer::writeXml( QDomNode &layer_node, QDomDocument &doc, const QgsReadWriteContext &context ) const
123{
125
126 // first get the layer element so that we can append the type attribute
127 QDomElement mapLayerNode = layer_node.toElement();
128
129 if ( mapLayerNode.isNull() )
130 {
131 QgsDebugMsgLevel( QStringLiteral( "can't find maplayer node" ), 2 );
132 return false;
133 }
134
135 mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( Qgis::LayerType::Group ) );
136
137 QDomElement childLayersElement = doc.createElement( QStringLiteral( "childLayers" ) );
138 for ( auto it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
139 {
140 QDomElement childElement = doc.createElement( QStringLiteral( "child" ) );
141 childElement.setAttribute( QStringLiteral( "layerid" ), it->layerId );
142 childLayersElement.appendChild( childElement );
143 }
144 mapLayerNode.appendChild( childLayersElement );
145
146 // renderer specific settings
147 QString errorMsg;
148 return writeSymbology( layer_node, doc, errorMsg, context );
149}
150
151bool QgsGroupLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &, const QgsReadWriteContext &, QgsMapLayer::StyleCategories categories ) const
152{
154
155 // add the layer opacity
156 if ( categories.testFlag( Rendering ) )
157 {
158 QDomElement layerOpacityElem = doc.createElement( QStringLiteral( "layerOpacity" ) );
159 const QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
160 layerOpacityElem.appendChild( layerOpacityText );
161 node.appendChild( layerOpacityElem );
162
163 if ( mPaintEffect && !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect.get() ) )
164 {
165 QDomElement paintEffectElement = doc.createElement( QStringLiteral( "paintEffect" ) );
166 mPaintEffect->saveProperties( doc, paintEffectElement );
167 node.appendChild( paintEffectElement );
168 }
169 }
170
171 if ( categories.testFlag( Symbology ) )
172 {
173 // add the blend mode field
174 QDomElement blendModeElem = doc.createElement( QStringLiteral( "blendMode" ) );
175 const QDomText blendModeText = doc.createTextNode( QString::number( static_cast< int >( QgsPainting::getBlendModeEnum( blendMode() ) ) ) );
176 blendModeElem.appendChild( blendModeText );
177 node.appendChild( blendModeElem );
178 }
179
180 return true;
181}
182
183bool QgsGroupLayer::readSymbology( const QDomNode &node, QString &, QgsReadWriteContext &, QgsMapLayer::StyleCategories categories )
184{
186
187 if ( categories.testFlag( Rendering ) )
188 {
189 const QDomNode layerOpacityNode = node.namedItem( QStringLiteral( "layerOpacity" ) );
190 if ( !layerOpacityNode.isNull() )
191 {
192 const QDomElement e = layerOpacityNode.toElement();
193 setOpacity( e.text().toDouble() );
194 }
195
196 //restore layer effect
197 const QDomElement effectElem = node.namedItem( QStringLiteral( "paintEffect" ) ).toElement();
198 if ( !effectElem.isNull() )
199 {
200 const QDomElement effectPropertiesElem = effectElem.firstChildElement( QStringLiteral( "effect" ) ).toElement();
201 mPaintEffect.reset( QgsApplication::paintEffectRegistry()->createEffect( effectPropertiesElem ) );
202 }
203 else
204 {
205 mPaintEffect.reset( QgsPaintEffectRegistry::defaultStack() );
206 mPaintEffect->setEnabled( false );
207 }
208 }
209
210 if ( categories.testFlag( Symbology ) )
211 {
212 // get and set the blend mode if it exists
213 const QDomNode blendModeNode = node.namedItem( QStringLiteral( "blendMode" ) );
214 if ( !blendModeNode.isNull() )
215 {
216 const QDomElement e = blendModeNode.toElement();
217 setBlendMode( QgsPainting::getCompositionMode( static_cast< Qgis::BlendMode >( e.text().toInt() ) ) );
218 }
219 }
220
221 return true;
222}
223
225{
227
228 return mDataProvider;
229}
230
232{
234
235 return mDataProvider;
236}
237
239{
241
242 QString metadata = QStringLiteral( "<html>\n<body>\n<h1>" ) + tr( "General" ) + QStringLiteral( "</h1>\n<hr>\n" ) + QStringLiteral( "<table class=\"list-view\">\n" );
243
244 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Name" ) + QStringLiteral( "</td><td>" ) + name() + QStringLiteral( "</td></tr>\n" );
245
246 // Extent
247 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Extent" ) + QStringLiteral( "</td><td>" ) + extent().toString() + QStringLiteral( "</td></tr>\n" );
248
249
250 metadata += QLatin1String( "\n</body>\n</html>\n" );
251 return metadata;
252}
253
255{
257
259 for ( int i = 0; i < mChildren.size(); ++i )
260 {
261 mChildren[i].resolve( project );
262
263 if ( mChildren[i].layer )
264 {
265 connect( mChildren[i].layer, &QgsMapLayer::repaintRequested, this, &QgsMapLayer::triggerRepaint, Qt::UniqueConnection );
266
267 // group layer inherits first valid child layer's crs
268 if ( !crs().isValid() )
269 {
270 setCrs( mChildren[i].layer->crs() );
271 mDataProvider->setCrs( crs() );
272 }
273 }
274 }
276}
277
278void QgsGroupLayer::setChildLayers( const QList< QgsMapLayer * > &layers )
279{
281
282 const QList< QgsMapLayer * > currentLayers = _qgis_listRefToRaw( mChildren );
283 for ( QgsMapLayer *layer : layers )
284 {
285 if ( !currentLayers.contains( layer ) )
286 {
287 connect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapLayer::triggerRepaint, Qt::UniqueConnection );
288 if ( layer->blendMode() == QPainter::CompositionMode_SourceOver && layer->customProperty( QStringLiteral( "_prevGroupBlendMode" ) ).isValid() )
289 {
290 // try to restore previous group blend mode
291 layer->setBlendMode( static_cast< QPainter::CompositionMode >( layer->customProperty( QStringLiteral( "_prevGroupBlendMode" ) ).toInt() ) );
292 }
293 }
294 }
295 for ( QgsMapLayer *layer : currentLayers )
296 {
297 if ( layer && !layers.contains( layer ) )
298 {
299 // layer removed from group
301
302 const QPainter::CompositionMode groupBlendMode = layer->blendMode();
304 {
305 layer->setBlendMode( QPainter::CompositionMode_SourceOver );
306 layer->setCustomProperty( QStringLiteral( "_prevGroupBlendMode" ), static_cast< int >( groupBlendMode ) );
307 }
308 else
309 {
310 layer->removeCustomProperty( QStringLiteral( "_prevGroupBlendMode" ) );
311 }
312 }
313 }
314 mChildren = _qgis_listRawToRef( layers );
315
316 // group layer inherits first valid child layer's crs
317 for ( const QgsMapLayer *layer : layers )
318 {
319 if ( layer->isValid() && layer->crs().isValid( ) )
320 {
321 setCrs( layer->crs() );
322 mDataProvider->setCrs( crs() );
323 break;
324 }
325 }
326
328}
329
330QList< QgsMapLayer * > QgsGroupLayer::childLayers() const
331{
333
334 return _qgis_listRefToRaw( mChildren );
335}
336
338{
340
341 return mPaintEffect.get();
342}
343
345{
347
348 mPaintEffect.reset( effect );
349}
350
352{
353 for ( const QgsMapLayerRef &child : std::as_const( mChildren ) )
354 {
355 if ( child.get() && QgsPainting::isClippingMode( QgsPainting::getBlendModeEnum( child->blendMode() ) ) )
356 {
357 child->setBlendMode( QPainter::CompositionMode_SourceOver );
358 }
359 }
360}
361
362//
363// QgsGroupLayerDataProvider
364//
366QgsGroupLayerDataProvider::QgsGroupLayerDataProvider(
367 const ProviderOptions &options,
369 : QgsDataProvider( QString(), options, flags )
370{}
371
372void QgsGroupLayerDataProvider::setCrs( const QgsCoordinateReferenceSystem &crs )
373{
375
376 mCrs = crs;
377}
378
380{
382
383 return mCrs;
384}
385
386QString QgsGroupLayerDataProvider::name() const
387{
389
390 return QStringLiteral( "annotation" );
391}
392
393QString QgsGroupLayerDataProvider::description() const
394{
396
397 return QString();
398}
399
400QgsRectangle QgsGroupLayerDataProvider::extent() const
401{
403
404 return QgsRectangle();
405}
406
407bool QgsGroupLayerDataProvider::isValid() const
408{
410
411 return true;
412}
414
The Qgis class provides global constants for use throughout the application.
Definition: qgis.h:54
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition: qgis.h:4041
@ Group
Composite group layer. Added in QGIS 3.24.
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.
QFlags< ReadFlag > ReadFlags
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.
void prepareLayersForRemovalFromGroup()
Prepares all child layers in the group prior to removal from the group.
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(Qgis::LayerType 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:75
QString name
Definition: qgsmaplayer.h:78
void setBlendMode(QPainter::CompositionMode blendMode)
Set the blending mode used for rendering a layer.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:81
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:80
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....
QFlags< StyleCategory > StyleCategories
Definition: qgsmaplayer.h:188
void willBeDeleted()
Emitted in the destructor when the layer is about to be deleted, but it is still in a perfectly valid...
virtual QgsMapLayer * clone() const =0
Returns a new instance equivalent to this one except for the id which is still unique.
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:83
@ 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:2167
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:84
bool mValid
Indicates if the layer is valid and can be drawn.
Definition: qgsmaplayer.h:2118
@ Symbology
Symbology.
Definition: qgsmaplayer.h:167
@ Rendering
Rendering: scale visibility, simplify method, opacity.
Definition: qgsmaplayer.h:176
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:2174
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 Qgis::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a Qgis::BlendMode corresponding to a QPainter::CompositionMode.
Definition: qgspainting.cpp:81
static bool isClippingMode(Qgis::BlendMode mode)
Returns true if mode is a clipping blend mode.
static QPainter::CompositionMode getCompositionMode(Qgis::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a Qgis::BlendMode.
Definition: qgspainting.cpp:21
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:107
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.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
_LayerRef< QgsMapLayer > QgsMapLayerRef
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
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