From 7132faa97470866baf09759bb17a2ecc005c5680 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 4 Aug 2017 00:03:41 +1000 Subject: [PATCH] Port Topocolor algorithm to new API --- .../algs/qgis/QGISAlgorithmProvider.py | 5 +- .../processing/algs/qgis/TopoColors.py | 73 +++++++++++-------- .../testdata/expected/topocolor_polys.gml | 4 +- .../tests/testdata/qgis_algorithm_tests.yaml | 53 +++++++------- 4 files changed, 76 insertions(+), 59 deletions(-) diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index c9f11fabbd2..e1e1cc7e47a 100644 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -133,6 +133,7 @@ from .SplitWithLines import SplitWithLines from .SumLines import SumLines from .SymmetricalDifference import SymmetricalDifference from .TextToFloat import TextToFloat +from .TopoColors import TopoColor from .Translate import Translate from .TruncateTable import TruncateTable from .Union import Union @@ -169,7 +170,6 @@ from .ZonalStatistics import ZonalStatistics # from .RasterCalculator import RasterCalculator # from .ExecuteSQL import ExecuteSQL # from .FindProjection import FindProjection -# from .TopoColors import TopoColor # from .EliminateSelection import EliminateSelection pluginPath = os.path.normpath(os.path.join( @@ -206,7 +206,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # IdwInterpolation(), TinInterpolation(), # RasterCalculator(), # ExecuteSQL(), FindProjection(), - # TopoColor(), EliminateSelection() + # EliminateSelection() # ] algs = [AddTableField(), Aspect(), @@ -301,6 +301,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): SumLines(), SymmetricalDifference(), TextToFloat(), + TopoColor(), Translate(), TruncateTable(), Union(), diff --git a/python/plugins/processing/algs/qgis/TopoColors.py b/python/plugins/processing/algs/qgis/TopoColors.py index b50625b4e75..33c078cce05 100644 --- a/python/plugins/processing/algs/qgis/TopoColors.py +++ b/python/plugins/processing/algs/qgis/TopoColors.py @@ -31,33 +31,31 @@ import sys from collections import defaultdict -from qgis.core import (QgsApplication, - QgsField, +from qgis.core import (QgsField, QgsFeatureSink, QgsGeometry, QgsSpatialIndex, QgsPointXY, NULL, - QgsProcessingUtils) + QgsProcessing, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterNumber, + QgsProcessingParameterEnum, + QgsProcessingParameterFeatureSink) from qgis.PyQt.QtCore import (QVariant) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import (ParameterVector, - ParameterSelection, - ParameterNumber) -from processing.core.outputs import OutputVector -from processing.tools import dataobjects pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] class TopoColor(QgisAlgorithm): - INPUT_LAYER = 'INPUT_LAYER' + INPUT = 'INPUT' MIN_COLORS = 'MIN_COLORS' MIN_DISTANCE = 'MIN_DISTANCE' BALANCE = 'BALANCE' - OUTPUT_LAYER = 'OUTPUT_LAYER' + OUTPUT = 'OUTPUT' def tags(self): return self.tr('topocolor,colors,graph,adjacent,assign').split(',') @@ -69,21 +67,23 @@ class TopoColor(QgisAlgorithm): super().__init__() def initAlgorithm(self, config=None): - self.addParameter(ParameterVector(self.INPUT_LAYER, - self.tr('Input layer'), [dataobjects.TYPE_VECTOR_POLYGON])) - self.addParameter(ParameterNumber(self.MIN_COLORS, - self.tr('Minimum number of colors'), 1, 1000, 4)) - self.addParameter(ParameterNumber(self.MIN_DISTANCE, - self.tr('Minimum distance between features'), 0.0, 999999999.0, 0.0)) + + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'), [QgsProcessing.TypeVectorPolygon])) + self.addParameter(QgsProcessingParameterNumber(self.MIN_COLORS, + self.tr('Minimum number of colors'), minValue=1, maxValue=1000, defaultValue=4)) + self.addParameter(QgsProcessingParameterNumber(self.MIN_DISTANCE, + self.tr('Minimum distance between features'), type=QgsProcessingParameterNumber.Double, + minValue=0.0, maxValue=999999999.0, defaultValue=0.0)) balance_by = [self.tr('By feature count'), self.tr('By assigned area'), self.tr('By distance between colors')] - self.addParameter(ParameterSelection( + self.addParameter(QgsProcessingParameterEnum( self.BALANCE, self.tr('Balance color assignment'), - balance_by, default=0)) + options=balance_by, defaultValue=0)) - self.addOutput(OutputVector(self.OUTPUT_LAYER, self.tr('Colored'), datatype=[dataobjects.TYPE_VECTOR_POLYGON])) + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Colored'), QgsProcessing.TypeVectorPolygon)) def name(self): return 'topologicalcoloring' @@ -92,18 +92,18 @@ class TopoColor(QgisAlgorithm): return self.tr('Topological coloring') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) - min_colors = self.getParameterValue(self.MIN_COLORS) - balance_by = self.getParameterValue(self.BALANCE) - min_distance = self.getParameterValue(self.MIN_DISTANCE) + source = self.parameterAsSource(parameters, self.INPUT, context) + min_colors = self.parameterAsInt(parameters, self.MIN_COLORS, context) + balance_by = self.parameterAsEnum(parameters, self.BALANCE, context) + min_distance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context) - fields = layer.fields() + fields = source.fields() fields.append(QgsField('color_id', QVariant.Int)) - writer = self.getOutputFromName( - self.OUTPUT_LAYER).getVectorWriter(fields, layer.wkbType(), layer.crs(), context) + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + fields, source.wkbType(), source.sourceCrs()) - features = {f.id(): f for f in QgsProcessingUtils.getFeatures(layer, context)} + features = {f.id(): f for f in source.getFeatures()} topology, id_graph = self.compute_graph(features, feedback, min_distance=min_distance) feature_colors = ColoringAlgorithm.balanced(features, @@ -118,6 +118,9 @@ class TopoColor(QgisAlgorithm): total = 20.0 / len(features) current = 0 for feature_id, input_feature in features.items(): + if feedback.isCanceled(): + break + output_feature = input_feature attributes = input_feature.attributes() if feature_id in feature_colors: @@ -126,11 +129,11 @@ class TopoColor(QgisAlgorithm): attributes.append(NULL) output_feature.setAttributes(attributes) - writer.addFeature(output_feature, QgsFeatureSink.FastInsert) + sink.addFeature(output_feature, QgsFeatureSink.FastInsert) current += 1 feedback.setProgress(80 + int(current * total)) - del writer + return {self.OUTPUT: dest_id} @staticmethod def compute_graph(features, feedback, create_id_graph=False, min_distance=0): @@ -148,6 +151,9 @@ class TopoColor(QgisAlgorithm): i = 0 for feature_id, f in features_with_geometry.items(): + if feedback.isCanceled(): + break + g = f.geometry() if min_distance > 0: g = g.buffer(min_distance, 5) @@ -172,6 +178,9 @@ class TopoColor(QgisAlgorithm): feedback.setProgress(int(i * total)) for feature_id, f in features_with_geometry.items(): + if feedback.isCanceled(): + break + if feature_id not in s.node_edge: s.add_edge(feature_id, None) @@ -206,6 +215,9 @@ class ColoringAlgorithm: i = 0 for (feature_id, n) in sorted_by_count: + if feedback.isCanceled(): + break + # first work out which already assigned colors are adjacent to this feature adjacent_colors = set() for neighbour in graph.node_edge[feature_id]: @@ -240,6 +252,9 @@ class ColoringAlgorithm: # loop through these, and calculate the minimum distance from this feature to the nearest # feature with each assigned color for other_feature_id, c in other_features.items(): + if feedback.isCanceled(): + break + other_geometry = features[other_feature_id].geometry() other_centroid = QgsPointXY(other_geometry.centroid().geometry()) diff --git a/python/plugins/processing/tests/testdata/expected/topocolor_polys.gml b/python/plugins/processing/tests/testdata/expected/topocolor_polys.gml index 09ad337285b..c16afe08ef6 100644 --- a/python/plugins/processing/tests/testdata/expected/topocolor_polys.gml +++ b/python/plugins/processing/tests/testdata/expected/topocolor_polys.gml @@ -41,7 +41,7 @@ 8.23935 -3.11331 11 - 4 + 5 @@ -52,7 +52,7 @@ 8.23935 -6.11331 12 - 5 + 4 diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index a0adf00e432..aa182b10f96 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -2598,32 +2598,33 @@ tests: name: expected/polygon_from_extent.gml type: vector -# - algorithm: qgis:topologicalcoloring -# name: Topological coloring -# params: -# INPUT_LAYER: -# name: custom/adjacent_polys.gml -# type: vector -# MIN_COLORS: 4 -# results: -# OUTPUT_LAYER: -# name: expected/topocolor_polys.gml -# type: vector -# -# - algorithm: qgis:topologicalcoloring -# name: Topological coloring w/ min distance -# params: -# BALANCE: '0' -# INPUT_LAYER: -# name: custom/adjacent_polys.gml -# type: vector -# MIN_COLORS: 4 -# MIN_DISTANCE: 4.0 -# results: -# OUTPUT_LAYER: -# name: expected/topocolor_polys_min_dist.gml -# type: vector -# + - algorithm: qgis:topologicalcoloring + name: Topological coloring + params: + BALANCE: 0 + INPUT: + name: custom/adjacent_polys.gml + type: vector + MIN_COLORS: 4 + results: + OUTPUT: + name: expected/topocolor_polys.gml + type: vector + + - algorithm: qgis:topologicalcoloring + name: Topological coloring w/ min distance + params: + BALANCE: 0 + INPUT: + name: custom/adjacent_polys.gml + type: vector + MIN_COLORS: 4 + MIN_DISTANCE: 4.0 + results: + OUTPUT: + name: expected/topocolor_polys_min_dist.gml + type: vector + - algorithm: qgis:regularpoints name: Regular point with standard extent params: