QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsreportsectionfieldgroup.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsreportsectionfieldgroup.cpp
3 --------------------
4 begin : December 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8/***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
18
19#include "qgslayout.h"
20
22
23QgsReportSectionFieldGroup::QgsReportSectionFieldGroup( QgsAbstractReportSection *parent )
24 : QgsAbstractReportSection( parent )
25{
26
27}
28
29QString QgsReportSectionFieldGroup::description() const
30{
31 if ( mCoverageLayer )
32 return QObject::tr( "Group: %1 - %2" ).arg( mCoverageLayer->name(), mField );
33 else
34 return QObject::tr( "Group" );
35}
36
37QIcon QgsReportSectionFieldGroup::icon() const
38{
39 return QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldText.svg" ) );
40}
41
42QgsReportSectionFieldGroup *QgsReportSectionFieldGroup::clone() const
43{
44 auto copy = std::make_unique< QgsReportSectionFieldGroup >( nullptr );
45 copyCommonProperties( copy.get() );
46
47 if ( mBody )
48 {
49 copy->mBody.reset( mBody->clone() );
50 }
51 else
52 copy->mBody.reset();
53
54 copy->setLayer( mCoverageLayer.get() );
55 copy->setField( mField );
56 copy->setSortAscending( mSortAscending );
57 copy->setBodyEnabled( mBodyEnabled );
58
59 return copy.release();
60}
61
62bool QgsReportSectionFieldGroup::beginRender()
63{
64 if ( !mCoverageLayer )
65 return false;
66
67 if ( !mField.isEmpty() )
68 {
69 mFieldIndex = mCoverageLayer->fields().lookupField( mField );
70 if ( mFieldIndex < 0 )
71 return false;
72
73 if ( mBody )
74 mBody->reportContext().setLayer( mCoverageLayer.get() );
75
76 mFeatures = QgsFeatureIterator();
77 }
78 return QgsAbstractReportSection::beginRender();
79}
80
81bool QgsReportSectionFieldGroup::prepareHeader()
82{
83 if ( !header() )
84 return false;
85
86 if ( !mFeatures.isValid() )
87 {
88 mFeatures = mCoverageLayer->getFeatures( buildFeatureRequest() );
89 }
90
91 mHeaderFeature = getNextFeature();
92 header()->reportContext().blockSignals( true );
93 header()->reportContext().setLayer( mCoverageLayer.get() );
94 header()->reportContext().blockSignals( false );
95 header()->reportContext().setFeature( mHeaderFeature );
96 mSkipNextRequest = true;
97 mNoFeatures = !mHeaderFeature.isValid();
98 return mHeaderVisibility == AlwaysInclude || !mNoFeatures;
99}
100
101bool QgsReportSectionFieldGroup::prepareFooter()
102{
103 return mFooterVisibility == AlwaysInclude || !mNoFeatures;
104}
105
106QgsLayout *QgsReportSectionFieldGroup::nextBody( bool &ok )
107{
108 if ( !mFeatures.isValid() )
109 {
110 mFeatures = mCoverageLayer->getFeatures( buildFeatureRequest() );
111 }
112
113 QgsFeature f;
114 if ( !mSkipNextRequest )
115 {
116 f = getNextFeature();
117 }
118 else
119 {
120 f = mHeaderFeature;
121 mSkipNextRequest = false;
122 }
123
124 if ( !f.isValid() )
125 {
126 // no features left for this iteration
127 mFeatures = QgsFeatureIterator();
128
129 if ( auto *lFooter = footer() )
130 {
131 lFooter->reportContext().blockSignals( true );
132 lFooter->reportContext().setLayer( mCoverageLayer.get() );
133 lFooter->reportContext().blockSignals( false );
134 lFooter->reportContext().setFeature( mLastFeature );
135 }
136 ok = false;
137 return nullptr;
138 }
139
140 mLastFeature = f;
141
142 updateChildContexts( f );
143
144 ok = true;
145 if ( mBody && mBodyEnabled )
146 {
147 mBody->reportContext().blockSignals( true );
148 mBody->reportContext().setLayer( mCoverageLayer.get() );
149 mBody->reportContext().blockSignals( false );
150 mBody->reportContext().setFeature( f );
151 }
152
153 return mBodyEnabled ? mBody.get() : nullptr;
154}
155
156void QgsReportSectionFieldGroup::reset()
157{
158 QgsAbstractReportSection::reset();
159 mEncounteredValues.clear();
160 mSkipNextRequest = false;
161 mHeaderFeature = QgsFeature();
162 mLastFeature = QgsFeature();
163 mFeatures = QgsFeatureIterator();
164 mNoFeatures = false;
165}
166
167void QgsReportSectionFieldGroup::setParentSection( QgsAbstractReportSection *parent )
168{
169 QgsAbstractReportSection::setParentSection( parent );
170 if ( !mCoverageLayer )
171 mCoverageLayer.resolveWeakly( project() );
172}
173
174void QgsReportSectionFieldGroup::reloadSettings()
175{
176 QgsAbstractReportSection::reloadSettings();
177 if ( mBody )
178 mBody->reloadSettings();
179}
180
181bool QgsReportSectionFieldGroup::writePropertiesToElement( QDomElement &element, QDomDocument &doc, const QgsReadWriteContext &context ) const
182{
183 element.setAttribute( QStringLiteral( "headerVisibility" ), static_cast< int >( mHeaderVisibility ) );
184 element.setAttribute( QStringLiteral( "footerVisibility" ), static_cast< int >( mFooterVisibility ) );
185 element.setAttribute( QStringLiteral( "field" ), mField );
186 element.setAttribute( QStringLiteral( "ascending" ), mSortAscending ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
187 element.setAttribute( QStringLiteral( "bodyEnabled" ), mBodyEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
188 if ( mCoverageLayer )
189 {
190 element.setAttribute( QStringLiteral( "coverageLayer" ), mCoverageLayer.layerId );
191 element.setAttribute( QStringLiteral( "coverageLayerName" ), mCoverageLayer.name );
192 element.setAttribute( QStringLiteral( "coverageLayerSource" ), mCoverageLayer.source );
193 element.setAttribute( QStringLiteral( "coverageLayerProvider" ), mCoverageLayer.provider );
194 }
195
196 if ( mBody )
197 {
198 QDomElement bodyElement = doc.createElement( QStringLiteral( "body" ) );
199 bodyElement.appendChild( mBody->writeXml( doc, context ) );
200 element.appendChild( bodyElement );
201 }
202 return true;
203}
204
205bool QgsReportSectionFieldGroup::readPropertiesFromElement( const QDomElement &element, const QDomDocument &doc, const QgsReadWriteContext &context )
206{
207 mHeaderVisibility = static_cast< SectionVisibility >( element.attribute( QStringLiteral( "headerVisibility" ) ).toInt() );
208 mFooterVisibility = static_cast< SectionVisibility >( element.attribute( QStringLiteral( "footerVisibility" ) ).toInt() );
209 mField = element.attribute( QStringLiteral( "field" ) );
210 mSortAscending = element.attribute( QStringLiteral( "ascending" ) ).toInt();
211 mBodyEnabled = element.attribute( QStringLiteral( "bodyEnabled" ) ).toInt();
212 QString layerId = element.attribute( QStringLiteral( "coverageLayer" ) );
213 QString layerName = element.attribute( QStringLiteral( "coverageLayerName" ) );
214 QString layerSource = element.attribute( QStringLiteral( "coverageLayerSource" ) );
215 QString layerProvider = element.attribute( QStringLiteral( "coverageLayerProvider" ) );
216 mCoverageLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
217 mCoverageLayer.resolveWeakly( project() );
218
219 const QDomElement bodyElement = element.firstChildElement( QStringLiteral( "body" ) );
220 if ( !bodyElement.isNull() )
221 {
222 const QDomElement bodyLayoutElem = bodyElement.firstChild().toElement();
223 auto body = std::make_unique< QgsLayout >( project() );
224 body->readXml( bodyLayoutElem, doc, context );
225 mBody = std::move( body );
226 }
227 return true;
228}
229
230bool QgsReportSectionFieldGroup::sortAscending() const
231{
232 return mSortAscending;
233}
234
235void QgsReportSectionFieldGroup::setSortAscending( bool sortAscending )
236{
237 mSortAscending = sortAscending;
238}
239
240QgsFeatureRequest QgsReportSectionFieldGroup::buildFeatureRequest() const
241{
242 QgsFeatureRequest request;
243 QVariantMap filter = context().fieldFilters;
244
245 QStringList filterParts;
246 for ( auto filterIt = filter.constBegin(); filterIt != filter.constEnd(); ++filterIt )
247 {
248 // use lookupField since we don't want case sensitivity
249 int fieldIndex = mCoverageLayer->fields().lookupField( filterIt.key() );
250 if ( fieldIndex >= 0 )
251 {
252 // layer has a matching field, so we need to filter by it
253 filterParts << QgsExpression::createFieldEqualityExpression( mCoverageLayer->fields().at( fieldIndex ).name(), filterIt.value() );
254 }
255 }
256 if ( !filterParts.empty() )
257 {
258 QString filterString = QStringLiteral( "(%1)" ).arg( filterParts.join( QLatin1String( ") AND (" ) ) );
259 request.setFilterExpression( filterString );
260 }
261
262 request.addOrderBy( mField, mSortAscending );
263 return request;
264}
265
266QgsFeature QgsReportSectionFieldGroup::getNextFeature()
267{
268 QgsFeature f;
269 QVariant currentValue;
270 bool first = true;
271 while ( first || ( ( !mBody || !mBodyEnabled ) && mEncounteredValues.contains( currentValue ) ) )
272 {
273 if ( !mFeatures.nextFeature( f ) )
274 {
275 return QgsFeature();
276 }
277
278 first = false;
279 currentValue = f.attribute( mFieldIndex );
280 }
281
282 mEncounteredValues.insert( currentValue );
283 return f;
284}
285
286void QgsReportSectionFieldGroup::updateChildContexts( const QgsFeature &feature )
287{
288 QgsReportSectionContext c = context();
289 c.feature = feature;
290 if ( mCoverageLayer )
291 c.currentLayer = mCoverageLayer.get();
292
293 QVariantMap currentFilter = c.fieldFilters;
294 currentFilter.insert( mField, feature.attribute( mFieldIndex ) );
295 c.fieldFilters = currentFilter;
296
297 const QList< QgsAbstractReportSection * > sections = childSections();
298 for ( QgsAbstractReportSection *section : std::as_const( sections ) )
299 {
300 section->setContext( c );
301 }
302}
303
305
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
Create an expression allowing to evaluate if a field is equal to a value.
Wrapper for iterator of features from vector data provider or vector layer.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & addOrderBy(const QString &expression, bool ascending=true)
Adds a new OrderByClause, appending it as the least important one.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition qgslayout.h:50
A container for the context for various read/write operations on objects.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
_LayerRef< QgsVectorLayer > QgsVectorLayerRef