From d3ad9131f46a36ec5c62b93733cab90876dfd7e1 Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Thu, 13 Jul 2017 11:07:01 +0700 Subject: [PATCH] [processing] restore intersection algorithm (#4849) --- .../processing/algs/qgis/Intersection.py | 70 ++++++++++++------- .../algs/qgis/QGISAlgorithmProvider.py | 4 +- .../tests/testdata/qgis_algorithm_tests.yaml | 28 ++++---- 3 files changed, 59 insertions(+), 43 deletions(-) diff --git a/python/plugins/processing/algs/qgis/Intersection.py b/python/plugins/processing/algs/qgis/Intersection.py index e66ce9d2d2f2..411c454e0e0c 100644 --- a/python/plugins/processing/algs/qgis/Intersection.py +++ b/python/plugins/processing/algs/qgis/Intersection.py @@ -34,13 +34,13 @@ QgsFeatureSink, QgsGeometry, QgsWkbTypes, - QgsMessageLog, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterFeatureSink, + QgsSpatialIndex, QgsProcessingUtils) from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException 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] @@ -58,7 +58,7 @@ class Intersection(QgisAlgorithm): INPUT = 'INPUT' - INPUT2 = 'INPUT2' + OVERLAY = 'OVERLAY' OUTPUT = 'OUTPUT' def icon(self): @@ -71,11 +71,12 @@ def __init__(self): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterVector(self.INPUT, - self.tr('Input layer'))) - self.addParameter(ParameterVector(self.INPUT2, - self.tr('Intersect layer'))) - self.addOutput(OutputVector(self.OUTPUT, self.tr('Intersection'))) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'))) + self.addParameter(QgsProcessingParameterFeatureSource(self.OVERLAY, + self.tr('Intersection layer'))) + + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Intersection'))) def name(self): return 'intersection' @@ -84,22 +85,31 @@ def displayName(self): return self.tr('Intersection') def processAlgorithm(self, parameters, context, feedback): - vlayerA = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) - vlayerB = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT2), context) + sourceA = self.parameterAsSource(parameters, self.INPUT, context) + sourceB = self.parameterAsSource(parameters, self.OVERLAY, context) + + 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()) - geomType = QgsWkbTypes.multiType(vlayerA.wkbType()) - fields = vector.combineVectorFields(vlayerA, vlayerB) - writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, geomType, vlayerA.crs(), context) outFeat = QgsFeature() - index = QgsProcessingUtils.createSpatialIndex(vlayerB, context) - selectionA = QgsProcessingUtils.getFeatures(vlayerA, context) - total = 100.0 / vlayerA.featureCount() if vlayerA.featureCount() else 0 - for current, inFeatA in enumerate(selectionA): - feedback.setProgress(int(current * total)) - geom = inFeatA.geometry() - atMapA = inFeatA.attributes() - intersects = index.intersects(geom.boundingBox()) + indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs()))) + + total = 100.0 / sourceA.featureCount() if sourceA.featureCount() else 1 + count = 0 + + for featA in sourceA.getFeatures(): + if feedback.isCanceled(): + break + + geom = featA.geometry() + atMapA = featA.attributes() + intersects = indexB.intersects(geom.boundingBox()) + request = QgsFeatureRequest().setFilterFids(intersects) + request.setDestinationCrs(sourceA.sourceCrs()) engine = None if len(intersects) > 0: @@ -107,10 +117,13 @@ def processAlgorithm(self, parameters, context, feedback): engine = QgsGeometry.createGeometryEngine(geom.geometry()) engine.prepareGeometry() - for inFeatB in vlayerB.getFeatures(request): - tmpGeom = inFeatB.geometry() + for featB in sourceB.getFeatures(request): + if feedback.isCanceled(): + break + + tmpGeom = featB.geometry() if engine.intersects(tmpGeom.geometry()): - atMapB = inFeatB.attributes() + atMapB = featB.attributes() int_geom = QgsGeometry(geom.intersection(tmpGeom)) if int_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(int_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection: int_com = geom.combine(tmpGeom) @@ -130,11 +143,14 @@ def processAlgorithm(self, parameters, context, feedback): attrs.extend(atMapA) attrs.extend(atMapB) outFeat.setAttributes(attrs) - writer.addFeature(outFeat, QgsFeatureSink.FastInsert) + sink.addFeature(outFeat, QgsFeatureSink.FastInsert) except: raise GeoAlgorithmExecutionException( 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} diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 4e2ebd4b1dde..e002aac0d6d3 100755 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -61,6 +61,7 @@ from .GridPolygon import GridPolygon from .ImportIntoPostGIS import ImportIntoPostGIS from .ImportIntoSpatialite import ImportIntoSpatialite +from .Intersection import Intersection from .Merge import Merge from .PointsLayerFromTable import PointsLayerFromTable from .PostGISExecuteSQL import PostGISExecuteSQL @@ -98,7 +99,6 @@ # from .ConvexHull import ConvexHull # from .FixedDistanceBuffer import FixedDistanceBuffer # from .VariableDistanceBuffer import VariableDistanceBuffer -# from .Intersection import Intersection # from .RandomSelection import RandomSelection # from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets # from .SelectByLocation import SelectByLocation @@ -194,7 +194,6 @@ def getAlgs(self): # PolygonsToLines(), LinesToPolygons(), ExtractNodes(), # ConvexHull(), FixedDistanceBuffer(), # VariableDistanceBuffer(), - # Intersection(), # RandomSelection(), RandomSelectionWithinSubsets(), # SelectByLocation(), # ExtractByLocation(), @@ -260,6 +259,7 @@ def getAlgs(self): GridPolygon(), ImportIntoPostGIS(), ImportIntoSpatialite(), + Intersection(), Merge(), PointsLayerFromTable(), PostGISExecuteSQL(), diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 8b808ff7b0bd..5ccf277a8416 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -115,20 +115,20 @@ tests: # # check this behavior. # # This test should stay in place because for shapefiles there should always # # be a polygon result created since it does not support geometry collections. -# - name: Intersection (Collection Fallback) -# algorithm: qgis:intersection -# params: -# INPUT: -# name: multipolys.gml -# type: vector -# INPUT2: -# name: polys.gml -# type: vector -# results: -# OUTPUT: -# name: expected/intersection_collection_fallback.shp -# type: vector -# + - algorithm: qgis:intersection + name: Intersects multipolygons with polygons + params: + INPUT: + name: multipolys.gml + type: vector + OVERLAY: + name: polys.gml + type: vector + results: + OUTPUT: + name: expected/intersection_collection_fallback.shp + type: vector + - name: Densify geometries algorithm: qgis:densifygeometries params: