[processing] restore union algorithm

This commit is contained in:
Mathieu Pellerin 2017-07-12 20:54:51 +07:00 committed by GitHub
parent 846abe7104
commit b46373b084
2 changed files with 77 additions and 74 deletions

View File

@ -75,6 +75,7 @@ from .Smooth import Smooth
from .SnapGeometries import SnapGeometriesToLayer
from .SpatialiteExecuteSQL import SpatialiteExecuteSQL
from .SymmetricalDifference import SymmetricalDifference
from .Union import Union
from .VectorSplit import VectorSplit
from .VoronoiPolygons import VoronoiPolygons
from .ZonalStatistics import ZonalStatistics
@ -101,7 +102,6 @@ from .ZonalStatistics import ZonalStatistics
# from .RandomSelection import RandomSelection
# from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets
# from .SelectByLocation import SelectByLocation
# from .Union import Union
# from .SpatialJoin import SpatialJoin
# from .DeleteDuplicateGeometries import DeleteDuplicateGeometries
# from .TextToFloat import TextToFloat
@ -194,7 +194,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
# PolygonsToLines(), LinesToPolygons(), ExtractNodes(),
# ConvexHull(), FixedDistanceBuffer(),
# VariableDistanceBuffer(),
# Intersection(), Union(),
# Intersection(),
# RandomSelection(), RandomSelectionWithinSubsets(),
# SelectByLocation(),
# ExtractByLocation(),
@ -274,6 +274,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
SnapGeometriesToLayer(),
SpatialiteExecuteSQL(),
SymmetricalDifference(),
Union(),
VectorSplit(),
VoronoiPolygons(),
ZonalStatistics()

View File

@ -35,11 +35,11 @@ from qgis.core import (QgsFeatureRequest,
QgsGeometry,
QgsWkbTypes,
QgsMessageLog,
QgsProcessingUtils)
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
QgsSpatialIndex)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import ParameterVector
from processing.core.outputs import OutputVector
from processing.tools import vector
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
@ -57,7 +57,7 @@ for key, value in list(wkbTypeGroups.items()):
class Union(QgisAlgorithm):
INPUT = 'INPUT'
INPUT2 = 'INPUT2'
OVERLAY = 'OVERLAY'
OUTPUT = 'OUTPUT'
def icon(self):
@ -70,11 +70,12 @@ class Union(QgisAlgorithm):
super().__init__()
def initAlgorithm(self, config=None):
self.addParameter(ParameterVector(Union.INPUT,
self.tr('Input layer')))
self.addParameter(ParameterVector(Union.INPUT2,
self.tr('Input layer 2')))
self.addOutput(OutputVector(Union.OUTPUT, self.tr('Union')))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
self.tr('Input layer')))
self.addParameter(QgsProcessingParameterFeatureSource(self.OVERLAY,
self.tr('Union layer')))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Union')))
def name(self):
return 'union'
@ -83,50 +84,52 @@ class Union(QgisAlgorithm):
return self.tr('Union')
def processAlgorithm(self, parameters, context, feedback):
vlayerA = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(Union.INPUT), context)
vlayerB = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(Union.INPUT2), context)
sourceA = self.parameterAsSource(parameters, self.INPUT, context)
sourceB = self.parameterAsSource(parameters, self.OVERLAY, context)
geomType = vlayerA.wkbType()
fields = vector.combineFields(vlayerA.fields(), vlayerB.fields())
writer = self.getOutputFromName(Union.OUTPUT).getVectorWriter(fields, geomType, vlayerA.crs(), context)
inFeatA = QgsFeature()
inFeatB = QgsFeature()
geomType = QgsWkbTypes.multiType(sourceA.wkbType())
fields = vector.combineFields(sourceA.fields(), sourceB.fields())
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, geomType, sourceA.sourceCrs())
featA = QgsFeature()
featB = QgsFeature()
outFeat = QgsFeature()
indexA = QgsProcessingUtils.createSpatialIndex(vlayerB, context)
indexB = QgsProcessingUtils.createSpatialIndex(vlayerA, context)
indexA = QgsSpatialIndex(sourceA)
indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs())))
total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount() and sourceB.featureCount() else 1
count = 0
nElement = 0
featuresA = QgsProcessingUtils.getFeatures(vlayerA, context)
nFeat = QgsProcessingUtils.featureCount(vlayerA, context)
for inFeatA in featuresA:
feedback.setProgress(nElement / float(nFeat) * 50)
nElement += 1
for featA in sourceA.getFeatures():
if feedback.isCanceled():
break
lstIntersectingB = []
geom = inFeatA.geometry()
atMapA = inFeatA.attributes()
intersects = indexA.intersects(geom.boundingBox())
geom = featA.geometry()
atMapA = featA.attributes()
intersects = indexB.intersects(geom.boundingBox())
if len(intersects) < 1:
try:
outFeat.setGeometry(geom)
outFeat.setAttributes(atMapA)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
# This really shouldn't happen, as we haven't
# edited the input geom at all
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
else:
request = QgsFeatureRequest().setFilterFids(intersects)
request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([])
request.setDestinationCrs(sourceA.sourceCrs())
engine = QgsGeometry.createGeometryEngine(geom.geometry())
engine.prepareGeometry()
for inFeatB in vlayerB.getFeatures(request):
count += 1
atMapB = inFeatB.attributes()
tmpGeom = inFeatB.geometry()
for featB in sourceB.getFeatures(request):
atMapB = featB.attributes()
tmpGeom = featB.geometry()
if engine.intersects(tmpGeom.geometry()):
int_geom = geom.intersection(tmpGeom)
@ -134,8 +137,7 @@ class Union(QgisAlgorithm):
if not int_geom:
# There was a problem creating the intersection
QgsMessageLog.logMessage(self.tr('GEOS geoprocessing error: One or more input features have invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
int_geom = QgsGeometry()
else:
int_geom = QgsGeometry(int_geom)
@ -149,10 +151,9 @@ class Union(QgisAlgorithm):
try:
outFeat.setGeometry(int_geom)
outFeat.setAttributes(atMapA + atMapB)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
else:
# Geometry list: prevents writing error
# in geometries of different types
@ -162,19 +163,18 @@ class Union(QgisAlgorithm):
try:
outFeat.setGeometry(int_geom)
outFeat.setAttributes(atMapA + atMapB)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
# the remaining bit of inFeatA's geometry
# the remaining bit of featA's geometry
# if there is nothing left, this will just silently fail and we're good
diff_geom = QgsGeometry(geom)
if len(lstIntersectingB) != 0:
intB = QgsGeometry.unaryUnion(lstIntersectingB)
diff_geom = diff_geom.difference(intB)
if diff_geom.wkbType() == 0 or QgsWkbTypes.flatType(diff_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection:
if diff_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(diff_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection:
temp_list = diff_geom.asGeometryCollection()
for i in temp_list:
if i.type() == geom.type():
@ -182,43 +182,45 @@ class Union(QgisAlgorithm):
try:
outFeat.setGeometry(diff_geom)
outFeat.setAttributes(atMapA)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
length = len(vlayerA.fields())
count += 1
feedback.setProgress(int(count * total))
length = len(sourceA.fields())
atMapA = [None] * length
featuresA = QgsProcessingUtils.getFeatures(vlayerB, context)
nFeat = QgsProcessingUtils.featureCount(vlayerB, context)
for inFeatA in featuresA:
feedback.setProgress(nElement / float(nFeat) * 100)
for featA in sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs(sourceA.sourceCrs())):
if feedback.isCanceled():
break
add = False
geom = inFeatA.geometry()
geom = featA.geometry()
diff_geom = QgsGeometry(geom)
atMap = [None] * length
atMap.extend(inFeatA.attributes())
intersects = indexB.intersects(geom.boundingBox())
atMap.extend(featA.attributes())
intersects = indexA.intersects(geom.boundingBox())
if len(intersects) < 1:
try:
outFeat.setGeometry(geom)
outFeat.setAttributes(atMap)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
else:
request = QgsFeatureRequest().setFilterFids(intersects)
request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([])
request.setDestinationCrs(sourceA.sourceCrs())
# use prepared geometries for faster intersection tests
engine = QgsGeometry.createGeometryEngine(diff_geom.geometry())
engine.prepareGeometry()
for inFeatB in vlayerA.getFeatures(request):
atMapB = inFeatB.attributes()
tmpGeom = inFeatB.geometry()
for featB in sourceA.getFeatures(request):
atMapB = featB.attributes()
tmpGeom = featB.geometry()
if engine.intersects(tmpGeom.geometry()):
add = True
@ -229,19 +231,19 @@ class Union(QgisAlgorithm):
# intersects, but the geometry doesn't
outFeat.setGeometry(diff_geom)
outFeat.setAttributes(atMap)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
if add:
try:
outFeat.setGeometry(diff_geom)
outFeat.setAttributes(atMap)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
nElement += 1
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
del writer
count += 1
feedback.setProgress(int(count * total))
return {self.OUTPUT: dest_id}