Skip to content

Commit 147f6e7

Browse files
committedApr 9, 2018
[FEATURE] Add feature filter algorithm
Adds a new "feature filter" algorithm. This algorithm takes a list of expressions and creates an output for each of them. Matching features from the input layer are sent to the outputs. This allows for an algorithm to only handle a subset of incoming features or to send different features to different outputs. The algorithm is only available in the modeler.
1 parent e6ef763 commit 147f6e7

9 files changed

+408
-0
lines changed
 

‎python/core/core_auto.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@
367367
%Include locator/qgslocator.sip
368368
%Include locator/qgslocatorfilter.sip
369369
%Include locator/qgslocatormodel.sip
370+
%Include processing/qgsprocessingalgorithmconfigurationwidget.sip
370371
%Include processing/qgsprocessingalgrunnertask.sip
371372
%Include processing/qgsprocessingfeedback.sip
372373
%Include processing/qgsprocessingprovider.sip
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/core/processing/qgsprocessingalgorithmconfigurationwidget.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
class QgsProcessingAlgorithmConfigurationWidget : QWidget
13+
{
14+
15+
%TypeHeaderCode
16+
#include "qgsprocessingalgorithmconfigurationwidget.h"
17+
%End
18+
public:
19+
QgsProcessingAlgorithmConfigurationWidget( QWidget *parent = 0 );
20+
virtual ~QgsProcessingAlgorithmConfigurationWidget();
21+
virtual QVariantMap configuration() const = 0;
22+
virtual void setConfiguration( const QVariantMap &configuration ) = 0;
23+
};
24+
25+
/************************************************************************
26+
* This file has been generated automatically from *
27+
* *
28+
* src/core/processing/qgsprocessingalgorithmconfigurationwidget.h *
29+
* *
30+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
31+
************************************************************************/

‎src/analysis/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ SET(QGIS_ANALYSIS_SRCS
3838
processing/qgsalgorithmextractbylocation.cpp
3939
processing/qgsalgorithmextractvertices.cpp
4040
processing/qgsalgorithmfiledownloader.cpp
41+
processing/qgsalgorithmfilter.cpp
4142
processing/qgsalgorithmfixgeometries.cpp
4243
processing/qgsalgorithmjoinbyattribute.cpp
4344
processing/qgsalgorithmjoinwithlines.cpp
@@ -139,6 +140,7 @@ SET(QGIS_ANALYSIS_MOC_HDRS
139140
network/qgsvectorlayerdirector.h
140141

141142
processing/qgsalgorithmfiledownloader.h
143+
processing/qgsalgorithmfilter.h
142144

143145
vector/geometry_checker/qgsfeaturepool.h
144146
vector/geometry_checker/qgsgeometrychecker.h
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
/***************************************************************************
2+
qgsalgorithmfilter.cpp
3+
---------------------
4+
begin : April 2018
5+
copyright : (C) 2018 by Matthias Kuhn
6+
email : matthias@opengis.ch
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 "qgsalgorithmfilter.h"
19+
#include "qgsapplication.h"
20+
21+
#include <QLabel>
22+
#include <QGridLayout>
23+
#include <QTableWidget>
24+
#include <QHeaderView>
25+
#include <QToolButton>
26+
27+
///@cond PRIVATE
28+
29+
QString QgsFilterAlgorithm::name() const
30+
{
31+
return QStringLiteral( "filter" );
32+
}
33+
34+
QString QgsFilterAlgorithm::displayName() const
35+
{
36+
return QObject::tr( "Filter" );
37+
}
38+
39+
QStringList QgsFilterAlgorithm::tags() const
40+
{
41+
return QObject::tr( "filter,proxy,redirect,route" ).split( ',' );
42+
}
43+
44+
QString QgsFilterAlgorithm::group() const
45+
{
46+
return QObject::tr( "Vector geometry" );
47+
}
48+
49+
QString QgsFilterAlgorithm::groupId() const
50+
{
51+
return QStringLiteral( "vectorgeometry" );
52+
}
53+
54+
QgsProcessingAlgorithm::Flags QgsFilterAlgorithm::flags() const
55+
{
56+
return FlagHideFromToolbox;
57+
}
58+
59+
QString QgsFilterAlgorithm::shortHelpString() const
60+
{
61+
return QObject::tr( "This algorithm filters features from the input layer and redirects them to one or several outputs." );
62+
}
63+
64+
QgsFilterAlgorithm *QgsFilterAlgorithm::createInstance() const
65+
{
66+
return new QgsFilterAlgorithm();
67+
}
68+
69+
QgsProcessingAlgorithmConfigurationWidget *QgsFilterAlgorithm::createModelerWidget() const
70+
{
71+
return new QgsFilterAlgorithmConfigurationWidget();
72+
}
73+
74+
QgsFilterAlgorithm::~QgsFilterAlgorithm()
75+
{
76+
qDeleteAll( mOutputs );
77+
}
78+
79+
void QgsFilterAlgorithm::initAlgorithm( const QVariantMap &configuration )
80+
{
81+
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
82+
83+
const QVariantList outputs = configuration.value( QStringLiteral( "outputs" ) ).toList();
84+
for ( const QVariant &output : outputs )
85+
{
86+
const QVariantMap outputDef = output.toMap();
87+
const QString name = QStringLiteral( "OUTPUT_%1" ).arg( outputDef.value( QStringLiteral( "name" ) ).toString() );
88+
QgsProcessingParameterFeatureSink *outputParam = new QgsProcessingParameterFeatureSink( name, outputDef.value( QStringLiteral( "name" ) ).toString() );
89+
outputParam->setFlags( QgsProcessingParameterDefinition::FlagHidden );
90+
addParameter( outputParam );
91+
mOutputs.append( new Output( name, outputDef.value( QStringLiteral( "expression" ) ).toString() ) );
92+
}
93+
}
94+
95+
96+
QgsFilterAlgorithmConfigurationWidget::QgsFilterAlgorithmConfigurationWidget( QWidget *parent )
97+
: QgsProcessingAlgorithmConfigurationWidget( parent )
98+
{
99+
setContentsMargins( 0, 0, 0, 0 );
100+
101+
mOutputExpressionWidget = new QTableWidget();
102+
mOutputExpressionWidget->setColumnCount( 2 );
103+
mOutputExpressionWidget->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "Output Name" ) ) );
104+
mOutputExpressionWidget->setHorizontalHeaderItem( 1, new QTableWidgetItem( tr( "Filter Expression" ) ) );
105+
mOutputExpressionWidget->horizontalHeader()->setStretchLastSection( true );
106+
QGridLayout *layout = new QGridLayout();
107+
setLayout( layout );
108+
109+
layout->addWidget( new QLabel( tr( "Outputs and filters" ) ), 0, 0, 1, 2 );
110+
layout->addWidget( mOutputExpressionWidget, 1, 0, 4, 1 );
111+
QToolButton *addOutputButton = new QToolButton();
112+
addOutputButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddLayer.svg" ) ) );
113+
addOutputButton->setText( tr( "Add Output" ) );
114+
115+
QToolButton *removeOutputButton = new QToolButton();
116+
removeOutputButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionRemoveLayer.svg" ) ) );
117+
removeOutputButton->setToolTip( tr( "Remove Selected Outputs" ) );
118+
119+
layout->addWidget( addOutputButton, 2, 1, 1, 1 );
120+
layout->addWidget( removeOutputButton, 3, 1, 1, 1 );
121+
122+
connect( addOutputButton, &QToolButton::clicked, this, &QgsFilterAlgorithmConfigurationWidget::addOutput );
123+
connect( removeOutputButton, &QToolButton::clicked, this, &QgsFilterAlgorithmConfigurationWidget::removeSelectedOutputs );
124+
125+
connect( mOutputExpressionWidget->selectionModel(), &QItemSelectionModel::selectionChanged, [removeOutputButton, this]
126+
{
127+
removeOutputButton->setEnabled( !mOutputExpressionWidget->selectionModel()->selectedIndexes().isEmpty() );
128+
} );
129+
}
130+
131+
QVariantMap QgsFilterAlgorithmConfigurationWidget::configuration() const
132+
{
133+
QVariantList outputs;
134+
135+
for ( int i = 0; i < mOutputExpressionWidget->rowCount(); ++i )
136+
{
137+
QVariantMap output;
138+
output.insert( QStringLiteral( "name" ), mOutputExpressionWidget->item( i, 0 )->text() );
139+
output.insert( QStringLiteral( "expression" ), mOutputExpressionWidget->item( i, 1 )->text() );
140+
outputs.append( output );
141+
}
142+
143+
QVariantMap map;
144+
map.insert( "outputs", outputs );
145+
146+
return map;
147+
}
148+
149+
150+
QVariantMap QgsFilterAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
151+
{
152+
std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
153+
if ( !source )
154+
return QVariantMap();
155+
156+
QgsExpressionContext expressionContext = context.expressionContext();
157+
for ( Output *output : qgis::as_const( mOutputs ) )
158+
{
159+
output->sink.reset( parameterAsSink( parameters, output->name, context, output->destinationIdentifier, source->fields(), source->wkbType(), source->sourceCrs() ) );
160+
output->expression.prepare( &expressionContext );
161+
}
162+
163+
long count = source->featureCount();
164+
165+
QgsFeature f;
166+
QgsFeatureIterator it = source->getFeatures();
167+
168+
double step = count > 0 ? 100.0 / count : 1;
169+
int current = 0;
170+
171+
while ( it.nextFeature( f ) )
172+
{
173+
if ( feedback->isCanceled() )
174+
{
175+
break;
176+
}
177+
178+
expressionContext.setFeature( f );
179+
180+
for ( Output *output : qgis::as_const( mOutputs ) )
181+
{
182+
if ( output->expression.evaluate( &expressionContext ).toBool() )
183+
output->sink->addFeature( f, QgsFeatureSink::FastInsert );
184+
}
185+
186+
feedback->setProgress( current * step );
187+
current++;
188+
}
189+
190+
QVariantMap outputs;
191+
for ( const Output *output : qgis::as_const( mOutputs ) )
192+
{
193+
outputs.insert( output->name, output->destinationIdentifier );
194+
}
195+
return outputs;
196+
}
197+
198+
199+
void QgsFilterAlgorithmConfigurationWidget::setConfiguration( const QVariantMap &configuration )
200+
{
201+
mOutputExpressionWidget->setRowCount( 0 );
202+
int currentRow = 0;
203+
const QVariantList outputs = configuration.value( "outputs" ).toList();
204+
205+
for ( const QVariant &outputvar : outputs )
206+
{
207+
const QVariantMap output = outputvar.toMap();
208+
mOutputExpressionWidget->insertRow( currentRow );
209+
mOutputExpressionWidget->setItem( currentRow, 0, new QTableWidgetItem( output.value( "name" ).toString() ) );
210+
mOutputExpressionWidget->setItem( currentRow, 1, new QTableWidgetItem( output.value( "expression" ).toString() ) );
211+
212+
currentRow++;
213+
}
214+
215+
if ( outputs.isEmpty() )
216+
addOutput();
217+
}
218+
219+
void QgsFilterAlgorithmConfigurationWidget::removeSelectedOutputs()
220+
{
221+
QItemSelection selection( mOutputExpressionWidget->selectionModel()->selection() );
222+
223+
QList<int> rows;
224+
const QModelIndexList indexes = selection.indexes();
225+
for ( const QModelIndex &index : indexes )
226+
{
227+
rows.append( index.row() );
228+
}
229+
230+
qSort( rows );
231+
232+
int prev = -1;
233+
for ( int i = rows.count() - 1; i >= 0; i -= 1 )
234+
{
235+
int current = rows[i];
236+
if ( current != prev )
237+
{
238+
mOutputExpressionWidget->removeRow( current );
239+
prev = current;
240+
}
241+
}
242+
}
243+
244+
void QgsFilterAlgorithmConfigurationWidget::addOutput()
245+
{
246+
mOutputExpressionWidget->setRowCount( mOutputExpressionWidget->rowCount() + 1 );
247+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/***************************************************************************
2+
qgsalgorithmfilter.h
3+
---------------------
4+
begin : April 2018
5+
copyright : (C) 2018 by Matthias Kuhn
6+
email : matthias@opengis.ch
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+
#ifndef QGSFILTERALGORITHM_H
19+
#define QGSFILTERALGORITHM_H
20+
21+
#define SIP_NO_FILE
22+
23+
#include "qgis.h"
24+
#include "qgsprocessingalgorithm.h"
25+
#include "qgsprocessingalgorithmconfigurationwidget.h"
26+
27+
class QgsProcessingModelAlgorithm;
28+
class QTableWidget;
29+
30+
///@cond PRIVATE
31+
32+
33+
class QgsFilterAlgorithmConfigurationWidget : public QgsProcessingAlgorithmConfigurationWidget
34+
{
35+
Q_OBJECT
36+
37+
public:
38+
QgsFilterAlgorithmConfigurationWidget( QWidget *parent = nullptr );
39+
40+
QVariantMap configuration() const override;
41+
42+
void setConfiguration( const QVariantMap &configuration ) override;
43+
44+
private slots:
45+
void removeSelectedOutputs();
46+
void addOutput();
47+
48+
private:
49+
QTableWidget *mOutputExpressionWidget;
50+
};
51+
52+
53+
/**
54+
* Feature filter algorithm for modeler.
55+
* Accepts a list of expressions and names and creates outputs where
56+
* matching features are sent to.
57+
*/
58+
class QgsFilterAlgorithm : public QgsProcessingAlgorithm
59+
{
60+
public:
61+
QgsFilterAlgorithm() = default;
62+
~QgsFilterAlgorithm();
63+
64+
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
65+
QString name() const override;
66+
QString displayName() const override;
67+
QStringList tags() const override;
68+
QString group() const override;
69+
QString groupId() const override;
70+
virtual Flags flags() const override;
71+
QString shortHelpString() const override;
72+
QgsFilterAlgorithm *createInstance() const override SIP_FACTORY;
73+
QgsProcessingAlgorithmConfigurationWidget *createModelerWidget() const override SIP_FACTORY;
74+
75+
protected:
76+
77+
QVariantMap processAlgorithm( const QVariantMap &parameters,
78+
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
79+
80+
private:
81+
struct Output
82+
{
83+
Output( const QString &name, const QString &expression )
84+
: name( name )
85+
, expression( expression )
86+
{}
87+
QString name;
88+
QgsExpression expression;
89+
std::unique_ptr< QgsFeatureSink > sink;
90+
QString destinationIdentifier;
91+
};
92+
93+
QList<Output *> mOutputs;
94+
};
95+
96+
#endif // QGSFILTERALGORITHM_H

‎src/analysis/processing/qgsnativealgorithms.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "qgsalgorithmextractbylocation.h"
3636
#include "qgsalgorithmextractvertices.h"
3737
#include "qgsalgorithmfiledownloader.h"
38+
#include "qgsalgorithmfilter.h"
3839
#include "qgsalgorithmfixgeometries.h"
3940
#include "qgsalgorithmjoinbyattribute.h"
4041
#include "qgsalgorithmjoinwithlines.h"
@@ -133,6 +134,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
133134
addAlgorithm( new QgsExtractByLocationAlgorithm() );
134135
addAlgorithm( new QgsExtractVerticesAlgorithm() );
135136
addAlgorithm( new QgsFileDownloaderAlgorithm() );
137+
addAlgorithm( new QgsFilterAlgorithm() );
136138
addAlgorithm( new QgsFixGeometriesAlgorithm() );
137139
addAlgorithm( new QgsImportPhotosAlgorithm() );
138140
addAlgorithm( new QgsJoinByAttributeAlgorithm() );

‎src/core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ SET(QGIS_CORE_SRCS
102102
locator/qgslocatormodel.cpp
103103

104104
processing/qgsprocessingalgorithm.cpp
105+
processing/qgsprocessingalgorithmconfigurationwidget.cpp
105106
processing/qgsprocessingalgrunnertask.cpp
106107
processing/qgsprocessingcontext.cpp
107108
processing/qgsprocessingfeedback.cpp
@@ -668,6 +669,7 @@ SET(QGIS_CORE_MOC_HDRS
668669
locator/qgslocatorfilter.h
669670
locator/qgslocatormodel.h
670671

672+
processing/qgsprocessingalgorithmconfigurationwidget.h
671673
processing/qgsprocessingalgrunnertask.h
672674
processing/qgsprocessingfeedback.h
673675
processing/qgsprocessingprovider.h
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include "qgsprocessingalgorithmconfigurationwidget.h"
2+
3+
QgsProcessingAlgorithmConfigurationWidget::QgsProcessingAlgorithmConfigurationWidget( QWidget *parent )
4+
: QWidget( parent )
5+
{
6+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#ifndef QGSPROCESSINGALGORITHMCONFIGURATIONWIDGET_H
2+
#define QGSPROCESSINGALGORITHMCONFIGURATIONWIDGET_H
3+
4+
5+
#include <QWidget>
6+
#include <QVariantMap>
7+
8+
#include "qgis_core.h"
9+
10+
class CORE_EXPORT QgsProcessingAlgorithmConfigurationWidget : public QWidget
11+
{
12+
Q_OBJECT
13+
14+
public:
15+
QgsProcessingAlgorithmConfigurationWidget( QWidget *parent = nullptr );
16+
virtual ~QgsProcessingAlgorithmConfigurationWidget() = default;
17+
virtual QVariantMap configuration() const = 0;
18+
virtual void setConfiguration( const QVariantMap &configuration ) = 0;
19+
};
20+
21+
#endif // QGSPROCESSINGALGORITHMCONFIGURATIONWIDGET_H

0 commit comments

Comments
 (0)
Please sign in to comment.