mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-17 00:04:02 -04:00
Port Topocolor algorithm to new API
This commit is contained in:
parent
ec4df6c019
commit
7132faa974
@ -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(),
|
||||||
|
@ -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())
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user