@@ -60,70 +60,88 @@ def defineCharacteristics(self):
60
60
self .addOutput (OutputVector (Clip .OUTPUT , self .tr ('Clipped' )))
61
61
62
62
def processAlgorithm (self , progress ):
63
- sourceLayer = dataobjects .getObjectFromUri (
63
+ source_layer = dataobjects .getObjectFromUri (
64
64
self .getParameterValue (Clip .INPUT ))
65
- maskLayer = dataobjects .getObjectFromUri (
65
+ mask_layer = dataobjects .getObjectFromUri (
66
66
self .getParameterValue (Clip .OVERLAY ))
67
67
68
68
writer = self .getOutputFromName (self .OUTPUT ).getVectorWriter (
69
- sourceLayer .pendingFields (),
70
- sourceLayer .dataProvider ().geometryType (),
71
- sourceLayer .dataProvider ().crs ())
72
-
73
- inFeatA = QgsFeature ()
74
- inFeatB = QgsFeature ()
75
- outFeat = QgsFeature ()
69
+ source_layer .fields (),
70
+ source_layer .dataProvider ().geometryType (),
71
+ source_layer .crs ())
76
72
77
73
# first build up a list of clip geometries
78
74
clip_geoms = []
79
- for maskFeat in vector .features (maskLayer , QgsFeatureRequest ().setSubsetOfAttributes ([])):
75
+ for maskFeat in vector .features (mask_layer , QgsFeatureRequest ().setSubsetOfAttributes ([])):
80
76
clip_geoms .append (maskFeat .geometry ())
81
77
78
+ # are we clipping against a single feature? if so, we can show finer progress reports
82
79
if len (clip_geoms ) > 1 :
83
80
combined_clip_geom = QgsGeometry .unaryUnion (clip_geoms )
81
+ single_clip_feature = False
84
82
else :
85
83
combined_clip_geom = clip_geoms [0 ]
84
+ single_clip_feature = True
86
85
87
- # use prepared geometries for faster insection tests
86
+ # use prepared geometries for faster intersection tests
88
87
engine = QgsGeometry .createGeometryEngine (combined_clip_geom .geometry ())
89
88
engine .prepareGeometry ()
90
89
91
- input_features = [f for f in vector .features (sourceLayer , QgsFeatureRequest ().setFilterRect (combined_clip_geom .boundingBox ()))]
92
- total = 100.0 / len (input_features )
93
- for current , in_feat in enumerate (input_features ):
94
- if not in_feat .geometry ():
95
- continue
96
-
97
- if not engine .intersects (in_feat .geometry ().geometry ()):
98
- continue
99
-
100
- if not engine .contains (in_feat .geometry ().geometry ()):
101
- cur_geom = in_feat .geometry ()
102
- new_geom = combined_clip_geom .intersection (cur_geom )
103
- if new_geom .wkbType () == Qgis .WKBUnknown or QgsWKBTypes .flatType (new_geom .geometry ().wkbType ()) == QgsWKBTypes .GeometryCollection :
104
- int_com = in_feat .geometry ().combine (new_geom )
105
- int_sym = in_feat .geometry ().symDifference (new_geom )
106
- new_geom = int_com .difference (int_sym )
107
- if new_geom .isGeosEmpty () or not new_geom .isGeosValid ():
108
- ProcessingLog .addToLog (ProcessingLog .LOG_ERROR ,
109
- self .tr ('GEOS geoprocessing error: One or more '
110
- 'input features have invalid geometry.' ))
90
+ tested_feature_ids = set ()
91
+
92
+ for i , clip_geom in enumerate (clip_geoms ):
93
+ input_features = [f for f in vector .features (source_layer , QgsFeatureRequest ().setFilterRect (clip_geom .boundingBox ()))]
94
+
95
+ if single_clip_feature :
96
+ total = 100.0 / len (input_features )
111
97
else :
112
- # clip geometry totally contains feature geometry, so no need to perform intersection
113
- new_geom = in_feat .geometry ()
114
-
115
- try :
116
- outFeat = QgsFeature ()
117
- outFeat .setGeometry (new_geom )
118
- outFeat .setAttributes (in_feat .attributes ())
119
- writer .addFeature (outFeat )
120
- except :
121
- ProcessingLog .addToLog (ProcessingLog .LOG_ERROR ,
122
- self .tr ('Feature geometry error: One or more '
123
- 'output features ignored due to '
124
- 'invalid geometry.' ))
125
- continue
126
-
127
- progress .setPercentage (int (current * total ))
98
+ total = 0
99
+
100
+ for current , in_feat in enumerate (input_features ):
101
+ if not in_feat .geometry ():
102
+ continue
103
+
104
+ if in_feat .id () in tested_feature_ids :
105
+ # don't retest a feature we have already checked
106
+ continue
107
+
108
+ tested_feature_ids .add (in_feat .id ())
109
+
110
+ if not engine .intersects (in_feat .geometry ().geometry ()):
111
+ continue
112
+
113
+ if not engine .contains (in_feat .geometry ().geometry ()):
114
+ cur_geom = in_feat .geometry ()
115
+ new_geom = combined_clip_geom .intersection (cur_geom )
116
+ if new_geom .wkbType () == Qgis .WKBUnknown or QgsWKBTypes .flatType (new_geom .geometry ().wkbType ()) == QgsWKBTypes .GeometryCollection :
117
+ int_com = in_feat .geometry ().combine (new_geom )
118
+ int_sym = in_feat .geometry ().symDifference (new_geom )
119
+ new_geom = int_com .difference (int_sym )
120
+ if new_geom .isGeosEmpty () or not new_geom .isGeosValid ():
121
+ ProcessingLog .addToLog (ProcessingLog .LOG_ERROR ,
122
+ self .tr ('GEOS geoprocessing error: One or more '
123
+ 'input features have invalid geometry.' ))
124
+ else :
125
+ # clip geometry totally contains feature geometry, so no need to perform intersection
126
+ new_geom = in_feat .geometry ()
127
+
128
+ try :
129
+ out_feat = QgsFeature ()
130
+ out_feat .setGeometry (new_geom )
131
+ out_feat .setAttributes (in_feat .attributes ())
132
+ writer .addFeature (out_feat )
133
+ except :
134
+ ProcessingLog .addToLog (ProcessingLog .LOG_ERROR ,
135
+ self .tr ('Feature geometry error: One or more '
136
+ 'output features ignored due to '
137
+ 'invalid geometry.' ))
138
+ continue
139
+
140
+ if single_clip_feature :
141
+ progress .setPercentage (int (current * total ))
142
+
143
+ if not single_clip_feature :
144
+ # coarse progress report for multiple clip geometries
145
+ progress .setPercentage (100.0 * i / len (clip_geoms ))
128
146
129
147
del writer
0 commit comments