35
35
QgsGeometry ,
36
36
QgsWkbTypes ,
37
37
QgsMessageLog ,
38
- QgsProcessingUtils )
38
+ QgsProcessingParameterFeatureSource ,
39
+ QgsProcessingParameterFeatureSink ,
40
+ QgsSpatialIndex )
39
41
40
42
from processing .algs .qgis .QgisAlgorithm import QgisAlgorithm
41
- from processing .core .parameters import ParameterVector
42
- from processing .core .outputs import OutputVector
43
43
from processing .tools import vector
44
44
45
45
pluginPath = os .path .split (os .path .split (os .path .dirname (__file__ ))[0 ])[0 ]
57
57
class Union (QgisAlgorithm ):
58
58
59
59
INPUT = 'INPUT'
60
- INPUT2 = 'INPUT2 '
60
+ OVERLAY = 'OVERLAY '
61
61
OUTPUT = 'OUTPUT'
62
62
63
63
def icon (self ):
@@ -70,11 +70,12 @@ def __init__(self):
70
70
super ().__init__ ()
71
71
72
72
def initAlgorithm (self , config = None ):
73
- self .addParameter (ParameterVector (Union .INPUT ,
74
- self .tr ('Input layer' )))
75
- self .addParameter (ParameterVector (Union .INPUT2 ,
76
- self .tr ('Input layer 2' )))
77
- self .addOutput (OutputVector (Union .OUTPUT , self .tr ('Union' )))
73
+ self .addParameter (QgsProcessingParameterFeatureSource (self .INPUT ,
74
+ self .tr ('Input layer' )))
75
+ self .addParameter (QgsProcessingParameterFeatureSource (self .OVERLAY ,
76
+ self .tr ('Union layer' )))
77
+
78
+ self .addParameter (QgsProcessingParameterFeatureSink (self .OUTPUT , self .tr ('Union' )))
78
79
79
80
def name (self ):
80
81
return 'union'
@@ -83,59 +84,60 @@ def displayName(self):
83
84
return self .tr ('Union' )
84
85
85
86
def processAlgorithm (self , parameters , context , feedback ):
86
- vlayerA = QgsProcessingUtils .mapLayerFromString (self .getParameterValue (Union .INPUT ), context )
87
- vlayerB = QgsProcessingUtils .mapLayerFromString (self .getParameterValue (Union .INPUT2 ), context )
88
-
89
- geomType = vlayerA .wkbType ()
90
- fields = vector .combineFields (vlayerA .fields (), vlayerB .fields ())
91
- writer = self .getOutputFromName (Union .OUTPUT ).getVectorWriter (fields , geomType , vlayerA .crs (), context )
92
- inFeatA = QgsFeature ()
93
- inFeatB = QgsFeature ()
87
+ sourceA = self .parameterAsSource (parameters , self .INPUT , context )
88
+ sourceB = self .parameterAsSource (parameters , self .OVERLAY , context )
89
+
90
+ geomType = QgsWkbTypes .multiType (sourceA .wkbType ())
91
+ fields = vector .combineFields (sourceA .fields (), sourceB .fields ())
92
+
93
+ (sink , dest_id ) = self .parameterAsSink (parameters , self .OUTPUT , context ,
94
+ fields , geomType , sourceA .sourceCrs ())
95
+
96
+ featA = QgsFeature ()
97
+ featB = QgsFeature ()
94
98
outFeat = QgsFeature ()
95
- indexA = QgsProcessingUtils .createSpatialIndex (vlayerB , context )
96
- indexB = QgsProcessingUtils .createSpatialIndex (vlayerA , context )
97
99
100
+ indexA = QgsSpatialIndex (sourceA )
101
+ indexB = QgsSpatialIndex (sourceB .getFeatures (QgsFeatureRequest ().setSubsetOfAttributes ([]).setDestinationCrs (sourceA .sourceCrs ())))
102
+
103
+ total = 100.0 / (sourceA .featureCount () * sourceB .featureCount ()) if sourceA .featureCount () and sourceB .featureCount () else 1
98
104
count = 0
99
- nElement = 0
100
- featuresA = QgsProcessingUtils .getFeatures (vlayerA , context )
101
- nFeat = QgsProcessingUtils .featureCount (vlayerA , context )
102
- for inFeatA in featuresA :
103
- feedback .setProgress (nElement / float (nFeat ) * 50 )
104
- nElement += 1
105
+
106
+ for featA in sourceA .getFeatures ():
107
+ if feedback .isCanceled ():
108
+ break
109
+
105
110
lstIntersectingB = []
106
- geom = inFeatA .geometry ()
107
- atMapA = inFeatA .attributes ()
108
- intersects = indexA .intersects (geom .boundingBox ())
111
+ geom = featA .geometry ()
112
+ atMapA = featA .attributes ()
113
+ intersects = indexB .intersects (geom .boundingBox ())
109
114
if len (intersects ) < 1 :
110
115
try :
111
116
outFeat .setGeometry (geom )
112
117
outFeat .setAttributes (atMapA )
113
- writer .addFeature (outFeat , QgsFeatureSink .FastInsert )
118
+ sink .addFeature (outFeat , QgsFeatureSink .FastInsert )
114
119
except :
115
120
# This really shouldn't happen, as we haven't
116
121
# edited the input geom at all
117
- QgsMessageLog .logMessage (self .tr ('Feature geometry error: One or more output features ignored due to invalid geometry.' ),
118
- self .tr ('Processing' ), QgsMessageLog .INFO )
122
+ feedback .pushInfo (self .tr ('Feature geometry error: One or more output features ignored due to invalid geometry.' ))
119
123
else :
120
- request = QgsFeatureRequest ().setFilterFids (intersects )
124
+ request = QgsFeatureRequest ().setFilterFids (intersects ).setSubsetOfAttributes ([])
125
+ request .setDestinationCrs (sourceA .sourceCrs ())
121
126
122
127
engine = QgsGeometry .createGeometryEngine (geom .geometry ())
123
128
engine .prepareGeometry ()
124
129
125
- for inFeatB in vlayerB .getFeatures (request ):
126
- count += 1
127
-
128
- atMapB = inFeatB .attributes ()
129
- tmpGeom = inFeatB .geometry ()
130
+ for featB in sourceB .getFeatures (request ):
131
+ atMapB = featB .attributes ()
132
+ tmpGeom = featB .geometry ()
130
133
131
134
if engine .intersects (tmpGeom .geometry ()):
132
135
int_geom = geom .intersection (tmpGeom )
133
136
lstIntersectingB .append (tmpGeom )
134
137
135
138
if not int_geom :
136
139
# There was a problem creating the intersection
137
- QgsMessageLog .logMessage (self .tr ('GEOS geoprocessing error: One or more input features have invalid geometry.' ),
138
- self .tr ('Processing' ), QgsMessageLog .INFO )
140
+ feedback .pushInfo (self .tr ('Feature geometry error: One or more output features ignored due to invalid geometry.' ))
139
141
int_geom = QgsGeometry ()
140
142
else :
141
143
int_geom = QgsGeometry (int_geom )
@@ -149,10 +151,9 @@ def processAlgorithm(self, parameters, context, feedback):
149
151
try :
150
152
outFeat .setGeometry (int_geom )
151
153
outFeat .setAttributes (atMapA + atMapB )
152
- writer .addFeature (outFeat , QgsFeatureSink .FastInsert )
154
+ sink .addFeature (outFeat , QgsFeatureSink .FastInsert )
153
155
except :
154
- QgsMessageLog .logMessage (self .tr ('Feature geometry error: One or more output features ignored due to invalid geometry.' ),
155
- self .tr ('Processing' ), QgsMessageLog .INFO )
156
+ feedback .pushInfo (self .tr ('Feature geometry error: One or more output features ignored due to invalid geometry.' ))
156
157
else :
157
158
# Geometry list: prevents writing error
158
159
# in geometries of different types
@@ -162,63 +163,64 @@ def processAlgorithm(self, parameters, context, feedback):
162
163
try :
163
164
outFeat .setGeometry (int_geom )
164
165
outFeat .setAttributes (atMapA + atMapB )
165
- writer .addFeature (outFeat , QgsFeatureSink .FastInsert )
166
+ sink .addFeature (outFeat , QgsFeatureSink .FastInsert )
166
167
except :
167
- QgsMessageLog .logMessage (self .tr ('Feature geometry error: One or more output features ignored due to invalid geometry.' ),
168
- self .tr ('Processing' ), QgsMessageLog .INFO )
168
+ feedback .pushInfo (self .tr ('Feature geometry error: One or more output features ignored due to invalid geometry.' ))
169
169
170
- # the remaining bit of inFeatA 's geometry
170
+ # the remaining bit of featA 's geometry
171
171
# if there is nothing left, this will just silently fail and we're good
172
172
diff_geom = QgsGeometry (geom )
173
173
if len (lstIntersectingB ) != 0 :
174
174
intB = QgsGeometry .unaryUnion (lstIntersectingB )
175
175
diff_geom = diff_geom .difference (intB )
176
176
177
- if diff_geom .wkbType () == 0 or QgsWkbTypes .flatType (diff_geom .geometry ().wkbType ()) == QgsWkbTypes .GeometryCollection :
177
+ if diff_geom .wkbType () == QgsWkbTypes . Unknown or QgsWkbTypes .flatType (diff_geom .geometry ().wkbType ()) == QgsWkbTypes .GeometryCollection :
178
178
temp_list = diff_geom .asGeometryCollection ()
179
179
for i in temp_list :
180
180
if i .type () == geom .type ():
181
181
diff_geom = QgsGeometry (i )
182
182
try :
183
183
outFeat .setGeometry (diff_geom )
184
184
outFeat .setAttributes (atMapA )
185
- writer .addFeature (outFeat , QgsFeatureSink .FastInsert )
185
+ sink .addFeature (outFeat , QgsFeatureSink .FastInsert )
186
186
except :
187
- QgsMessageLog .logMessage (self .tr ('Feature geometry error: One or more output features ignored due to invalid geometry.' ),
188
- self .tr ('Processing' ), QgsMessageLog .INFO )
187
+ feedback .pushInfo (self .tr ('Feature geometry error: One or more output features ignored due to invalid geometry.' ))
189
188
190
- length = len (vlayerA .fields ())
189
+ count += 1
190
+ feedback .setProgress (int (count * total ))
191
+
192
+ length = len (sourceA .fields ())
191
193
atMapA = [None ] * length
192
194
193
- featuresA = QgsProcessingUtils .getFeatures (vlayerB , context )
194
- nFeat = QgsProcessingUtils . featureCount ( vlayerB , context )
195
- for inFeatA in featuresA :
196
- feedback . setProgress ( nElement / float ( nFeat ) * 100 )
195
+ for featA in sourceB .getFeatures (QgsFeatureRequest (). setDestinationCrs ( sourceA . sourceCrs ())):
196
+ if feedback . isCanceled ():
197
+ break
198
+
197
199
add = False
198
- geom = inFeatA .geometry ()
200
+ geom = featA .geometry ()
199
201
diff_geom = QgsGeometry (geom )
200
202
atMap = [None ] * length
201
- atMap .extend (inFeatA .attributes ())
202
- intersects = indexB .intersects (geom .boundingBox ())
203
+ atMap .extend (featA .attributes ())
204
+ intersects = indexA .intersects (geom .boundingBox ())
203
205
204
206
if len (intersects ) < 1 :
205
207
try :
206
208
outFeat .setGeometry (geom )
207
209
outFeat .setAttributes (atMap )
208
- writer .addFeature (outFeat , QgsFeatureSink .FastInsert )
210
+ sink .addFeature (outFeat , QgsFeatureSink .FastInsert )
209
211
except :
210
- QgsMessageLog .logMessage (self .tr ('Feature geometry error: One or more output features ignored due to invalid geometry.' ),
211
- self .tr ('Processing' ), QgsMessageLog .INFO )
212
+ feedback .pushInfo (self .tr ('Feature geometry error: One or more output features ignored due to invalid geometry.' ))
212
213
else :
213
- request = QgsFeatureRequest ().setFilterFids (intersects )
214
+ request = QgsFeatureRequest ().setFilterFids (intersects ).setSubsetOfAttributes ([])
215
+ request .setDestinationCrs (sourceA .sourceCrs ())
214
216
215
217
# use prepared geometries for faster intersection tests
216
218
engine = QgsGeometry .createGeometryEngine (diff_geom .geometry ())
217
219
engine .prepareGeometry ()
218
220
219
- for inFeatB in vlayerA .getFeatures (request ):
220
- atMapB = inFeatB .attributes ()
221
- tmpGeom = inFeatB .geometry ()
221
+ for featB in sourceA .getFeatures (request ):
222
+ atMapB = featB .attributes ()
223
+ tmpGeom = featB .geometry ()
222
224
223
225
if engine .intersects (tmpGeom .geometry ()):
224
226
add = True
@@ -229,19 +231,19 @@ def processAlgorithm(self, parameters, context, feedback):
229
231
# intersects, but the geometry doesn't
230
232
outFeat .setGeometry (diff_geom )
231
233
outFeat .setAttributes (atMap )
232
- writer .addFeature (outFeat , QgsFeatureSink .FastInsert )
234
+ sink .addFeature (outFeat , QgsFeatureSink .FastInsert )
233
235
except :
234
- QgsMessageLog .logMessage (self .tr ('Feature geometry error: One or more output features ignored due to invalid geometry.' ),
235
- self .tr ('Processing' ), QgsMessageLog .INFO )
236
+ feedback .pushInfo (self .tr ('Feature geometry error: One or more output features ignored due to invalid geometry.' ))
236
237
237
238
if add :
238
239
try :
239
240
outFeat .setGeometry (diff_geom )
240
241
outFeat .setAttributes (atMap )
241
- writer .addFeature (outFeat , QgsFeatureSink .FastInsert )
242
+ sink .addFeature (outFeat , QgsFeatureSink .FastInsert )
242
243
except :
243
- QgsMessageLog .logMessage (self .tr ('Feature geometry error: One or more output features ignored due to invalid geometry.' ),
244
- self .tr ('Processing' ), QgsMessageLog .INFO )
245
- nElement += 1
244
+ feedback .pushInfo (self .tr ('Feature geometry error: One or more output features ignored due to invalid geometry.' ))
245
+
246
+ count += 1
247
+ feedback .setProgress (int (count * total ))
246
248
247
- del writer
249
+ return { self . OUTPUT : dest_id }
0 commit comments