28
28
29
29
import math
30
30
31
- from qgis .core import (QgsApplication ,
32
- QgsWkbTypes ,
31
+ from qgis .core import (QgsWkbTypes ,
33
32
QgsFeature ,
34
33
QgsFeatureSink ,
35
34
QgsGeometry ,
36
35
QgsPointXY ,
37
- QgsMessageLog ,
38
- QgsProcessingUtils )
36
+ QgsProcessing ,
37
+ QgsProcessingParameterField ,
38
+ QgsProcessingParameterFeatureSource ,
39
+ QgsProcessingParameterEnum ,
40
+ QgsProcessingParameterNumber ,
41
+ QgsProcessingParameterFeatureSink )
39
42
40
43
from processing .algs .qgis .QgisAlgorithm import QgisAlgorithm
41
- from processing .core .parameters import ParameterVector
42
- from processing .core .parameters import ParameterSelection
43
- from processing .core .parameters import ParameterTableField
44
- from processing .core .parameters import ParameterNumber
45
- from processing .core .outputs import OutputVector
46
- from processing .tools import dataobjects
47
44
48
45
49
46
class RectanglesOvalsDiamondsVariable (QgisAlgorithm ):
50
47
51
- INPUT_LAYER = 'INPUT_LAYER '
48
+ INPUT = 'INPUT '
52
49
SHAPE = 'SHAPE'
53
50
WIDTH = 'WIDTH'
54
51
HEIGHT = 'HEIGHT'
55
52
ROTATION = 'ROTATION'
56
53
SEGMENTS = 'SEGMENTS'
57
- OUTPUT_LAYER = 'OUTPUT_LAYER '
54
+ OUTPUT = 'OUTPUT '
58
55
59
56
def group (self ):
60
57
return self .tr ('Vector geometry tools' )
@@ -65,33 +62,35 @@ def __init__(self):
65
62
def initAlgorithm (self , config = None ):
66
63
self .shapes = [self .tr ('Rectangles' ), self .tr ('Diamonds' ), self .tr ('Ovals' )]
67
64
68
- self .addParameter (ParameterVector (self .INPUT_LAYER ,
69
- self .tr ('Input layer' ),
70
- [dataobjects .TYPE_VECTOR_POINT ]))
71
- self .addParameter (ParameterSelection (self .SHAPE ,
72
- self .tr ('Buffer shape' ), self .shapes ))
73
- self .addParameter (ParameterTableField (self .WIDTH ,
74
- self .tr ('Width field' ),
75
- self .INPUT_LAYER ,
76
- ParameterTableField .DATA_TYPE_NUMBER ))
77
- self .addParameter (ParameterTableField (self .HEIGHT ,
78
- self .tr ('Height field' ),
79
- self .INPUT_LAYER ,
80
- ParameterTableField .DATA_TYPE_NUMBER ))
81
- self .addParameter (ParameterTableField (self .ROTATION ,
82
- self .tr ('Rotation field' ),
83
- self .INPUT_LAYER ,
84
- ParameterTableField .DATA_TYPE_NUMBER ,
85
- True ))
86
- self .addParameter (ParameterNumber (self .SEGMENTS ,
87
- self .tr ('Number of segments' ),
88
- 1 ,
89
- 999999999 ,
90
- 36 ))
91
-
92
- self .addOutput (OutputVector (self .OUTPUT_LAYER ,
93
- self .tr ('Output' ),
94
- datatype = [dataobjects .TYPE_VECTOR_POLYGON ]))
65
+ self .addParameter (QgsProcessingParameterFeatureSource (self .INPUT ,
66
+ self .tr ('Input layer' ),
67
+ [QgsProcessing .TypeVectorPoint ]))
68
+
69
+ self .addParameter (QgsProcessingParameterEnum (self .SHAPE ,
70
+ self .tr ('Buffer shape' ), options = self .shapes ))
71
+
72
+ self .addParameter (QgsProcessingParameterField (self .WIDTH ,
73
+ self .tr ('Width field' ),
74
+ parentLayerParameterName = self .INPUT ,
75
+ type = QgsProcessingParameterField .Numeric ))
76
+ self .addParameter (QgsProcessingParameterField (self .HEIGHT ,
77
+ self .tr ('Height field' ),
78
+ parentLayerParameterName = self .INPUT ,
79
+ type = QgsProcessingParameterField .Numeric ))
80
+ self .addParameter (QgsProcessingParameterField (self .ROTATION ,
81
+ self .tr ('Rotation field' ),
82
+ parentLayerParameterName = self .INPUT ,
83
+ type = QgsProcessingParameterField .Numeric ,
84
+ optional = True ))
85
+ self .addParameter (QgsProcessingParameterNumber (self .SEGMENTS ,
86
+ self .tr ('Number of segments' ),
87
+ minValue = 1 ,
88
+ maxValue = 999999999 ,
89
+ defaultValue = 36 ))
90
+
91
+ self .addParameter (QgsProcessingParameterFeatureSink (self .OUTPUT ,
92
+ self .tr ('Output' ),
93
+ type = QgsProcessing .TypeVectorPolygon ))
95
94
96
95
def name (self ):
97
96
return 'rectanglesovalsdiamondsvariable'
@@ -100,40 +99,50 @@ def displayName(self):
100
99
return self .tr ('Rectangles, ovals, diamonds (variable)' )
101
100
102
101
def processAlgorithm (self , parameters , context , feedback ):
103
- layer = QgsProcessingUtils .mapLayerFromString (self .getParameterValue (self .INPUT_LAYER ), context )
104
- shape = self .getParameterValue (self .SHAPE )
105
- width = self .getParameterValue (self .WIDTH )
106
- height = self .getParameterValue (self .HEIGHT )
107
- rotation = self .getParameterValue (self .ROTATION )
108
- segments = self .getParameterValue (self .SEGMENTS )
102
+ source = self .parameterAsSource (parameters , self .INPUT , context )
103
+ shape = self .parameterAsEnum (parameters , self .SHAPE , context )
109
104
110
- writer = self .getOutputFromName (
111
- self .OUTPUT_LAYER ).getVectorWriter (layer .fields (), QgsWkbTypes .Polygon , layer .crs (), context )
105
+ width_field = self .parameterAsString (parameters , self .WIDTH , context )
106
+ height_field = self .parameterAsString (parameters , self .HEIGHT , context )
107
+ rotation_field = self .parameterAsString (parameters , self .ROTATION , context )
108
+ segments = self .parameterAsInt (parameters , self .SEGMENTS , context )
112
109
113
- features = QgsProcessingUtils .getFeatures (layer , context )
110
+ (sink , dest_id ) = self .parameterAsSink (parameters , self .OUTPUT , context ,
111
+ source .fields (), QgsWkbTypes .Polygon , source .sourceCrs ())
114
112
113
+ width = source .fields ().lookupField (width_field )
114
+ height = source .fields ().lookupField (height_field )
115
+ rotation = source .fields ().lookupField (rotation_field )
115
116
if shape == 0 :
116
- self .rectangles (writer , features , width , height , rotation )
117
+ self .rectangles (sink , source , width , height , rotation , feedback )
117
118
elif shape == 1 :
118
- self .diamonds (writer , features , width , height , rotation )
119
+ self .diamonds (sink , source , width , height , rotation , feedback )
119
120
else :
120
- self .ovals (writer , features , width , height , rotation , segments )
121
+ self .ovals (sink , source , width , height , rotation , segments , feedback )
121
122
122
- del writer
123
+ return { self . OUTPUT : dest_id }
123
124
124
- def rectangles (self , writer , features , width , height , rotation ):
125
+ def rectangles (self , sink , source , width , height , rotation , feedback ):
125
126
ft = QgsFeature ()
127
+ features = source .getFeatures ()
126
128
127
- if rotation is not None :
129
+ total = 100.0 / source .featureCount () if source .featureCount () else 0
130
+
131
+ if rotation >= 0 :
128
132
for current , feat in enumerate (features ):
133
+ if feedback .isCanceled ():
134
+ break
135
+
136
+ if not feat .hasGeometry ():
137
+ continue
138
+
129
139
w = feat [width ]
130
140
h = feat [height ]
131
141
angle = feat [rotation ]
132
142
if not w or not h or not angle :
133
- QgsMessageLog .logMessage (self .tr ('Feature {} has empty '
134
- 'width, height or angle. '
135
- 'Skipping...' .format (feat .id ())),
136
- self .tr ('Processing' ), QgsMessageLog .WARNING )
143
+ feedback .pushInfo (self .tr ('Feature {} has empty '
144
+ 'width, height or angle. '
145
+ 'Skipping...' .format (feat .id ())))
137
146
continue
138
147
139
148
xOffset = w / 2.0
@@ -149,16 +158,23 @@ def rectangles(self, writer, features, width, height, rotation):
149
158
150
159
ft .setGeometry (QgsGeometry .fromPolygon (polygon ))
151
160
ft .setAttributes (feat .attributes ())
152
- writer .addFeature (ft , QgsFeatureSink .FastInsert )
161
+ sink .addFeature (ft , QgsFeatureSink .FastInsert )
162
+
163
+ feedback .setProgress (int (current * total ))
153
164
else :
154
165
for current , feat in enumerate (features ):
166
+ if feedback .isCanceled ():
167
+ break
168
+
169
+ if not feat .hasGeometry ():
170
+ continue
171
+
155
172
w = feat [width ]
156
173
h = feat [height ]
157
174
if not w or not h :
158
- QgsMessageLog .logMessage (self .tr ('Feature {} has empty '
159
- 'width or height. '
160
- 'Skipping...' .format (feat .id ())),
161
- self .tr ('Processing' ), QgsMessageLog .WARNING )
175
+ feedback .pushInfo (self .tr ('Feature {} has empty '
176
+ 'width or height. '
177
+ 'Skipping...' .format (feat .id ())))
162
178
continue
163
179
164
180
xOffset = w / 2.0
@@ -172,21 +188,30 @@ def rectangles(self, writer, features, width, height, rotation):
172
188
173
189
ft .setGeometry (QgsGeometry .fromPolygon (polygon ))
174
190
ft .setAttributes (feat .attributes ())
175
- writer .addFeature (ft , QgsFeatureSink .FastInsert )
191
+ sink .addFeature (ft , QgsFeatureSink .FastInsert )
192
+
193
+ feedback .setProgress (int (current * total ))
176
194
177
- def diamonds (self , writer , features , width , height , rotation ):
195
+ def diamonds (self , sink , source , width , height , rotation , feedback ):
196
+ features = source .getFeatures ()
178
197
ft = QgsFeature ()
179
198
180
- if rotation is not None :
199
+ total = 100.0 / source .featureCount () if source .featureCount () else 0
200
+ if rotation >= 0 :
181
201
for current , feat in enumerate (features ):
202
+ if feedback .isCanceled ():
203
+ break
204
+
205
+ if not feat .hasGeometry ():
206
+ continue
207
+
182
208
w = feat [width ]
183
209
h = feat [height ]
184
210
angle = feat [rotation ]
185
211
if not w or not h or not angle :
186
- QgsMessageLog .logMessage (self .tr ('Feature {} has empty '
187
- 'width, height or angle. '
188
- 'Skipping...' .format (feat .id ())),
189
- self .tr ('Processing' ), QgsMessageLog .WARNING )
212
+ feedback .pushInfo (self .tr ('Feature {} has empty '
213
+ 'width, height or angle. '
214
+ 'Skipping...' .format (feat .id ())))
190
215
continue
191
216
192
217
xOffset = w / 2.0
@@ -202,16 +227,22 @@ def diamonds(self, writer, features, width, height, rotation):
202
227
203
228
ft .setGeometry (QgsGeometry .fromPolygon (polygon ))
204
229
ft .setAttributes (feat .attributes ())
205
- writer .addFeature (ft , QgsFeatureSink .FastInsert )
230
+ sink .addFeature (ft , QgsFeatureSink .FastInsert )
231
+ feedback .setProgress (int (current * total ))
206
232
else :
207
233
for current , feat in enumerate (features ):
234
+ if feedback .isCanceled ():
235
+ break
236
+
237
+ if not feat .hasGeometry ():
238
+ continue
239
+
208
240
w = feat [width ]
209
241
h = feat [height ]
210
242
if not w or not h :
211
- QgsMessageLog .logMessage (self .tr ('Feature {} has empty '
212
- 'width or height. '
213
- 'Skipping...' .format (feat .id ())),
214
- self .tr ('Processing' ), QgsMessageLog .WARNING )
243
+ feedback .pushInfo (self .tr ('Feature {} has empty '
244
+ 'width or height. '
245
+ 'Skipping...' .format (feat .id ())))
215
246
continue
216
247
217
248
xOffset = w / 2.0
@@ -225,21 +256,29 @@ def diamonds(self, writer, features, width, height, rotation):
225
256
226
257
ft .setGeometry (QgsGeometry .fromPolygon (polygon ))
227
258
ft .setAttributes (feat .attributes ())
228
- writer .addFeature (ft , QgsFeatureSink .FastInsert )
259
+ sink .addFeature (ft , QgsFeatureSink .FastInsert )
260
+ feedback .setProgress (int (current * total ))
229
261
230
- def ovals (self , writer , features , width , height , rotation , segments ):
262
+ def ovals (self , sink , source , width , height , rotation , segments , feedback ):
263
+ features = source .getFeatures ()
231
264
ft = QgsFeature ()
232
265
233
- if rotation is not None :
266
+ total = 100.0 / source .featureCount () if source .featureCount () else 0
267
+ if rotation >= 0 :
234
268
for current , feat in enumerate (features ):
269
+ if feedback .isCanceled ():
270
+ break
271
+
272
+ if not feat .hasGeometry ():
273
+ continue
274
+
235
275
w = feat [width ]
236
276
h = feat [height ]
237
277
angle = feat [rotation ]
238
278
if not w or not h or not angle :
239
- QgsMessageLog .logMessage (self .tr ('Feature {} has empty '
240
- 'width, height or angle. '
241
- 'Skipping...' .format (feat .id ())),
242
- self .tr ('Processing' ), QgsMessageLog .WARNING )
279
+ feedback .pushInfo (self .tr ('Feature {} has empty '
280
+ 'width, height or angle. '
281
+ 'Skipping...' .format (feat .id ())))
243
282
continue
244
283
245
284
xOffset = w / 2.0
@@ -257,16 +296,22 @@ def ovals(self, writer, features, width, height, rotation, segments):
257
296
258
297
ft .setGeometry (QgsGeometry .fromPolygon (polygon ))
259
298
ft .setAttributes (feat .attributes ())
260
- writer .addFeature (ft , QgsFeatureSink .FastInsert )
299
+ sink .addFeature (ft , QgsFeatureSink .FastInsert )
300
+ feedback .setProgress (int (current * total ))
261
301
else :
262
302
for current , feat in enumerate (features ):
303
+ if feedback .isCanceled ():
304
+ break
305
+
306
+ if not feat .hasGeometry ():
307
+ continue
308
+
263
309
w = feat [width ]
264
310
h = feat [height ]
265
311
if not w or not h :
266
- QgsMessageLog .logMessage (self .tr ('Feature {} has empty '
267
- 'width or height. '
268
- 'Skipping...' .format (feat .id ())),
269
- self .tr ('Processing' ), QgsMessageLog .WARNING )
312
+ feedback .pushInfo (self .tr ('Feature {} has empty '
313
+ 'width or height. '
314
+ 'Skipping...' .format (feat .id ())))
270
315
continue
271
316
272
317
xOffset = w / 2.0
@@ -282,4 +327,5 @@ def ovals(self, writer, features, width, height, rotation, segments):
282
327
283
328
ft .setGeometry (QgsGeometry .fromPolygon (polygon ))
284
329
ft .setAttributes (feat .attributes ())
285
- writer .addFeature (ft , QgsFeatureSink .FastInsert )
330
+ sink .addFeature (ft , QgsFeatureSink .FastInsert )
331
+ feedback .setProgress (int (current * total ))
0 commit comments