Port Topocolor algorithm to new API

This commit is contained in:
Nyall Dawson 2017-08-04 00:03:41 +10:00
parent ec4df6c019
commit 7132faa974
4 changed files with 76 additions and 59 deletions

View File

@ -133,6 +133,7 @@ from .SplitWithLines import SplitWithLines
from .SumLines import SumLines from .SumLines import SumLines
from .SymmetricalDifference import SymmetricalDifference from .SymmetricalDifference import SymmetricalDifference
from .TextToFloat import TextToFloat from .TextToFloat import TextToFloat
from .TopoColors import TopoColor
from .Translate import Translate from .Translate import Translate
from .TruncateTable import TruncateTable from .TruncateTable import TruncateTable
from .Union import Union from .Union import Union
@ -169,7 +170,6 @@ from .ZonalStatistics import ZonalStatistics
# from .RasterCalculator import RasterCalculator # from .RasterCalculator import RasterCalculator
# from .ExecuteSQL import ExecuteSQL # from .ExecuteSQL import ExecuteSQL
# from .FindProjection import FindProjection # from .FindProjection import FindProjection
# from .TopoColors import TopoColor
# from .EliminateSelection import EliminateSelection # from .EliminateSelection import EliminateSelection
pluginPath = os.path.normpath(os.path.join( pluginPath = os.path.normpath(os.path.join(
@ -206,7 +206,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
# IdwInterpolation(), TinInterpolation(), # IdwInterpolation(), TinInterpolation(),
# RasterCalculator(), # RasterCalculator(),
# ExecuteSQL(), FindProjection(), # ExecuteSQL(), FindProjection(),
# TopoColor(), EliminateSelection() # EliminateSelection()
# ] # ]
algs = [AddTableField(), algs = [AddTableField(),
Aspect(), Aspect(),
@ -301,6 +301,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
SumLines(), SumLines(),
SymmetricalDifference(), SymmetricalDifference(),
TextToFloat(), TextToFloat(),
TopoColor(),
Translate(), Translate(),
TruncateTable(), TruncateTable(),
Union(), Union(),

View File

@ -31,33 +31,31 @@ import sys
from collections import defaultdict from collections import defaultdict
from qgis.core import (QgsApplication, from qgis.core import (QgsField,
QgsField,
QgsFeatureSink, QgsFeatureSink,
QgsGeometry, QgsGeometry,
QgsSpatialIndex, QgsSpatialIndex,
QgsPointXY, QgsPointXY,
NULL, NULL,
QgsProcessingUtils) QgsProcessing,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterNumber,
QgsProcessingParameterEnum,
QgsProcessingParameterFeatureSink)
from qgis.PyQt.QtCore import (QVariant) from qgis.PyQt.QtCore import (QVariant)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm 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] pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
class TopoColor(QgisAlgorithm): class TopoColor(QgisAlgorithm):
INPUT_LAYER = 'INPUT_LAYER' INPUT = 'INPUT'
MIN_COLORS = 'MIN_COLORS' MIN_COLORS = 'MIN_COLORS'
MIN_DISTANCE = 'MIN_DISTANCE' MIN_DISTANCE = 'MIN_DISTANCE'
BALANCE = 'BALANCE' BALANCE = 'BALANCE'
OUTPUT_LAYER = 'OUTPUT_LAYER' OUTPUT = 'OUTPUT'
def tags(self): def tags(self):
return self.tr('topocolor,colors,graph,adjacent,assign').split(',') return self.tr('topocolor,colors,graph,adjacent,assign').split(',')
@ -69,21 +67,23 @@ class TopoColor(QgisAlgorithm):
super().__init__() super().__init__()
def initAlgorithm(self, config=None): def initAlgorithm(self, config=None):
self.addParameter(ParameterVector(self.INPUT_LAYER,
self.tr('Input layer'), [dataobjects.TYPE_VECTOR_POLYGON])) self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
self.addParameter(ParameterNumber(self.MIN_COLORS, self.tr('Input layer'), [QgsProcessing.TypeVectorPolygon]))
self.tr('Minimum number of colors'), 1, 1000, 4)) self.addParameter(QgsProcessingParameterNumber(self.MIN_COLORS,
self.addParameter(ParameterNumber(self.MIN_DISTANCE, self.tr('Minimum number of colors'), minValue=1, maxValue=1000, defaultValue=4))
self.tr('Minimum distance between features'), 0.0, 999999999.0, 0.0)) 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'), balance_by = [self.tr('By feature count'),
self.tr('By assigned area'), self.tr('By assigned area'),
self.tr('By distance between colors')] self.tr('By distance between colors')]
self.addParameter(ParameterSelection( self.addParameter(QgsProcessingParameterEnum(
self.BALANCE, self.BALANCE,
self.tr('Balance color assignment'), 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): def name(self):
return 'topologicalcoloring' return 'topologicalcoloring'
@ -92,18 +92,18 @@ class TopoColor(QgisAlgorithm):
return self.tr('Topological coloring') return self.tr('Topological coloring')
def processAlgorithm(self, parameters, context, feedback): def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) source = self.parameterAsSource(parameters, self.INPUT, context)
min_colors = self.getParameterValue(self.MIN_COLORS) min_colors = self.parameterAsInt(parameters, self.MIN_COLORS, context)
balance_by = self.getParameterValue(self.BALANCE) balance_by = self.parameterAsEnum(parameters, self.BALANCE, context)
min_distance = self.getParameterValue(self.MIN_DISTANCE) min_distance = self.parameterAsDouble(parameters, self.MIN_DISTANCE, context)
fields = layer.fields() fields = source.fields()
fields.append(QgsField('color_id', QVariant.Int)) fields.append(QgsField('color_id', QVariant.Int))
writer = self.getOutputFromName( (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
self.OUTPUT_LAYER).getVectorWriter(fields, layer.wkbType(), layer.crs(), 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) topology, id_graph = self.compute_graph(features, feedback, min_distance=min_distance)
feature_colors = ColoringAlgorithm.balanced(features, feature_colors = ColoringAlgorithm.balanced(features,
@ -118,6 +118,9 @@ class TopoColor(QgisAlgorithm):
total = 20.0 / len(features) total = 20.0 / len(features)
current = 0 current = 0
for feature_id, input_feature in features.items(): for feature_id, input_feature in features.items():
if feedback.isCanceled():
break
output_feature = input_feature output_feature = input_feature
attributes = input_feature.attributes() attributes = input_feature.attributes()
if feature_id in feature_colors: if feature_id in feature_colors:
@ -126,11 +129,11 @@ class TopoColor(QgisAlgorithm):
attributes.append(NULL) attributes.append(NULL)
output_feature.setAttributes(attributes) output_feature.setAttributes(attributes)
writer.addFeature(output_feature, QgsFeatureSink.FastInsert) sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
current += 1 current += 1
feedback.setProgress(80 + int(current * total)) feedback.setProgress(80 + int(current * total))
del writer return {self.OUTPUT: dest_id}
@staticmethod @staticmethod
def compute_graph(features, feedback, create_id_graph=False, min_distance=0): def compute_graph(features, feedback, create_id_graph=False, min_distance=0):
@ -148,6 +151,9 @@ class TopoColor(QgisAlgorithm):
i = 0 i = 0
for feature_id, f in features_with_geometry.items(): for feature_id, f in features_with_geometry.items():
if feedback.isCanceled():
break
g = f.geometry() g = f.geometry()
if min_distance > 0: if min_distance > 0:
g = g.buffer(min_distance, 5) g = g.buffer(min_distance, 5)
@ -172,6 +178,9 @@ class TopoColor(QgisAlgorithm):
feedback.setProgress(int(i * total)) feedback.setProgress(int(i * total))
for feature_id, f in features_with_geometry.items(): for feature_id, f in features_with_geometry.items():
if feedback.isCanceled():
break
if feature_id not in s.node_edge: if feature_id not in s.node_edge:
s.add_edge(feature_id, None) s.add_edge(feature_id, None)
@ -206,6 +215,9 @@ class ColoringAlgorithm:
i = 0 i = 0
for (feature_id, n) in sorted_by_count: for (feature_id, n) in sorted_by_count:
if feedback.isCanceled():
break
# first work out which already assigned colors are adjacent to this feature # first work out which already assigned colors are adjacent to this feature
adjacent_colors = set() adjacent_colors = set()
for neighbour in graph.node_edge[feature_id]: 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 # loop through these, and calculate the minimum distance from this feature to the nearest
# feature with each assigned color # feature with each assigned color
for other_feature_id, c in other_features.items(): for other_feature_id, c in other_features.items():
if feedback.isCanceled():
break
other_geometry = features[other_feature_id].geometry() other_geometry = features[other_feature_id].geometry()
other_centroid = QgsPointXY(other_geometry.centroid().geometry()) other_centroid = QgsPointXY(other_geometry.centroid().geometry())

View File

@ -41,7 +41,7 @@
<ogr:right>8.23935</ogr:right> <ogr:right>8.23935</ogr:right>
<ogr:bottom>-3.11331</ogr:bottom> <ogr:bottom>-3.11331</ogr:bottom>
<ogr:id>11</ogr:id> <ogr:id>11</ogr:id>
<ogr:color_id>4</ogr:color_id> <ogr:color_id>5</ogr:color_id>
</ogr:topocolor_polys> </ogr:topocolor_polys>
</gml:featureMember> </gml:featureMember>
<gml:featureMember> <gml:featureMember>
@ -52,7 +52,7 @@
<ogr:right>8.23935</ogr:right> <ogr:right>8.23935</ogr:right>
<ogr:bottom>-6.11331</ogr:bottom> <ogr:bottom>-6.11331</ogr:bottom>
<ogr:id>12</ogr:id> <ogr:id>12</ogr:id>
<ogr:color_id>5</ogr:color_id> <ogr:color_id>4</ogr:color_id>
</ogr:topocolor_polys> </ogr:topocolor_polys>
</gml:featureMember> </gml:featureMember>
<gml:featureMember> <gml:featureMember>

View File

@ -2598,32 +2598,33 @@ tests:
name: expected/polygon_from_extent.gml name: expected/polygon_from_extent.gml
type: vector type: vector
# - algorithm: qgis:topologicalcoloring - algorithm: qgis:topologicalcoloring
# name: Topological coloring name: Topological coloring
# params: params:
# INPUT_LAYER: BALANCE: 0
# name: custom/adjacent_polys.gml INPUT:
# type: vector name: custom/adjacent_polys.gml
# MIN_COLORS: 4 type: vector
# results: MIN_COLORS: 4
# OUTPUT_LAYER: results:
# name: expected/topocolor_polys.gml OUTPUT:
# type: vector name: expected/topocolor_polys.gml
# type: vector
# - algorithm: qgis:topologicalcoloring
# name: Topological coloring w/ min distance - algorithm: qgis:topologicalcoloring
# params: name: Topological coloring w/ min distance
# BALANCE: '0' params:
# INPUT_LAYER: BALANCE: 0
# name: custom/adjacent_polys.gml INPUT:
# type: vector name: custom/adjacent_polys.gml
# MIN_COLORS: 4 type: vector
# MIN_DISTANCE: 4.0 MIN_COLORS: 4
# results: MIN_DISTANCE: 4.0
# OUTPUT_LAYER: results:
# name: expected/topocolor_polys_min_dist.gml OUTPUT:
# type: vector name: expected/topocolor_polys_min_dist.gml
# type: vector
- algorithm: qgis:regularpoints - algorithm: qgis:regularpoints
name: Regular point with standard extent name: Regular point with standard extent
params: params: