QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
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
20#include "qgsapplication.h"
22#include "qgsmaplayerfactory.h"
24#include "qgsmaplayerref.h"
25#include "qgsmaplayerutils.h"
26#include "qgspainteffect.h"
28#include "qgspainting.h"
29#include "qgsthreadingutils.h"
30
31#include <QString>
32
33#include "moc_qgsgrouplayer.cpp"
34
35using namespace Qt::StringLiterals;
36
37QgsGroupLayer::QgsGroupLayer( const QString &name, const LayerOptions &options )
38 : QgsMapLayer( Qgis::LayerType::Group, name )
39 , mTransformContext( options.transformContext )
40{
41 mShouldValidateCrs = false;
42 mValid = true;
43
44 mPaintEffect.reset( QgsPaintEffectRegistry::defaultStack() );
45 mPaintEffect->setEnabled( false );
46
48 providerOptions.transformContext = options.transformContext;
49 mDataProvider = std::make_unique<QgsGroupLayerDataProvider>( providerOptions, Qgis::DataProviderReadFlags() );
50}
51
57
59{
61
62 const QgsGroupLayer::LayerOptions options( mTransformContext );
63 auto layer = std::make_unique< QgsGroupLayer >( name(), options );
64 QgsMapLayer::clone( layer.get() );
65 layer->setChildLayers( _qgis_listRefToRaw( mChildren ) );
66 layer->setPaintEffect( mPaintEffect ? mPaintEffect->clone() : nullptr );
67 return layer.release();
68}
69
76
83
85{
87
88 if ( mDataProvider )
89 mDataProvider->setTransformContext( context );
90
91 mTransformContext = context;
93}
94
95bool QgsGroupLayer::readXml( const QDomNode &layerNode, QgsReadWriteContext &context )
96{
98
100 {
101 return false;
102 }
103
104 const QList< QgsMapLayer * > currentLayers = _qgis_listRefToRaw( mChildren );
105 for ( QgsMapLayer *layer : currentLayers )
106 {
108 }
109
110 mChildren.clear();
111 const QDomNodeList childLayersElements = layerNode.toElement().elementsByTagName( u"childLayers"_s );
112 const QDomNodeList children = childLayersElements.at( 0 ).childNodes();
113 for ( int i = 0; i < children.size(); ++i )
114 {
115 const QDomElement childElement = children.at( i ).toElement();
116 const QString id = childElement.attribute( u"layerid"_s );
117 mChildren.append( QgsMapLayerRef( id ) );
118 }
120
121 QString errorMsg;
122 readSymbology( layerNode, errorMsg, context );
123
125
126 return mValid;
127}
128
129bool QgsGroupLayer::writeXml( QDomNode &layer_node, QDomDocument &doc, const QgsReadWriteContext &context ) const
130{
132
133 // first get the layer element so that we can append the type attribute
134 QDomElement mapLayerNode = layer_node.toElement();
135
136 if ( mapLayerNode.isNull() )
137 {
138 QgsDebugMsgLevel( u"can't find maplayer node"_s, 2 );
139 return false;
140 }
141
142 mapLayerNode.setAttribute( u"type"_s, QgsMapLayerFactory::typeToString( Qgis::LayerType::Group ) );
143
144 QDomElement childLayersElement = doc.createElement( u"childLayers"_s );
145 for ( auto it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
146 {
147 QDomElement childElement = doc.createElement( u"child"_s );
148 childElement.setAttribute( u"layerid"_s, it->layerId );
149 childLayersElement.appendChild( childElement );
150 }
151 mapLayerNode.appendChild( childLayersElement );
152
153 // renderer specific settings
154 QString errorMsg;
155 return writeSymbology( layer_node, doc, errorMsg, context );
156}
157
158bool QgsGroupLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &, const QgsReadWriteContext &, QgsMapLayer::StyleCategories categories ) const
159{
161
162 // add the layer opacity
163 if ( categories.testFlag( Rendering ) )
164 {
165 QDomElement layerOpacityElem = doc.createElement( u"layerOpacity"_s );
166 const QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) );
167 layerOpacityElem.appendChild( layerOpacityText );
168 node.appendChild( layerOpacityElem );
169
170 if ( mPaintEffect && !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect.get() ) )
171 {
172 QDomElement paintEffectElement = doc.createElement( u"paintEffect"_s );
173 mPaintEffect->saveProperties( doc, paintEffectElement );
174 node.appendChild( paintEffectElement );
175 }
176 }
177
178 if ( categories.testFlag( Symbology ) )
179 {
180 // add the blend mode field
181 QDomElement blendModeElem = doc.createElement( u"blendMode"_s );
182 const QDomText blendModeText = doc.createTextNode( QString::number( static_cast< int >( QgsPainting::getBlendModeEnum( blendMode() ) ) ) );
183 blendModeElem.appendChild( blendModeText );
184 node.appendChild( blendModeElem );
185 }
186
187 return true;
188}
189
190bool QgsGroupLayer::readSymbology( const QDomNode &node, QString &, QgsReadWriteContext &, QgsMapLayer::StyleCategories categories )
191{
193
194 if ( categories.testFlag( Rendering ) )
195 {
196 const QDomNode layerOpacityNode = node.namedItem( u"layerOpacity"_s );
197 if ( !layerOpacityNode.isNull() )
198 {
199 const QDomElement e = layerOpacityNode.toElement();
200 setOpacity( e.text().toDouble() );
201 }
202
203 //restore layer effect
204 const QDomElement effectElem = node.namedItem( u"paintEffect"_s ).toElement();
205 if ( !effectElem.isNull() )
206 {
207 const QDomElement effectPropertiesElem = effectElem.firstChildElement( u"effect"_s ).toElement();
208 mPaintEffect.reset( QgsApplication::paintEffectRegistry()->createEffect( effectPropertiesElem ) );
209 }
210 else
211 {
212 mPaintEffect.reset( QgsPaintEffectRegistry::defaultStack() );
213 mPaintEffect->setEnabled( false );
214 }
215 }
216
217 if ( categories.testFlag( Symbology ) )
218 {
219 // get and set the blend mode if it exists
220 const QDomNode blendModeNode = node.namedItem( u"blendMode"_s );
221 if ( !blendModeNode.isNull() )
222 {
223 const QDomElement e = blendModeNode.toElement();
224 setBlendMode( QgsPainting::getCompositionMode( static_cast< Qgis::BlendMode >( e.text().toInt() ) ) );
225 }
226 }
227
228 return true;
229}
230
232{
234
235 return mDataProvider.get();
236}
237
239{
241
242 return mDataProvider.get();
243}
244
246{
248
249 QString metadata = u"<html>\n<body>\n<h1>"_s + tr( "General" ) + u"</h1>\n<hr>\n"_s + u"<table class=\"list-view\">\n"_s;
250
251 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Name" ) + u"</td><td>"_s + name() + u"</td></tr>\n"_s;
252
253 // Extent
254 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Extent" ) + u"</td><td>"_s + extent().toString() + u"</td></tr>\n"_s;
255
256
257 metadata += "\n</body>\n</html>\n"_L1;
258 return metadata;
259}
260
262{
264
266 for ( int i = 0; i < mChildren.size(); ++i )
267 {
268 mChildren[i].resolve( project );
269
270 if ( mChildren[i].layer )
271 {
272 connect( mChildren[i].layer, &QgsMapLayer::repaintRequested, this, &QgsMapLayer::triggerRepaint, Qt::UniqueConnection );
273
274 // group layer inherits first valid child layer's crs
275 if ( !crs().isValid() )
276 {
277 setCrs( mChildren[i].layer->crs() );
278 mDataProvider->setCrs( crs() );
279 }
280 }
281 }
283}
284
285void QgsGroupLayer::setChildLayers( const QList< QgsMapLayer * > &layers )
286{
288
289 const QList< QgsMapLayer * > currentLayers = _qgis_listRefToRaw( mChildren );
290 for ( QgsMapLayer *layer : layers )
291 {
292 if ( !currentLayers.contains( layer ) )
293 {
294 connect( layer, &QgsMapLayer::repaintRequested, this, &QgsMapLayer::triggerRepaint, Qt::UniqueConnection );
295 if ( layer->blendMode() == QPainter::CompositionMode_SourceOver && layer->customProperty( u"_prevGroupBlendMode"_s ).isValid() )
296 {
297 // try to restore previous group blend mode
298 layer->setBlendMode( static_cast< QPainter::CompositionMode >( layer->customProperty( u"_prevGroupBlendMode"_s ).toInt() ) );
299 }
300 }
301 }
302 for ( QgsMapLayer *layer : currentLayers )
303 {
304 if ( layer && !layers.contains( layer ) )
305 {
306 // layer removed from group
308
309 const QPainter::CompositionMode groupBlendMode = layer->blendMode();
311 {
312 layer->setBlendMode( QPainter::CompositionMode_SourceOver );
313 layer->setCustomProperty( u"_prevGroupBlendMode"_s, static_cast< int >( groupBlendMode ) );
314 }
315 else
316 {
317 layer->removeCustomProperty( u"_prevGroupBlendMode"_s );
318 }
319 }
320 }
321 mChildren = _qgis_listRawToRef( layers );
322
323 // group layer inherits first valid child layer's crs
324 for ( const QgsMapLayer *layer : layers )
325 {
326 if ( layer->isValid() && layer->crs().isValid( ) )
327 {
328 setCrs( layer->crs() );
329 mDataProvider->setCrs( crs() );
330 break;
331 }
332 }
333
335}
336
337QList< QgsMapLayer * > QgsGroupLayer::childLayers() const
338{
340
341 return _qgis_listRefToRaw( mChildren );
342}
343
345{
347
348 return mPaintEffect.get();
349}
350
352{
354
355 mPaintEffect.reset( effect );
356}
357
359{
360 for ( const QgsMapLayerRef &child : std::as_const( mChildren ) )
361 {
362 if ( child.get() && QgsPainting::isClippingMode( QgsPainting::getBlendModeEnum( child->blendMode() ) ) )
363 {
364 child->setBlendMode( QPainter::CompositionMode_SourceOver );
365 }
366 }
367}
368
369//
370// QgsGroupLayerDataProvider
371//
373QgsGroupLayerDataProvider::QgsGroupLayerDataProvider(
374 const ProviderOptions &options,
376 : QgsDataProvider( QString(), options, flags )
377{}
378
379void QgsGroupLayerDataProvider::setCrs( const QgsCoordinateReferenceSystem &crs )
380{
382
383 mCrs = crs;
384}
385
386QgsCoordinateReferenceSystem QgsGroupLayerDataProvider::crs() const
387{
389
390 return mCrs;
391}
392
393QString QgsGroupLayerDataProvider::name() const
394{
396
397 return u"annotation"_s;
398}
399
400QString QgsGroupLayerDataProvider::description() const
401{
403
404 return QString();
405}
406
407QgsRectangle QgsGroupLayerDataProvider::extent() const
408{
410
411 return QgsRectangle();
412}
413
414bool QgsGroupLayerDataProvider::isValid() const
415{
417
418 return true;
419}
421
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:59
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:5002
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:505
@ Group
Composite group layer. Added in QGIS 3.24.
Definition qgis.h:201
static QgsPaintEffectRegistry * paintEffectRegistry()
Returns the application's paint effect registry, used for managing paint effects.
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.
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.
QString name
Definition qgsmaplayer.h:87
void setBlendMode(QPainter::CompositionMode blendMode)
Set the blending mode used for rendering a layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
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:89
QgsMapLayer(Qgis::LayerType type=Qgis::LayerType::Vector, const QString &name=QString(), const QString &source=QString())
Constructor for QgsMapLayer.
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
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
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.
@ FlagDontResolveLayers
Don't resolve layer paths or create data providers for layers.
QgsMapLayer::ReadFlags mReadFlags
Read flags. It's up to the subclass to respect these when restoring state from XML.
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:95
bool mValid
Indicates if the layer is valid and can be drawn.
@ Symbology
Symbology.
@ Rendering
Rendering: scale visibility, simplify method, opacity.
void invalidateWgs84Extent()
Invalidates the WGS84 extent.
bool mShouldValidateCrs
true if the layer's CRS should be validated and invalid CRSes are not permitted.
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.
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.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:112
A container for the context for various read/write operations on objects.
A rectangle specified with double values.
Q_INVOKABLE 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:63
_LayerRef< QgsMapLayer > QgsMapLayerRef
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
Setting options for creating vector data providers.
QgsCoordinateTransformContext transformContext
Coordinate transform context.
Setting options for loading group layers.
QgsCoordinateTransformContext transformContext
Coordinate transform context.