QGIS API Documentation 3.99.0-Master (09f76ad7019)
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
21#include <QString>
22
23using namespace Qt::StringLiterals;
24
26
27QgsReportSectionFieldGroup::QgsReportSectionFieldGroup( QgsAbstractReportSection *parent )
28 : QgsAbstractReportSection( parent )
29{
30
31}
32
33QString QgsReportSectionFieldGroup::description() const
34{
35 if ( mCoverageLayer )
36 return QObject::tr( "Group: %1 - %2" ).arg( mCoverageLayer->name(), mField );
37 else
38 return QObject::tr( "Group" );
39}
40
41QIcon QgsReportSectionFieldGroup::icon() const
42{
43 return QgsApplication::getThemeIcon( u"/mIconFieldText.svg"_s );
44}
45
46QgsReportSectionFieldGroup *QgsReportSectionFieldGroup::clone() const
47{
48 auto copy = std::make_unique< QgsReportSectionFieldGroup >( nullptr );
49 copyCommonProperties( copy.get() );
50
51 if ( mBody )
52 {
53 copy->mBody.reset( mBody->clone() );
54 }
55 else
56 copy->mBody.reset();
57
58 copy->setLayer( mCoverageLayer.get() );
59 copy->setField( mField );
60 copy->setSortAscending( mSortAscending );
61 copy->setBodyEnabled( mBodyEnabled );
62
63 return copy.release();
64}
65
66bool QgsReportSectionFieldGroup::beginRender()
67{
68 if ( !mCoverageLayer )
69 return false;
70
71 if ( !mField.isEmpty() )
72 {
73 mFieldIndex = mCoverageLayer->fields().lookupField( mField );
74 if ( mFieldIndex < 0 )
75 return false;
76
77 if ( mBody )
78 mBody->reportContext().setLayer( mCoverageLayer.get() );
79
80 mFeatures = QgsFeatureIterator();
81 }
82 return QgsAbstractReportSection::beginRender();
83}
84
85bool QgsReportSectionFieldGroup::prepareHeader()
86{
87 if ( !header() )
88 return false;
89
90 if ( !mFeatures.isValid() )
91 {
92 mFeatures = mCoverageLayer->getFeatures( buildFeatureRequest() );
93 }
94
95 mHeaderFeature = getNextFeature();
96 header()->reportContext().blockSignals( true );
97 header()->reportContext().setLayer( mCoverageLayer.get() );
98 header()->reportContext().blockSignals( false );
99 header()->reportContext().setFeature( mHeaderFeature );
100 mSkipNextRequest = true;
101 mNoFeatures = !mHeaderFeature.isValid();
102 return mHeaderVisibility == AlwaysInclude || !mNoFeatures;
103}
104
105bool QgsReportSectionFieldGroup::prepareFooter()
106{
107 return mFooterVisibility == AlwaysInclude || !mNoFeatures;
108}
109
110QgsLayout *QgsReportSectionFieldGroup::nextBody( bool &ok )
111{
112 if ( !mFeatures.isValid() )
113 {
114 mFeatures = mCoverageLayer->getFeatures( buildFeatureRequest() );
115 }
116
117 QgsFeature f;
118 if ( !mSkipNextRequest )
119 {
120 f = getNextFeature();
121 }
122 else
123 {
124 f = mHeaderFeature;
125 mSkipNextRequest = false;
126 }
127
128 if ( !f.isValid() )
129 {
130 // no features left for this iteration
131 mFeatures = QgsFeatureIterator();
132
133 if ( auto *lFooter = footer() )
134 {
135 lFooter->reportContext().blockSignals( true );
136 lFooter->reportContext().setLayer( mCoverageLayer.get() );
137 lFooter->reportContext().blockSignals( false );
138 lFooter->reportContext().setFeature( mLastFeature );
139 }
140 ok = false;
141 return nullptr;
142 }
143
144 mLastFeature = f;
145
146 updateChildContexts( f );
147
148 ok = true;
149 if ( mBody && mBodyEnabled )
150 {
151 mBody->reportContext().blockSignals( true );
152 mBody->reportContext().setLayer( mCoverageLayer.get() );
153 mBody->reportContext().blockSignals( false );
154 mBody->reportContext().setFeature( f );
155 }
156
157 return mBodyEnabled ? mBody.get() : nullptr;
158}
159
160void QgsReportSectionFieldGroup::reset()
161{
162 QgsAbstractReportSection::reset();
163 mEncounteredValues.clear();
164 mSkipNextRequest = false;
165 mHeaderFeature = QgsFeature();
166 mLastFeature = QgsFeature();
167 mFeatures = QgsFeatureIterator();
168 mNoFeatures = false;
169}
170
171void QgsReportSectionFieldGroup::setParentSection( QgsAbstractReportSection *parent )
172{
173 QgsAbstractReportSection::setParentSection( parent );
174 if ( !mCoverageLayer )
175 mCoverageLayer.resolveWeakly( project() );
176}
177
178void QgsReportSectionFieldGroup::reloadSettings()
179{
180 QgsAbstractReportSection::reloadSettings();
181 if ( mBody )
182 mBody->reloadSettings();
183}
184
185bool QgsReportSectionFieldGroup::writePropertiesToElement( QDomElement &element, QDomDocument &doc, const QgsReadWriteContext &context ) const
186{
187 element.setAttribute( u"headerVisibility"_s, static_cast< int >( mHeaderVisibility ) );
188 element.setAttribute( u"footerVisibility"_s, static_cast< int >( mFooterVisibility ) );
189 element.setAttribute( u"field"_s, mField );
190 element.setAttribute( u"ascending"_s, mSortAscending ? u"1"_s : u"0"_s );
191 element.setAttribute( u"bodyEnabled"_s, mBodyEnabled ? u"1"_s : u"0"_s );
192 if ( mCoverageLayer )
193 {
194 element.setAttribute( u"coverageLayer"_s, mCoverageLayer.layerId );
195 element.setAttribute( u"coverageLayerName"_s, mCoverageLayer.name );
196 element.setAttribute( u"coverageLayerSource"_s, mCoverageLayer.source );
197 element.setAttribute( u"coverageLayerProvider"_s, mCoverageLayer.provider );
198 }
199
200 if ( mBody )
201 {
202 QDomElement bodyElement = doc.createElement( u"body"_s );
203 bodyElement.appendChild( mBody->writeXml( doc, context ) );
204 element.appendChild( bodyElement );
205 }
206 return true;
207}
208
209bool QgsReportSectionFieldGroup::readPropertiesFromElement( const QDomElement &element, const QDomDocument &doc, const QgsReadWriteContext &context )
210{
211 mHeaderVisibility = static_cast< SectionVisibility >( element.attribute( u"headerVisibility"_s ).toInt() );
212 mFooterVisibility = static_cast< SectionVisibility >( element.attribute( u"footerVisibility"_s ).toInt() );
213 mField = element.attribute( u"field"_s );
214 mSortAscending = element.attribute( u"ascending"_s ).toInt();
215 mBodyEnabled = element.attribute( u"bodyEnabled"_s ).toInt();
216 QString layerId = element.attribute( u"coverageLayer"_s );
217 QString layerName = element.attribute( u"coverageLayerName"_s );
218 QString layerSource = element.attribute( u"coverageLayerSource"_s );
219 QString layerProvider = element.attribute( u"coverageLayerProvider"_s );
220 mCoverageLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
221 mCoverageLayer.resolveWeakly( project() );
222
223 const QDomElement bodyElement = element.firstChildElement( u"body"_s );
224 if ( !bodyElement.isNull() )
225 {
226 const QDomElement bodyLayoutElem = bodyElement.firstChild().toElement();
227 auto body = std::make_unique< QgsLayout >( project() );
228 body->readXml( bodyLayoutElem, doc, context );
229 mBody = std::move( body );
230 }
231 return true;
232}
233
234bool QgsReportSectionFieldGroup::sortAscending() const
235{
236 return mSortAscending;
237}
238
239void QgsReportSectionFieldGroup::setSortAscending( bool sortAscending )
240{
241 mSortAscending = sortAscending;
242}
243
244QgsFeatureRequest QgsReportSectionFieldGroup::buildFeatureRequest() const
245{
246 QgsFeatureRequest request;
247 QVariantMap filter = context().fieldFilters;
248
249 QStringList filterParts;
250 for ( auto filterIt = filter.constBegin(); filterIt != filter.constEnd(); ++filterIt )
251 {
252 // use lookupField since we don't want case sensitivity
253 int fieldIndex = mCoverageLayer->fields().lookupField( filterIt.key() );
254 if ( fieldIndex >= 0 )
255 {
256 // layer has a matching field, so we need to filter by it
257 filterParts << QgsExpression::createFieldEqualityExpression( mCoverageLayer->fields().at( fieldIndex ).name(), filterIt.value() );
258 }
259 }
260 if ( !filterParts.empty() )
261 {
262 QString filterString = u"(%1)"_s.arg( filterParts.join( ") AND ("_L1 ) );
263 request.setFilterExpression( filterString );
264 }
265
266 request.addOrderBy( mField, mSortAscending );
267 return request;
268}
269
270QgsFeature QgsReportSectionFieldGroup::getNextFeature()
271{
272 QgsFeature f;
273 QVariant currentValue;
274 bool first = true;
275 while ( first || ( ( !mBody || !mBodyEnabled ) && mEncounteredValues.contains( currentValue ) ) )
276 {
277 if ( !mFeatures.nextFeature( f ) )
278 {
279 return QgsFeature();
280 }
281
282 first = false;
283 currentValue = f.attribute( mFieldIndex );
284 }
285
286 mEncounteredValues.insert( currentValue );
287 return f;
288}
289
290void QgsReportSectionFieldGroup::updateChildContexts( const QgsFeature &feature )
291{
292 QgsReportSectionContext c = context();
293 c.feature = feature;
294 if ( mCoverageLayer )
295 c.currentLayer = mCoverageLayer.get();
296
297 QVariantMap currentFilter = c.fieldFilters;
298 currentFilter.insert( mField, feature.attribute( mFieldIndex ) );
299 c.fieldFilters = currentFilter;
300
301 const QList< QgsAbstractReportSection * > sections = childSections();
302 for ( QgsAbstractReportSection *section : std::as_const( sections ) )
303 {
304 section->setContext( c );
305 }
306}
307
309
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:60
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