From 115ede60ce61f902119c8c83811fd2feadf543ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20Str=C3=B6bl?= Date: Thu, 23 Feb 2017 09:33:28 +0100 Subject: [PATCH] [processing] replace alg Eliminate with new alg EliminateSelection * [processing] replace alg Eliminate with new alg EliminateSelection * Deprecate Eliminate algorithm * Expose new EliminateSelection algorithm in GUI * Rename ouput layer * Reflect algorithm changes in help * Remove superfluous init method * Simplify code, thanks Nyall * Improve transfer of selection to processLayer * Remove deprecated Eliminate algorithm * Remove test for Eliminate * Fix indentation --- python/plugins/processing/algs/help/qgis.yaml | 5 +- .../{Eliminate.py => EliminateSelection.py} | 185 +++--------------- .../algs/qgis/QGISAlgorithmProvider.py | 6 +- python/plugins/processing/gui/menus.py | 2 +- .../expected/eliminate_largest_area.gfs | 31 --- .../expected/eliminate_largest_area.gml | 50 ----- .../expected/eliminate_smallest_area.gfs | 31 --- .../expected/eliminate_smallest_area.gml | 50 ----- .../tests/testdata/qgis_algorithm_tests.yaml | 52 ----- 9 files changed, 32 insertions(+), 380 deletions(-) rename python/plugins/processing/algs/qgis/{Eliminate.py => EliminateSelection.py} (50%) delete mode 100644 python/plugins/processing/tests/testdata/expected/eliminate_largest_area.gfs delete mode 100644 python/plugins/processing/tests/testdata/expected/eliminate_largest_area.gml delete mode 100644 python/plugins/processing/tests/testdata/expected/eliminate_smallest_area.gfs delete mode 100644 python/plugins/processing/tests/testdata/expected/eliminate_smallest_area.gml diff --git a/python/plugins/processing/algs/help/qgis.yaml b/python/plugins/processing/algs/help/qgis.yaml index dfeaf5b7c7b..ada45368d4b 100755 --- a/python/plugins/processing/algs/help/qgis.yaml +++ b/python/plugins/processing/algs/help/qgis.yaml @@ -160,8 +160,9 @@ qgis:distancetonearesthub: > qgis:dropgeometries: > This algorithm removes any geometries from an input layer and returns a layer containing only the feature attributes. -qgis:eliminatesliverpolygons: > - This algorithm combines selected polygons of the input layer with certain adjacent polygons by erasing their common boundary. Eliminate can either use an existing selection or a logical query based on one of the layer's fields to make the selection itself. The adjacent polygon can be either the one with the largest or smallest area or the one sharing the largest common boundary with the polygon to be eliminated. Eliminate is normally used to get rid of sliver polygons, i.e. tiny polygons that are a result of polygon intersection processes where boundaries of the inputs are similar but not identical. +qgis:eliminateselectedpolygons: > + This algorithm combines selected polygons of the input layer with certain adjacent polygons by erasing their common boundary. The adjacent polygon can be either the one with the largest or smallest area or the one sharing the largest common boundary with the polygon to be eliminated. The selected features will always be eliminated whether the option "Use only selected features" is set or not. + Eliminate is normally used to get rid of sliver polygons, i.e. tiny polygons that are a result of polygon intersection processes where boundaries of the inputs are similar but not identical. qgis:explodelines: > This algorithm takes a lines layer and creates a new one in which each line is replaced is replaced by a set of lines representing the segments in the original line. Each line in the resulting layer contains only a start and an end point, with no intermediate nodes between them. diff --git a/python/plugins/processing/algs/qgis/Eliminate.py b/python/plugins/processing/algs/qgis/EliminateSelection.py similarity index 50% rename from python/plugins/processing/algs/qgis/Eliminate.py rename to python/plugins/processing/algs/qgis/EliminateSelection.py index 8d698eb94ce..9ec0df877c4 100644 --- a/python/plugins/processing/algs/qgis/Eliminate.py +++ b/python/plugins/processing/algs/qgis/EliminateSelection.py @@ -2,10 +2,10 @@ """ *************************************************************************** - Eliminate.py + EliminateSelection.py --------------------- - Date : August 2012 - Copyright : (C) 2013 by Bernhard Ströbl + Date : January 2017 + Copyright : (C) 2017 by Bernhard Ströbl Email : bernhard.stroebl@jena.de *************************************************************************** * * @@ -20,8 +20,8 @@ from builtins import str from builtins import range __author__ = 'Bernhard Ströbl' -__date__ = 'September 2013' -__copyright__ = '(C) 2013, Bernhard Ströbl' +__date__ = 'January 2017' +__copyright__ = '(C) 2017, Bernhard Ströbl' # This will get replaced with a git SHA1 when you do a git archive @@ -30,7 +30,6 @@ __revision__ = '$Format:%H$' import os from qgis.PyQt.QtGui import QIcon -from qgis.PyQt.QtCore import QLocale, QDate, QVariant from qgis.core import QgsFeatureRequest, QgsFeature, QgsGeometry @@ -38,9 +37,6 @@ from processing.core.GeoAlgorithm import GeoAlgorithm from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException from processing.core.ProcessingLog import ProcessingLog from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterBoolean -from processing.core.parameters import ParameterTableField -from processing.core.parameters import ParameterString from processing.core.parameters import ParameterSelection from processing.core.outputs import OutputVector from processing.tools import dataobjects, vector @@ -48,15 +44,11 @@ from processing.tools import dataobjects, vector pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] -class Eliminate(GeoAlgorithm): +class EliminateSelection(GeoAlgorithm): INPUT = 'INPUT' OUTPUT = 'OUTPUT' MODE = 'MODE' - KEEPSELECTION = 'KEEPSELECTION' - ATTRIBUTE = 'ATTRIBUTE' - COMPARISONVALUE = 'COMPARISONVALUE' - COMPARISON = 'COMPARISON' MODE_LARGEST_AREA = 0 MODE_SMALLEST_AREA = 1 @@ -66,7 +58,7 @@ class Eliminate(GeoAlgorithm): return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'eliminate.png')) def defineCharacteristics(self): - self.name, self.i18n_name = self.trAlgorithm('Eliminate sliver polygons') + self.name, self.i18n_name = self.trAlgorithm('Eliminate selected polygons') self.group, self.i18n_group = self.trAlgorithm('Vector geometry tools') self.modes = [self.tr('Largest area'), @@ -75,153 +67,37 @@ class Eliminate(GeoAlgorithm): self.addParameter(ParameterVector(self.INPUT, self.tr('Input layer'), [dataobjects.TYPE_VECTOR_POLYGON])) - self.addParameter(ParameterBoolean(self.KEEPSELECTION, - self.tr('Use current selection in input layer (works only if called from toolbox)'), False)) - self.addParameter(ParameterTableField(self.ATTRIBUTE, - self.tr('Selection attribute'), self.INPUT)) - self.comparisons = [ - '==', - '!=', - '>', - '>=', - '<', - '<=', - 'begins with', - 'contains', - ] - self.addParameter(ParameterSelection(self.COMPARISON, - self.tr('Comparison'), self.comparisons, default=0)) - self.addParameter(ParameterString(self.COMPARISONVALUE, - self.tr('Value'), default='0')) self.addParameter(ParameterSelection(self.MODE, self.tr('Merge selection with the neighbouring polygon with the'), self.modes)) - self.addOutput(OutputVector(self.OUTPUT, self.tr('Cleaned'), datatype=[dataobjects.TYPE_VECTOR_POLYGON])) + self.addOutput(OutputVector(self.OUTPUT, self.tr('Eliminated'), datatype=[dataobjects.TYPE_VECTOR_POLYGON])) def processAlgorithm(self, feedback): inLayer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT)) boundary = self.getParameterValue(self.MODE) == self.MODE_BOUNDARY smallestArea = self.getParameterValue(self.MODE) == self.MODE_SMALLEST_AREA - keepSelection = self.getParameterValue(self.KEEPSELECTION) - processLayer = vector.duplicateInMemory(inLayer) - if not keepSelection: - # Make a selection with the values provided - attribute = self.getParameterValue(self.ATTRIBUTE) - comparison = self.comparisons[self.getParameterValue(self.COMPARISON)] - comparisonvalue = self.getParameterValue(self.COMPARISONVALUE) - - selectindex = vector.resolveFieldIndex(processLayer, attribute) - selectType = processLayer.fields()[selectindex].type() - selectionError = False - - if selectType in [QVariant.Int, QVariant.LongLong, QVariant.UInt, QVariant.ULongLong]: - try: - y = int(comparisonvalue) - except ValueError: - selectionError = True - msg = self.tr('Cannot convert "%s" to integer' % str(comparisonvalue)) - elif selectType == QVariant.Double: - try: - y = float(comparisonvalue) - except ValueError: - selectionError = True - msg = self.tr('Cannot convert "%s" to float' % str(comparisonvalue)) - elif selectType == QVariant.String: - # 10: string, boolean - try: - y = str(comparisonvalue) - except ValueError: - selectionError = True - msg = self.tr('Cannot convert "%s" to Unicode' % str(comparisonvalue)) - elif selectType == QVariant.Date: - # date - dateAndFormat = comparisonvalue.split(' ') - - if len(dateAndFormat) == 1: - # QDate object - y = QLocale.system().toDate(dateAndFormat[0]) - - if y.isNull(): - msg = self.tr('Cannot convert "%s" to date with system date format %s' % (str(dateAndFormat), QLocale.system().dateFormat())) - elif len(dateAndFormat) == 2: - y = QDate.fromString(dateAndFormat[0], dateAndFormat[1]) - - if y.isNull(): - msg = self.tr('Cannot convert "%s" to date with format string "%s"' % (str(dateAndFormat[0]), dateAndFormat[1])) - else: - y = QDate() - msg = '' - - if y.isNull(): - # Conversion was unsuccessful - selectionError = True - msg += self.tr('Enter the date and the date format, e.g. "07.26.2011" "MM.dd.yyyy".') - - if (comparison == 'begins with' or comparison == 'contains') \ - and selectType != QVariant.String: - selectionError = True - msg = self.tr('"%s" can only be used with string fields' % comparison) - - selected = [] - - if selectionError: - raise GeoAlgorithmExecutionException( - self.tr('Error in selection input: %s' % msg)) - else: - for feature in processLayer.getFeatures(): - aValue = feature.attributes()[selectindex] - - if aValue is None: - continue - - if selectType in [QVariant.Int, QVariant.LongLong, QVariant.UInt, QVariant.ULongLong]: - x = int(aValue) - elif selectType == QVariant.Double: - x = float(aValue) - elif selectType == QVariant.String: - # 10: string, boolean - x = str(aValue) - elif selectType == QVariant.Date: - # date - x = aValue # should be date - - match = False - - if comparison == '==': - match = x == y - elif comparison == '!=': - match = x != y - elif comparison == '>': - match = x > y - elif comparison == '>=': - match = x >= y - elif comparison == '<': - match = x < y - elif comparison == '<=': - match = x <= y - elif comparison == 'begins with': - match = x.startswith(y) - elif comparison == 'contains': - match = x.find(y) >= 0 - - if match: - selected.append(feature.id()) - - processLayer.selectByIds(selected) - - if processLayer.selectedFeatureCount() == 0: + if inLayer.selectedFeatureCount() == 0: ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, self.tr('%s: (No selection in input layer "%s")' % (self.commandLineName(), self.getParameterValue(self.INPUT)))) - # Keep references to the features to eliminate featToEliminate = [] - for aFeat in processLayer.selectedFeatures(): - featToEliminate.append(aFeat) + selFeatIds = inLayer.selectedFeatureIds() + output = self.getOutputFromName(self.OUTPUT) + writer = output.getVectorWriter(inLayer.fields(), + inLayer.wkbType(), inLayer.crs()) - # Delete all features to eliminate in processLayer (we won't save this) + for aFeat in inLayer.getFeatures(): + if aFeat.id() in selFeatIds: + # Keep references to the features to eliminate + featToEliminate.append(aFeat) + else: + # write the others to output + writer.addFeature(aFeat) + + # Delete all features to eliminate in processLayer + processLayer = output.layer processLayer.startEditing() - processLayer.deleteSelectedFeatures() # ANALYZE if len(featToEliminate) > 0: # Prevent zero division @@ -319,19 +195,8 @@ class Eliminate(GeoAlgorithm): featToEliminate = featNotEliminated # End while - - # Create output - output = self.getOutputFromName(self.OUTPUT) - writer = output.getVectorWriter(processLayer.fields(), - processLayer.wkbType(), processLayer.crs()) - - # Write all features that are left over to output layer - iterator = processLayer.getFeatures() - for feature in iterator: - writer.addFeature(feature) - - # Leave processLayer untouched - processLayer.rollBack() + if not processLayer.commitChanges(): + raise GeoAlgorithmExecutionException(self.tr('Could not commit changes')) for feature in featNotEliminated: writer.addFeature(feature) diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index 735b5148d5d..ac0ac97eeec 100755 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -87,7 +87,6 @@ from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets from .SelectByLocation import SelectByLocation from .Union import Union from .DensifyGeometriesInterval import DensifyGeometriesInterval -from .Eliminate import Eliminate from .SpatialJoin import SpatialJoin from .DeleteColumn import DeleteColumn from .DeleteHoles import DeleteHoles @@ -187,6 +186,7 @@ from .FixGeometry import FixGeometry from .ExecuteSQL import ExecuteSQL from .FindProjection import FindProjection from .TopoColors import TopoColor +from .EliminateSelection import EliminateSelection pluginPath = os.path.normpath(os.path.join( os.path.split(os.path.dirname(__file__))[0], os.pardir)) @@ -208,7 +208,7 @@ class QGISAlgorithmProvider(AlgorithmProvider): DensifyGeometries(), DensifyGeometriesInterval(), MultipartToSingleparts(), SinglePartsToMultiparts(), PolygonsToLines(), LinesToPolygons(), ExtractNodes(), - Eliminate(), ConvexHull(), FixedDistanceBuffer(), + ConvexHull(), FixedDistanceBuffer(), VariableDistanceBuffer(), Dissolve(), Difference(), Intersection(), Union(), Clip(), ExtentFromLayer(), RandomSelection(), RandomSelectionWithinSubsets(), @@ -257,7 +257,7 @@ class QGISAlgorithmProvider(AlgorithmProvider): ShortestPathLayerToPoint(), ServiceAreaFromPoint(), ServiceAreaFromLayer(), TruncateTable(), Polygonize(), FixGeometry(), ExecuteSQL(), FindProjection(), - TopoColor() + TopoColor(), EliminateSelection() ] if hasPlotly: diff --git a/python/plugins/processing/gui/menus.py b/python/plugins/processing/gui/menus.py index 79087762224..5edd97c33e0 100644 --- a/python/plugins/processing/gui/menus.py +++ b/python/plugins/processing/gui/menus.py @@ -51,7 +51,7 @@ defaultMenuEntries.update({'qgis:convexhull': geoprocessingToolsMenu, 'qgis:clip': geoprocessingToolsMenu, 'qgis:difference': geoprocessingToolsMenu, 'qgis:dissolve': geoprocessingToolsMenu, - 'qgis:eliminatesliverpolygons': geoprocessingToolsMenu}) + 'qgis:eliminateselectedpolygons': geoprocessingToolsMenu}) geometryToolsMenu = vectorMenu + "/" + Processing.tr('G&eometry Tools') defaultMenuEntries.update({'qgis:checkvalidity': geometryToolsMenu, 'qgis:exportaddgeometrycolumns': geometryToolsMenu, diff --git a/python/plugins/processing/tests/testdata/expected/eliminate_largest_area.gfs b/python/plugins/processing/tests/testdata/expected/eliminate_largest_area.gfs deleted file mode 100644 index 15256d85a45..00000000000 --- a/python/plugins/processing/tests/testdata/expected/eliminate_largest_area.gfs +++ /dev/null @@ -1,31 +0,0 @@ - - - eliminate_largest_area - eliminate_largest_area - 3 - EPSG:4326 - - 5 - -1.00000 - 10.00000 - -3.00000 - 6.00000 - - - name - name - String - 5 - - - intval - intval - Integer - - - floatval - floatval - Real - - - diff --git a/python/plugins/processing/tests/testdata/expected/eliminate_largest_area.gml b/python/plugins/processing/tests/testdata/expected/eliminate_largest_area.gml deleted file mode 100644 index bc50902bb16..00000000000 --- a/python/plugins/processing/tests/testdata/expected/eliminate_largest_area.gml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - -1-3 - 106 - - - - - - -1,-1 -1,3 3,3 3,2 6,1 6,-3 2,-1 -1,-1 - aaaaa - 33 - 44.123456 - - - - - 5,5 6,4 4,4 5,5 - Aaaaa - -33 - 0 - - - - - 2,5 2,6 3,6 3,5 2,5 - bbaaa - 0.123 - - - - - 6,1 10,1 10,-3 6,-3 6,17,0 7,-2 9,-2 9,0 7,0 - ASDF - 0 - - - - - 120 - -100291.43213 - - - diff --git a/python/plugins/processing/tests/testdata/expected/eliminate_smallest_area.gfs b/python/plugins/processing/tests/testdata/expected/eliminate_smallest_area.gfs deleted file mode 100644 index ac1efc8eb9b..00000000000 --- a/python/plugins/processing/tests/testdata/expected/eliminate_smallest_area.gfs +++ /dev/null @@ -1,31 +0,0 @@ - - - eliminate_smallest_area - eliminate_smallest_area - 3 - EPSG:4326 - - 5 - -1.00000 - 10.00000 - -3.00000 - 6.00000 - - - name - name - String - 5 - - - intval - intval - Integer - - - floatval - floatval - Real - - - diff --git a/python/plugins/processing/tests/testdata/expected/eliminate_smallest_area.gml b/python/plugins/processing/tests/testdata/expected/eliminate_smallest_area.gml deleted file mode 100644 index 48eadf1d445..00000000000 --- a/python/plugins/processing/tests/testdata/expected/eliminate_smallest_area.gml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - -1-3 - 106 - - - - - - -1,-1 -1,3 3,3 3,2 2,2 2,-1 -1,-1 - aaaaa - 33 - 44.123456 - - - - - 5,5 6,4 4,4 5,5 - Aaaaa - -33 - 0 - - - - - 2,5 2,6 3,6 3,5 2,5 - bbaaa - 0.123 - - - - - 6,1 10,1 10,-3 6,-3 2,-1 2,2 3,2 6,17,0 7,-2 9,-2 9,0 7,0 - ASDF - 0 - - - - - 120 - -100291.43213 - - - diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index c6213b23c68..a00180b4815 100755 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -252,58 +252,6 @@ tests: name: expected/autoincrement_field.gml type: vector - # Eliminate sliver polygons - # case 1: merge with largest area - - algorithm: qgis:eliminatesliverpolygons - name: Eliminate sliver polygons largest area - params: - ATTRIBUTE: 'fid' - COMPARISON: '0' - COMPARISONVALUE: 'polys.5' - INPUT: - name: polys.gml - type: vector - KEEPSELECTION: 'False' - MODE: '0' - results: - OUTPUT: - name: expected/eliminate_largest_area.gml - type: vector - - # case 2: merge with smallest area - - algorithm: qgis:eliminatesliverpolygons - name: Eliminate sliver polygons smallest area - params: - ATTRIBUTE: 'fid' - COMPARISON: '0' - COMPARISONVALUE: 'polys.5' - INPUT: - name: polys.gml - type: vector - KEEPSELECTION: 'False' - MODE: '1' - results: - OUTPUT: - name: expected/eliminate_smallest_area.gml - type: vector - - # case 3: merge with longest common boundary - - algorithm: qgis:eliminatesliverpolygons - name: Eliminate sliver polygons largest area - params: - ATTRIBUTE: 'fid' - COMPARISON: '0' - COMPARISONVALUE: 'polys.5' - INPUT: - name: polys.gml - type: vector - KEEPSELECTION: 'False' - MODE: '2' - results: - OUTPUT: - name: expected/eliminate_largest_area.gml - type: vector - - algorithm: qgis:dissolve name: Dissolve using field params: