Skip to content

Commit b46373b

Browse files
authoredJul 12, 2017
[processing] restore union algorithm
1 parent 846abe7 commit b46373b

File tree

2 files changed

+78
-75
lines changed

2 files changed

+78
-75
lines changed
 

‎python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
from .SnapGeometries import SnapGeometriesToLayer
7676
from .SpatialiteExecuteSQL import SpatialiteExecuteSQL
7777
from .SymmetricalDifference import SymmetricalDifference
78+
from .Union import Union
7879
from .VectorSplit import VectorSplit
7980
from .VoronoiPolygons import VoronoiPolygons
8081
from .ZonalStatistics import ZonalStatistics
@@ -101,7 +102,6 @@
101102
# from .RandomSelection import RandomSelection
102103
# from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets
103104
# from .SelectByLocation import SelectByLocation
104-
# from .Union import Union
105105
# from .SpatialJoin import SpatialJoin
106106
# from .DeleteDuplicateGeometries import DeleteDuplicateGeometries
107107
# from .TextToFloat import TextToFloat
@@ -194,7 +194,7 @@ def getAlgs(self):
194194
# PolygonsToLines(), LinesToPolygons(), ExtractNodes(),
195195
# ConvexHull(), FixedDistanceBuffer(),
196196
# VariableDistanceBuffer(),
197-
# Intersection(), Union(),
197+
# Intersection(),
198198
# RandomSelection(), RandomSelectionWithinSubsets(),
199199
# SelectByLocation(),
200200
# ExtractByLocation(),
@@ -274,6 +274,7 @@ def getAlgs(self):
274274
SnapGeometriesToLayer(),
275275
SpatialiteExecuteSQL(),
276276
SymmetricalDifference(),
277+
Union(),
277278
VectorSplit(),
278279
VoronoiPolygons(),
279280
ZonalStatistics()

‎python/plugins/processing/algs/qgis/Union.py

Lines changed: 75 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@
3535
QgsGeometry,
3636
QgsWkbTypes,
3737
QgsMessageLog,
38-
QgsProcessingUtils)
38+
QgsProcessingParameterFeatureSource,
39+
QgsProcessingParameterFeatureSink,
40+
QgsSpatialIndex)
3941

4042
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
41-
from processing.core.parameters import ParameterVector
42-
from processing.core.outputs import OutputVector
4343
from processing.tools import vector
4444

4545
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
@@ -57,7 +57,7 @@
5757
class 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

Comments
 (0)
Please sign in to comment.