31
31
32
32
from collections import defaultdict
33
33
34
- from qgis .core import (QgsApplication ,
35
- QgsField ,
34
+ from qgis .core import (QgsField ,
36
35
QgsFeatureSink ,
37
36
QgsGeometry ,
38
37
QgsSpatialIndex ,
39
38
QgsPointXY ,
40
39
NULL ,
41
- QgsProcessingUtils )
40
+ QgsProcessing ,
41
+ QgsProcessingParameterFeatureSource ,
42
+ QgsProcessingParameterNumber ,
43
+ QgsProcessingParameterEnum ,
44
+ QgsProcessingParameterFeatureSink )
42
45
43
46
from qgis .PyQt .QtCore import (QVariant )
44
47
45
48
from processing .algs .qgis .QgisAlgorithm import QgisAlgorithm
46
- from processing .core .parameters import (ParameterVector ,
47
- ParameterSelection ,
48
- ParameterNumber )
49
- from processing .core .outputs import OutputVector
50
- from processing .tools import dataobjects
51
49
52
50
pluginPath = os .path .split (os .path .split (os .path .dirname (__file__ ))[0 ])[0 ]
53
51
54
52
55
53
class TopoColor (QgisAlgorithm ):
56
- INPUT_LAYER = 'INPUT_LAYER '
54
+ INPUT = 'INPUT '
57
55
MIN_COLORS = 'MIN_COLORS'
58
56
MIN_DISTANCE = 'MIN_DISTANCE'
59
57
BALANCE = 'BALANCE'
60
- OUTPUT_LAYER = 'OUTPUT_LAYER '
58
+ OUTPUT = 'OUTPUT '
61
59
62
60
def tags (self ):
63
61
return self .tr ('topocolor,colors,graph,adjacent,assign' ).split (',' )
@@ -69,21 +67,23 @@ def __init__(self):
69
67
super ().__init__ ()
70
68
71
69
def initAlgorithm (self , config = None ):
72
- self .addParameter (ParameterVector (self .INPUT_LAYER ,
73
- self .tr ('Input layer' ), [dataobjects .TYPE_VECTOR_POLYGON ]))
74
- self .addParameter (ParameterNumber (self .MIN_COLORS ,
75
- self .tr ('Minimum number of colors' ), 1 , 1000 , 4 ))
76
- self .addParameter (ParameterNumber (self .MIN_DISTANCE ,
77
- self .tr ('Minimum distance between features' ), 0.0 , 999999999.0 , 0.0 ))
70
+
71
+ self .addParameter (QgsProcessingParameterFeatureSource (self .INPUT ,
72
+ self .tr ('Input layer' ), [QgsProcessing .TypeVectorPolygon ]))
73
+ self .addParameter (QgsProcessingParameterNumber (self .MIN_COLORS ,
74
+ self .tr ('Minimum number of colors' ), minValue = 1 , maxValue = 1000 , defaultValue = 4 ))
75
+ self .addParameter (QgsProcessingParameterNumber (self .MIN_DISTANCE ,
76
+ self .tr ('Minimum distance between features' ), type = QgsProcessingParameterNumber .Double ,
77
+ minValue = 0.0 , maxValue = 999999999.0 , defaultValue = 0.0 ))
78
78
balance_by = [self .tr ('By feature count' ),
79
79
self .tr ('By assigned area' ),
80
80
self .tr ('By distance between colors' )]
81
- self .addParameter (ParameterSelection (
81
+ self .addParameter (QgsProcessingParameterEnum (
82
82
self .BALANCE ,
83
83
self .tr ('Balance color assignment' ),
84
- balance_by , default = 0 ))
84
+ options = balance_by , defaultValue = 0 ))
85
85
86
- self .addOutput ( OutputVector (self .OUTPUT_LAYER , self .tr ('Colored' ), datatype = [ dataobjects . TYPE_VECTOR_POLYGON ] ))
86
+ self .addParameter ( QgsProcessingParameterFeatureSink (self .OUTPUT , self .tr ('Colored' ), QgsProcessing . TypeVectorPolygon ))
87
87
88
88
def name (self ):
89
89
return 'topologicalcoloring'
@@ -92,18 +92,18 @@ def displayName(self):
92
92
return self .tr ('Topological coloring' )
93
93
94
94
def processAlgorithm (self , parameters , context , feedback ):
95
- layer = QgsProcessingUtils . mapLayerFromString ( self .getParameterValue ( self .INPUT_LAYER ) , context )
96
- min_colors = self .getParameterValue ( self .MIN_COLORS )
97
- balance_by = self .getParameterValue ( self .BALANCE )
98
- min_distance = self .getParameterValue ( self .MIN_DISTANCE )
95
+ source = self .parameterAsSource ( parameters , self .INPUT , context )
96
+ min_colors = self .parameterAsInt ( parameters , self .MIN_COLORS , context )
97
+ balance_by = self .parameterAsEnum ( parameters , self .BALANCE , context )
98
+ min_distance = self .parameterAsDouble ( parameters , self .MIN_DISTANCE , context )
99
99
100
- fields = layer .fields ()
100
+ fields = source .fields ()
101
101
fields .append (QgsField ('color_id' , QVariant .Int ))
102
102
103
- writer = self .getOutputFromName (
104
- self . OUTPUT_LAYER ). getVectorWriter ( fields , layer .wkbType (), layer . crs (), context )
103
+ ( sink , dest_id ) = self .parameterAsSink ( parameters , self . OUTPUT , context ,
104
+ fields , source .wkbType (), source . sourceCrs () )
105
105
106
- features = {f .id (): f for f in QgsProcessingUtils .getFeatures (layer , context )}
106
+ features = {f .id (): f for f in source .getFeatures ()}
107
107
108
108
topology , id_graph = self .compute_graph (features , feedback , min_distance = min_distance )
109
109
feature_colors = ColoringAlgorithm .balanced (features ,
@@ -118,6 +118,9 @@ def processAlgorithm(self, parameters, context, feedback):
118
118
total = 20.0 / len (features )
119
119
current = 0
120
120
for feature_id , input_feature in features .items ():
121
+ if feedback .isCanceled ():
122
+ break
123
+
121
124
output_feature = input_feature
122
125
attributes = input_feature .attributes ()
123
126
if feature_id in feature_colors :
@@ -126,11 +129,11 @@ def processAlgorithm(self, parameters, context, feedback):
126
129
attributes .append (NULL )
127
130
output_feature .setAttributes (attributes )
128
131
129
- writer .addFeature (output_feature , QgsFeatureSink .FastInsert )
132
+ sink .addFeature (output_feature , QgsFeatureSink .FastInsert )
130
133
current += 1
131
134
feedback .setProgress (80 + int (current * total ))
132
135
133
- del writer
136
+ return { self . OUTPUT : dest_id }
134
137
135
138
@staticmethod
136
139
def compute_graph (features , feedback , create_id_graph = False , min_distance = 0 ):
@@ -148,6 +151,9 @@ def compute_graph(features, feedback, create_id_graph=False, min_distance=0):
148
151
149
152
i = 0
150
153
for feature_id , f in features_with_geometry .items ():
154
+ if feedback .isCanceled ():
155
+ break
156
+
151
157
g = f .geometry ()
152
158
if min_distance > 0 :
153
159
g = g .buffer (min_distance , 5 )
@@ -172,6 +178,9 @@ def compute_graph(features, feedback, create_id_graph=False, min_distance=0):
172
178
feedback .setProgress (int (i * total ))
173
179
174
180
for feature_id , f in features_with_geometry .items ():
181
+ if feedback .isCanceled ():
182
+ break
183
+
175
184
if feature_id not in s .node_edge :
176
185
s .add_edge (feature_id , None )
177
186
@@ -206,6 +215,9 @@ def balanced(features, graph, feedback, balance=0, min_colors=4):
206
215
i = 0
207
216
208
217
for (feature_id , n ) in sorted_by_count :
218
+ if feedback .isCanceled ():
219
+ break
220
+
209
221
# first work out which already assigned colors are adjacent to this feature
210
222
adjacent_colors = set ()
211
223
for neighbour in graph .node_edge [feature_id ]:
@@ -240,6 +252,9 @@ def balanced(features, graph, feedback, balance=0, min_colors=4):
240
252
# loop through these, and calculate the minimum distance from this feature to the nearest
241
253
# feature with each assigned color
242
254
for other_feature_id , c in other_features .items ():
255
+ if feedback .isCanceled ():
256
+ break
257
+
243
258
other_geometry = features [other_feature_id ].geometry ()
244
259
other_centroid = QgsPointXY (other_geometry .centroid ().geometry ())
245
260
0 commit comments