mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-18 00:03:05 -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 .SnapGeometries import SnapGeometriesToLayer
|
||||||
from .SpatialiteExecuteSQL import SpatialiteExecuteSQL
|
from .SpatialiteExecuteSQL import SpatialiteExecuteSQL
|
||||||
from .SymmetricalDifference import SymmetricalDifference
|
from .SymmetricalDifference import SymmetricalDifference
|
||||||
|
from .Union import Union
|
||||||
from .VectorSplit import VectorSplit
|
from .VectorSplit import VectorSplit
|
||||||
from .VoronoiPolygons import VoronoiPolygons
|
from .VoronoiPolygons import VoronoiPolygons
|
||||||
from .ZonalStatistics import ZonalStatistics
|
from .ZonalStatistics import ZonalStatistics
|
||||||
@ -101,7 +102,6 @@ from .ZonalStatistics import ZonalStatistics
|
|||||||
# from .RandomSelection import RandomSelection
|
# from .RandomSelection import RandomSelection
|
||||||
# from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets
|
# from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets
|
||||||
# from .SelectByLocation import SelectByLocation
|
# from .SelectByLocation import SelectByLocation
|
||||||
# from .Union import Union
|
|
||||||
# from .SpatialJoin import SpatialJoin
|
# from .SpatialJoin import SpatialJoin
|
||||||
# from .DeleteDuplicateGeometries import DeleteDuplicateGeometries
|
# from .DeleteDuplicateGeometries import DeleteDuplicateGeometries
|
||||||
# from .TextToFloat import TextToFloat
|
# from .TextToFloat import TextToFloat
|
||||||
@ -194,7 +194,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
|
|||||||
# PolygonsToLines(), LinesToPolygons(), ExtractNodes(),
|
# PolygonsToLines(), LinesToPolygons(), ExtractNodes(),
|
||||||
# ConvexHull(), FixedDistanceBuffer(),
|
# ConvexHull(), FixedDistanceBuffer(),
|
||||||
# VariableDistanceBuffer(),
|
# VariableDistanceBuffer(),
|
||||||
# Intersection(), Union(),
|
# Intersection(),
|
||||||
# RandomSelection(), RandomSelectionWithinSubsets(),
|
# RandomSelection(), RandomSelectionWithinSubsets(),
|
||||||
# SelectByLocation(),
|
# SelectByLocation(),
|
||||||
# ExtractByLocation(),
|
# ExtractByLocation(),
|
||||||
@ -274,6 +274,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
|
|||||||
SnapGeometriesToLayer(),
|
SnapGeometriesToLayer(),
|
||||||
SpatialiteExecuteSQL(),
|
SpatialiteExecuteSQL(),
|
||||||
SymmetricalDifference(),
|
SymmetricalDifference(),
|
||||||
|
Union(),
|
||||||
VectorSplit(),
|
VectorSplit(),
|
||||||
VoronoiPolygons(),
|
VoronoiPolygons(),
|
||||||
ZonalStatistics()
|
ZonalStatistics()
|
||||||
|
@ -35,11 +35,11 @@ from qgis.core import (QgsFeatureRequest,
|
|||||||
QgsGeometry,
|
QgsGeometry,
|
||||||
QgsWkbTypes,
|
QgsWkbTypes,
|
||||||
QgsMessageLog,
|
QgsMessageLog,
|
||||||
QgsProcessingUtils)
|
QgsProcessingParameterFeatureSource,
|
||||||
|
QgsProcessingParameterFeatureSink,
|
||||||
|
QgsSpatialIndex)
|
||||||
|
|
||||||
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
|
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
|
||||||
from processing.core.parameters import ParameterVector
|
|
||||||
from processing.core.outputs import OutputVector
|
|
||||||
from processing.tools import vector
|
from processing.tools import vector
|
||||||
|
|
||||||
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
|
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):
|
class Union(QgisAlgorithm):
|
||||||
|
|
||||||
INPUT = 'INPUT'
|
INPUT = 'INPUT'
|
||||||
INPUT2 = 'INPUT2'
|
OVERLAY = 'OVERLAY'
|
||||||
OUTPUT = 'OUTPUT'
|
OUTPUT = 'OUTPUT'
|
||||||
|
|
||||||
def icon(self):
|
def icon(self):
|
||||||
@ -70,11 +70,12 @@ class Union(QgisAlgorithm):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def initAlgorithm(self, config=None):
|
def initAlgorithm(self, config=None):
|
||||||
self.addParameter(ParameterVector(Union.INPUT,
|
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
|
||||||
self.tr('Input layer')))
|
self.tr('Input layer')))
|
||||||
self.addParameter(ParameterVector(Union.INPUT2,
|
self.addParameter(QgsProcessingParameterFeatureSource(self.OVERLAY,
|
||||||
self.tr('Input layer 2')))
|
self.tr('Union layer')))
|
||||||
self.addOutput(OutputVector(Union.OUTPUT, self.tr('Union')))
|
|
||||||
|
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Union')))
|
||||||
|
|
||||||
def name(self):
|
def name(self):
|
||||||
return 'union'
|
return 'union'
|
||||||
@ -83,50 +84,52 @@ class Union(QgisAlgorithm):
|
|||||||
return self.tr('Union')
|
return self.tr('Union')
|
||||||
|
|
||||||
def processAlgorithm(self, parameters, context, feedback):
|
def processAlgorithm(self, parameters, context, feedback):
|
||||||
vlayerA = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(Union.INPUT), context)
|
sourceA = self.parameterAsSource(parameters, self.INPUT, context)
|
||||||
vlayerB = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(Union.INPUT2), context)
|
sourceB = self.parameterAsSource(parameters, self.OVERLAY, context)
|
||||||
|
|
||||||
geomType = vlayerA.wkbType()
|
geomType = QgsWkbTypes.multiType(sourceA.wkbType())
|
||||||
fields = vector.combineFields(vlayerA.fields(), vlayerB.fields())
|
fields = vector.combineFields(sourceA.fields(), sourceB.fields())
|
||||||
writer = self.getOutputFromName(Union.OUTPUT).getVectorWriter(fields, geomType, vlayerA.crs(), context)
|
|
||||||
inFeatA = QgsFeature()
|
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
|
||||||
inFeatB = QgsFeature()
|
fields, geomType, sourceA.sourceCrs())
|
||||||
|
|
||||||
|
featA = QgsFeature()
|
||||||
|
featB = QgsFeature()
|
||||||
outFeat = 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
|
count = 0
|
||||||
nElement = 0
|
|
||||||
featuresA = QgsProcessingUtils.getFeatures(vlayerA, context)
|
for featA in sourceA.getFeatures():
|
||||||
nFeat = QgsProcessingUtils.featureCount(vlayerA, context)
|
if feedback.isCanceled():
|
||||||
for inFeatA in featuresA:
|
break
|
||||||
feedback.setProgress(nElement / float(nFeat) * 50)
|
|
||||||
nElement += 1
|
|
||||||
lstIntersectingB = []
|
lstIntersectingB = []
|
||||||
geom = inFeatA.geometry()
|
geom = featA.geometry()
|
||||||
atMapA = inFeatA.attributes()
|
atMapA = featA.attributes()
|
||||||
intersects = indexA.intersects(geom.boundingBox())
|
intersects = indexB.intersects(geom.boundingBox())
|
||||||
if len(intersects) < 1:
|
if len(intersects) < 1:
|
||||||
try:
|
try:
|
||||||
outFeat.setGeometry(geom)
|
outFeat.setGeometry(geom)
|
||||||
outFeat.setAttributes(atMapA)
|
outFeat.setAttributes(atMapA)
|
||||||
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
||||||
except:
|
except:
|
||||||
# This really shouldn't happen, as we haven't
|
# This really shouldn't happen, as we haven't
|
||||||
# edited the input geom at all
|
# edited the input geom at all
|
||||||
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
|
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
|
||||||
self.tr('Processing'), QgsMessageLog.INFO)
|
|
||||||
else:
|
else:
|
||||||
request = QgsFeatureRequest().setFilterFids(intersects)
|
request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([])
|
||||||
|
request.setDestinationCrs(sourceA.sourceCrs())
|
||||||
|
|
||||||
engine = QgsGeometry.createGeometryEngine(geom.geometry())
|
engine = QgsGeometry.createGeometryEngine(geom.geometry())
|
||||||
engine.prepareGeometry()
|
engine.prepareGeometry()
|
||||||
|
|
||||||
for inFeatB in vlayerB.getFeatures(request):
|
for featB in sourceB.getFeatures(request):
|
||||||
count += 1
|
atMapB = featB.attributes()
|
||||||
|
tmpGeom = featB.geometry()
|
||||||
atMapB = inFeatB.attributes()
|
|
||||||
tmpGeom = inFeatB.geometry()
|
|
||||||
|
|
||||||
if engine.intersects(tmpGeom.geometry()):
|
if engine.intersects(tmpGeom.geometry()):
|
||||||
int_geom = geom.intersection(tmpGeom)
|
int_geom = geom.intersection(tmpGeom)
|
||||||
@ -134,8 +137,7 @@ class Union(QgisAlgorithm):
|
|||||||
|
|
||||||
if not int_geom:
|
if not int_geom:
|
||||||
# There was a problem creating the intersection
|
# There was a problem creating the intersection
|
||||||
QgsMessageLog.logMessage(self.tr('GEOS geoprocessing error: One or more input features have invalid geometry.'),
|
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
|
||||||
self.tr('Processing'), QgsMessageLog.INFO)
|
|
||||||
int_geom = QgsGeometry()
|
int_geom = QgsGeometry()
|
||||||
else:
|
else:
|
||||||
int_geom = QgsGeometry(int_geom)
|
int_geom = QgsGeometry(int_geom)
|
||||||
@ -149,10 +151,9 @@ class Union(QgisAlgorithm):
|
|||||||
try:
|
try:
|
||||||
outFeat.setGeometry(int_geom)
|
outFeat.setGeometry(int_geom)
|
||||||
outFeat.setAttributes(atMapA + atMapB)
|
outFeat.setAttributes(atMapA + atMapB)
|
||||||
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
||||||
except:
|
except:
|
||||||
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
|
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
|
||||||
self.tr('Processing'), QgsMessageLog.INFO)
|
|
||||||
else:
|
else:
|
||||||
# Geometry list: prevents writing error
|
# Geometry list: prevents writing error
|
||||||
# in geometries of different types
|
# in geometries of different types
|
||||||
@ -162,19 +163,18 @@ class Union(QgisAlgorithm):
|
|||||||
try:
|
try:
|
||||||
outFeat.setGeometry(int_geom)
|
outFeat.setGeometry(int_geom)
|
||||||
outFeat.setAttributes(atMapA + atMapB)
|
outFeat.setAttributes(atMapA + atMapB)
|
||||||
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
||||||
except:
|
except:
|
||||||
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
|
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
|
||||||
self.tr('Processing'), QgsMessageLog.INFO)
|
|
||||||
|
|
||||||
# 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
|
# if there is nothing left, this will just silently fail and we're good
|
||||||
diff_geom = QgsGeometry(geom)
|
diff_geom = QgsGeometry(geom)
|
||||||
if len(lstIntersectingB) != 0:
|
if len(lstIntersectingB) != 0:
|
||||||
intB = QgsGeometry.unaryUnion(lstIntersectingB)
|
intB = QgsGeometry.unaryUnion(lstIntersectingB)
|
||||||
diff_geom = diff_geom.difference(intB)
|
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()
|
temp_list = diff_geom.asGeometryCollection()
|
||||||
for i in temp_list:
|
for i in temp_list:
|
||||||
if i.type() == geom.type():
|
if i.type() == geom.type():
|
||||||
@ -182,43 +182,45 @@ class Union(QgisAlgorithm):
|
|||||||
try:
|
try:
|
||||||
outFeat.setGeometry(diff_geom)
|
outFeat.setGeometry(diff_geom)
|
||||||
outFeat.setAttributes(atMapA)
|
outFeat.setAttributes(atMapA)
|
||||||
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
||||||
except:
|
except:
|
||||||
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
|
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
|
||||||
self.tr('Processing'), QgsMessageLog.INFO)
|
|
||||||
|
|
||||||
length = len(vlayerA.fields())
|
count += 1
|
||||||
|
feedback.setProgress(int(count * total))
|
||||||
|
|
||||||
|
length = len(sourceA.fields())
|
||||||
atMapA = [None] * length
|
atMapA = [None] * length
|
||||||
|
|
||||||
featuresA = QgsProcessingUtils.getFeatures(vlayerB, context)
|
for featA in sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs(sourceA.sourceCrs())):
|
||||||
nFeat = QgsProcessingUtils.featureCount(vlayerB, context)
|
if feedback.isCanceled():
|
||||||
for inFeatA in featuresA:
|
break
|
||||||
feedback.setProgress(nElement / float(nFeat) * 100)
|
|
||||||
add = False
|
add = False
|
||||||
geom = inFeatA.geometry()
|
geom = featA.geometry()
|
||||||
diff_geom = QgsGeometry(geom)
|
diff_geom = QgsGeometry(geom)
|
||||||
atMap = [None] * length
|
atMap = [None] * length
|
||||||
atMap.extend(inFeatA.attributes())
|
atMap.extend(featA.attributes())
|
||||||
intersects = indexB.intersects(geom.boundingBox())
|
intersects = indexA.intersects(geom.boundingBox())
|
||||||
|
|
||||||
if len(intersects) < 1:
|
if len(intersects) < 1:
|
||||||
try:
|
try:
|
||||||
outFeat.setGeometry(geom)
|
outFeat.setGeometry(geom)
|
||||||
outFeat.setAttributes(atMap)
|
outFeat.setAttributes(atMap)
|
||||||
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
||||||
except:
|
except:
|
||||||
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
|
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
|
||||||
self.tr('Processing'), QgsMessageLog.INFO)
|
|
||||||
else:
|
else:
|
||||||
request = QgsFeatureRequest().setFilterFids(intersects)
|
request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([])
|
||||||
|
request.setDestinationCrs(sourceA.sourceCrs())
|
||||||
|
|
||||||
# use prepared geometries for faster intersection tests
|
# use prepared geometries for faster intersection tests
|
||||||
engine = QgsGeometry.createGeometryEngine(diff_geom.geometry())
|
engine = QgsGeometry.createGeometryEngine(diff_geom.geometry())
|
||||||
engine.prepareGeometry()
|
engine.prepareGeometry()
|
||||||
|
|
||||||
for inFeatB in vlayerA.getFeatures(request):
|
for featB in sourceA.getFeatures(request):
|
||||||
atMapB = inFeatB.attributes()
|
atMapB = featB.attributes()
|
||||||
tmpGeom = inFeatB.geometry()
|
tmpGeom = featB.geometry()
|
||||||
|
|
||||||
if engine.intersects(tmpGeom.geometry()):
|
if engine.intersects(tmpGeom.geometry()):
|
||||||
add = True
|
add = True
|
||||||
@ -229,19 +231,19 @@ class Union(QgisAlgorithm):
|
|||||||
# intersects, but the geometry doesn't
|
# intersects, but the geometry doesn't
|
||||||
outFeat.setGeometry(diff_geom)
|
outFeat.setGeometry(diff_geom)
|
||||||
outFeat.setAttributes(atMap)
|
outFeat.setAttributes(atMap)
|
||||||
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
||||||
except:
|
except:
|
||||||
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
|
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
|
||||||
self.tr('Processing'), QgsMessageLog.INFO)
|
|
||||||
|
|
||||||
if add:
|
if add:
|
||||||
try:
|
try:
|
||||||
outFeat.setGeometry(diff_geom)
|
outFeat.setGeometry(diff_geom)
|
||||||
outFeat.setAttributes(atMap)
|
outFeat.setAttributes(atMap)
|
||||||
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
||||||
except:
|
except:
|
||||||
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
|
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
|
||||||
self.tr('Processing'), QgsMessageLog.INFO)
|
|
||||||
nElement += 1
|
|
||||||
|
|
||||||
del writer
|
count += 1
|
||||||
|
feedback.setProgress(int(count * total))
|
||||||
|
|
||||||
|
return {self.OUTPUT: dest_id}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user