mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
[processing] restore union algorithm
This commit is contained in:
parent
846abe7104
commit
b46373b084
@ -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()
|
||||
|
@ -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}
|
||||
|
Loading…
x
Reference in New Issue
Block a user