3535 QgsGeometry ,
3636 QgsWkbTypes ,
3737 QgsMessageLog ,
38- QgsProcessingUtils )
38+ QgsProcessingParameterFeatureSource ,
39+ QgsProcessingParameterFeatureSink ,
40+ QgsSpatialIndex )
3941
4042from processing .algs .qgis .QgisAlgorithm import QgisAlgorithm
41- from processing .core .parameters import ParameterVector
42- from processing .core .outputs import OutputVector
4343from processing .tools import vector
4444
4545pluginPath = os .path .split (os .path .split (os .path .dirname (__file__ ))[0 ])[0 ]
5757class Union (QgisAlgorithm ):
5858
5959 INPUT = 'INPUT'
60- INPUT2 = 'INPUT2 '
60+ OVERLAY = 'OVERLAY '
6161 OUTPUT = 'OUTPUT'
6262
6363 def icon (self ):
@@ -70,11 +70,12 @@ def __init__(self):
7070 super ().__init__ ()
7171
7272 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' )))
7879
7980 def name (self ):
8081 return 'union'
@@ -83,59 +84,60 @@ def displayName(self):
8384 return self .tr ('Union' )
8485
8586 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 ()
9498 outFeat = QgsFeature ()
95- indexA = QgsProcessingUtils .createSpatialIndex (vlayerB , context )
96- indexB = QgsProcessingUtils .createSpatialIndex (vlayerA , context )
9799
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
98104 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+
105110 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 ())
109114 if len (intersects ) < 1 :
110115 try :
111116 outFeat .setGeometry (geom )
112117 outFeat .setAttributes (atMapA )
113- writer .addFeature (outFeat , QgsFeatureSink .FastInsert )
118+ sink .addFeature (outFeat , QgsFeatureSink .FastInsert )
114119 except :
115120 # This really shouldn't happen, as we haven't
116121 # 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.' ))
119123 else :
120- request = QgsFeatureRequest ().setFilterFids (intersects )
124+ request = QgsFeatureRequest ().setFilterFids (intersects ).setSubsetOfAttributes ([])
125+ request .setDestinationCrs (sourceA .sourceCrs ())
121126
122127 engine = QgsGeometry .createGeometryEngine (geom .geometry ())
123128 engine .prepareGeometry ()
124129
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 ()
130133
131134 if engine .intersects (tmpGeom .geometry ()):
132135 int_geom = geom .intersection (tmpGeom )
133136 lstIntersectingB .append (tmpGeom )
134137
135138 if not int_geom :
136139 # 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.' ))
139141 int_geom = QgsGeometry ()
140142 else :
141143 int_geom = QgsGeometry (int_geom )
@@ -149,10 +151,9 @@ def processAlgorithm(self, parameters, context, feedback):
149151 try :
150152 outFeat .setGeometry (int_geom )
151153 outFeat .setAttributes (atMapA + atMapB )
152- writer .addFeature (outFeat , QgsFeatureSink .FastInsert )
154+ sink .addFeature (outFeat , QgsFeatureSink .FastInsert )
153155 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.' ))
156157 else :
157158 # Geometry list: prevents writing error
158159 # in geometries of different types
@@ -162,63 +163,64 @@ def processAlgorithm(self, parameters, context, feedback):
162163 try :
163164 outFeat .setGeometry (int_geom )
164165 outFeat .setAttributes (atMapA + atMapB )
165- writer .addFeature (outFeat , QgsFeatureSink .FastInsert )
166+ sink .addFeature (outFeat , QgsFeatureSink .FastInsert )
166167 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.' ))
169169
170- # the remaining bit of inFeatA 's geometry
170+ # the remaining bit of featA 's geometry
171171 # if there is nothing left, this will just silently fail and we're good
172172 diff_geom = QgsGeometry (geom )
173173 if len (lstIntersectingB ) != 0 :
174174 intB = QgsGeometry .unaryUnion (lstIntersectingB )
175175 diff_geom = diff_geom .difference (intB )
176176
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 :
178178 temp_list = diff_geom .asGeometryCollection ()
179179 for i in temp_list :
180180 if i .type () == geom .type ():
181181 diff_geom = QgsGeometry (i )
182182 try :
183183 outFeat .setGeometry (diff_geom )
184184 outFeat .setAttributes (atMapA )
185- writer .addFeature (outFeat , QgsFeatureSink .FastInsert )
185+ sink .addFeature (outFeat , QgsFeatureSink .FastInsert )
186186 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.' ))
189188
190- length = len (vlayerA .fields ())
189+ count += 1
190+ feedback .setProgress (int (count * total ))
191+
192+ length = len (sourceA .fields ())
191193 atMapA = [None ] * length
192194
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+
197199 add = False
198- geom = inFeatA .geometry ()
200+ geom = featA .geometry ()
199201 diff_geom = QgsGeometry (geom )
200202 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 ())
203205
204206 if len (intersects ) < 1 :
205207 try :
206208 outFeat .setGeometry (geom )
207209 outFeat .setAttributes (atMap )
208- writer .addFeature (outFeat , QgsFeatureSink .FastInsert )
210+ sink .addFeature (outFeat , QgsFeatureSink .FastInsert )
209211 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.' ))
212213 else :
213- request = QgsFeatureRequest ().setFilterFids (intersects )
214+ request = QgsFeatureRequest ().setFilterFids (intersects ).setSubsetOfAttributes ([])
215+ request .setDestinationCrs (sourceA .sourceCrs ())
214216
215217 # use prepared geometries for faster intersection tests
216218 engine = QgsGeometry .createGeometryEngine (diff_geom .geometry ())
217219 engine .prepareGeometry ()
218220
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 ()
222224
223225 if engine .intersects (tmpGeom .geometry ()):
224226 add = True
@@ -229,19 +231,19 @@ def processAlgorithm(self, parameters, context, feedback):
229231 # intersects, but the geometry doesn't
230232 outFeat .setGeometry (diff_geom )
231233 outFeat .setAttributes (atMap )
232- writer .addFeature (outFeat , QgsFeatureSink .FastInsert )
234+ sink .addFeature (outFeat , QgsFeatureSink .FastInsert )
233235 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.' ))
236237
237238 if add :
238239 try :
239240 outFeat .setGeometry (diff_geom )
240241 outFeat .setAttributes (atMap )
241- writer .addFeature (outFeat , QgsFeatureSink .FastInsert )
242+ sink .addFeature (outFeat , QgsFeatureSink .FastInsert )
242243 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 ))
246248
247- del writer
249+ return { self . OUTPUT : dest_id }
0 commit comments