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