27
27
from qgis .core import (QgsExpression ,
28
28
QgsFeatureRequest ,
29
29
QgsApplication ,
30
- QgsProcessingUtils )
30
+ QgsProcessingUtils ,
31
+ QgsProcessingParameterFeatureSource ,
32
+ QgsProcessingParameterExpression ,
33
+ QgsProcessingParameterFeatureSink ,
34
+ QgsProcessingOutputVectorLayer ,
35
+ QgsProcessingParameterDefinition )
31
36
32
37
from processing .core .GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
33
38
from processing .core .parameters import ParameterVector
@@ -41,6 +46,7 @@ class ExtractByExpression(QgisAlgorithm):
41
46
INPUT = 'INPUT'
42
47
EXPRESSION = 'EXPRESSION'
43
48
OUTPUT = 'OUTPUT'
49
+ FAIL_OUTPUT = 'FAIL_OUTPUT'
44
50
45
51
def icon (self ):
46
52
return QgsApplication .getThemeIcon ("/providerQgis.svg" )
@@ -56,11 +62,16 @@ def group(self):
56
62
57
63
def __init__ (self ):
58
64
super ().__init__ ()
59
- self .addParameter (ParameterVector (self .INPUT ,
60
- self .tr ('Input Layer' )))
61
- self .addParameter (ParameterExpression (self .EXPRESSION ,
62
- self .tr ("Expression" ), parent_layer = self .INPUT ))
63
- self .addOutput (OutputVector (self .OUTPUT , self .tr ('Extracted (expression)' )))
65
+ self .addParameter (QgsProcessingParameterFeatureSource (self .INPUT ,
66
+ self .tr ('Input layer' )))
67
+ self .addParameter (QgsProcessingParameterExpression (self .EXPRESSION ,
68
+ self .tr ('Expression' ), None , self .INPUT ))
69
+
70
+ self .addParameter (QgsProcessingParameterFeatureSink (self .OUTPUT , self .tr ('Matching features' )))
71
+ self .addOutput (QgsProcessingOutputVectorLayer (self .OUTPUT , self .tr ('Matching (expression)' )))
72
+ self .addParameter (QgsProcessingParameterFeatureSink (self .FAIL_OUTPUT , self .tr ('Non-matching' ),
73
+ QgsProcessingParameterDefinition .TypeVectorAny , None , True ))
74
+ self .addOutput (QgsProcessingOutputVectorLayer (self .FAIL_OUTPUT , self .tr ('Non-matching (expression)' )))
64
75
65
76
def name (self ):
66
77
return 'extractbyexpression'
@@ -69,18 +80,48 @@ def displayName(self):
69
80
return self .tr ('Extract by expression' )
70
81
71
82
def processAlgorithm (self , parameters , context , feedback ):
72
- layer = QgsProcessingUtils .mapLayerFromString (self .getParameterValue (self .INPUT ), context )
73
- expression_string = self .getParameterValue (self .EXPRESSION )
74
- writer = self .getOutputFromName (self .OUTPUT ).getVectorWriter (layer .fields (), layer .wkbType (), layer .crs (),
75
- context )
83
+ source = self .parameterAsSource (parameters , self .INPUT , context )
84
+ expression_string = self .parameterAsExpression (parameters , self .EXPRESSION , context )
85
+
86
+ (matching_sink , matching_sink_id ) = self .parameterAsSink (parameters , self .OUTPUT , context ,
87
+ source .fields (), source .wkbType (), source .sourceCrs ())
88
+ (nonmatching_sink , non_matching_sink_id ) = self .parameterAsSink (parameters , self .FAIL_OUTPUT , context ,
89
+ source .fields (), source .wkbType (), source .sourceCrs ())
76
90
77
91
expression = QgsExpression (expression_string )
78
- if not expression .hasParserError ():
92
+ if expression .hasParserError ():
93
+ raise GeoAlgorithmExecutionException (expression .parserErrorString ())
94
+ expression_context = self .createExpressionContext (parameters , context )
95
+
96
+ if not nonmatching_sink :
97
+ # not saving failing features - so only fetch good features
79
98
req = QgsFeatureRequest ().setFilterExpression (expression_string )
99
+ req .setExpressionContext (expression_context )
100
+
101
+ for f in source .getFeatures (req ):
102
+ if feedback .isCanceled ():
103
+ break
104
+ matching_sink .addFeature (f )
80
105
else :
81
- raise GeoAlgorithmExecutionException (expression .parserErrorString ())
106
+ # saving non-matching features, so we need EVERYTHING
107
+ expression_context .setFields (source .fields ())
108
+ expression .prepare (expression_context )
109
+
110
+ total = 100.0 / source .featureCount ()
111
+
112
+ for current , f in enumerate (source .getFeatures ()):
113
+ if feedback .isCanceled ():
114
+ break
115
+
116
+ expression_context .setFeature (f )
117
+ if expression .evaluate (expression_context ):
118
+ matching_sink .addFeature (f )
119
+ else :
120
+ nonmatching_sink .addFeature (f )
82
121
83
- for f in layer .getFeatures (req ):
84
- writer .addFeature (f )
122
+ feedback .setProgress (int (current * total ))
85
123
86
- del writer
124
+ results = {self .OUTPUT : matching_sink_id }
125
+ if nonmatching_sink :
126
+ results [self .FAIL_OUTPUT ] = non_matching_sink_id
127
+ return results
0 commit comments