38
38
QgsWkbTypes ,
39
39
QgsFeatureRequest ,
40
40
QgsFields ,
41
+ QgsRectangle ,
41
42
QgsProcessingParameterFeatureSource ,
42
43
QgsProcessingParameterField ,
43
44
QgsProcessingParameterEnum ,
53
54
54
55
55
56
class MinimumBoundingGeometry (QgisAlgorithm ):
56
-
57
57
INPUT = 'INPUT'
58
58
OUTPUT = 'OUTPUT'
59
59
TYPE = 'TYPE'
@@ -76,11 +76,13 @@ def initAlgorithm(self, config=None):
76
76
self .addParameter (QgsProcessingParameterFeatureSource (self .INPUT ,
77
77
self .tr ('Input layer' )))
78
78
self .addParameter (QgsProcessingParameterField (self .FIELD ,
79
- self .tr ('Field (optional, set if features should be grouped by class)' ),
79
+ self .tr (
80
+ 'Field (optional, set if features should be grouped by class)' ),
80
81
parentLayerParameterName = self .INPUT , optional = True ))
81
82
self .addParameter (QgsProcessingParameterEnum (self .TYPE ,
82
83
self .tr ('Geometry type' ), options = self .type_names ))
83
- self .addParameter (QgsProcessingParameterFeatureSink (self .OUTPUT , self .tr ('Bounding geometry' ), QgsProcessing .TypeVectorPolygon ))
84
+ self .addParameter (QgsProcessingParameterFeatureSink (self .OUTPUT , self .tr ('Bounding geometry' ),
85
+ QgsProcessing .TypeVectorPolygon ))
84
86
85
87
def name (self ):
86
88
return 'minimumboundinggeometry'
@@ -89,7 +91,9 @@ def displayName(self):
89
91
return self .tr ('Minimum bounding geometry' )
90
92
91
93
def tags (self ):
92
- return self .tr ('bounding,box,bounds,envelope,minimum,oriented,rectangle,enclosing,circle,convex,hull,generalization' ).split (',' )
94
+ return self .tr (
95
+ 'bounding,box,bounds,envelope,minimum,oriented,rectangle,enclosing,circle,convex,hull,generalization' ).split (
96
+ ',' )
93
97
94
98
def processAlgorithm (self , parameters , context , feedback ):
95
99
source = self .parameterAsSource (parameters , self .INPUT , context )
@@ -108,13 +112,13 @@ def processAlgorithm(self, parameters, context, feedback):
108
112
if field_index >= 0 :
109
113
fields .append (source .fields ()[field_index ])
110
114
if type == 0 :
111
- #envelope
115
+ # envelope
112
116
fields .append (QgsField ('width' , QVariant .Double , '' , 20 , 6 ))
113
117
fields .append (QgsField ('height' , QVariant .Double , '' , 20 , 6 ))
114
118
fields .append (QgsField ('area' , QVariant .Double , '' , 20 , 6 ))
115
119
fields .append (QgsField ('perimeter' , QVariant .Double , '' , 20 , 6 ))
116
120
elif type == 1 :
117
- #oriented rect
121
+ # oriented rect
118
122
fields .append (QgsField ('width' , QVariant .Double , '' , 20 , 6 ))
119
123
fields .append (QgsField ('height' , QVariant .Double , '' , 20 , 6 ))
120
124
fields .append (QgsField ('angle' , QVariant .Double , '' , 20 , 6 ))
@@ -134,6 +138,7 @@ def processAlgorithm(self, parameters, context, feedback):
134
138
135
139
if field_index >= 0 :
136
140
geometry_dict = {}
141
+ bounds_dict = {}
137
142
total = 50.0 / source .featureCount () if source .featureCount () else 1
138
143
features = source .getFeatures (QgsFeatureRequest ().setSubsetOfAttributes ([field_index ]))
139
144
for current , f in enumerate (features ):
@@ -143,41 +148,77 @@ def processAlgorithm(self, parameters, context, feedback):
143
148
if not f .hasGeometry ():
144
149
continue
145
150
146
- if not f .attributes ()[field_index ] in geometry_dict :
147
- geometry_dict [f .attributes ()[field_index ]] = [f .geometry ()]
151
+ if type == 0 :
152
+ # bounding boxes - calculate on the fly for efficiency
153
+ if not f .attributes ()[field_index ] in bounds_dict :
154
+ bounds_dict [f .attributes ()[field_index ]] = f .geometry ().boundingBox ()
155
+ else :
156
+ bounds_dict [f .attributes ()[field_index ]].combineExtentWith (f .geometry ().boundingBox ())
148
157
else :
149
- geometry_dict [f .attributes ()[field_index ]].append (f .geometry ())
158
+ if not f .attributes ()[field_index ] in geometry_dict :
159
+ geometry_dict [f .attributes ()[field_index ]] = [f .geometry ()]
160
+ else :
161
+ geometry_dict [f .attributes ()[field_index ]].append (f .geometry ())
150
162
151
163
feedback .setProgress (int (current * total ))
152
164
153
- current = 0
154
- total = 50.0 / len (geometry_dict ) if geometry_dict else 1
155
- for group , geometries in geometry_dict .items ():
156
- if feedback .isCanceled ():
157
- break
158
-
159
- feature = self .createFeature (feedback , current , type , geometries , group )
160
- sink .addFeature (feature , QgsFeatureSink .FastInsert )
161
- geometry_dict [group ] = None
162
-
163
- feedback .setProgress (50 + int (current * total ))
164
- current += 1
165
+ if type == 0 :
166
+ # bounding boxes
167
+ current = 0
168
+ total = 50.0 / len (bounds_dict ) if bounds_dict else 1
169
+ for group , rect in bounds_dict .items ():
170
+ if feedback .isCanceled ():
171
+ break
172
+
173
+ # envelope
174
+ feature = QgsFeature ()
175
+ feature .setGeometry (QgsGeometry .fromRect (rect ))
176
+ feature .setAttributes ([current , group , rect .width (), rect .height (), rect .area (), rect .perimeter ()])
177
+ sink .addFeature (feature , QgsFeatureSink .FastInsert )
178
+ geometry_dict [group ] = None
179
+
180
+ feedback .setProgress (50 + int (current * total ))
181
+ current += 1
182
+ else :
183
+ current = 0
184
+ total = 50.0 / len (geometry_dict ) if geometry_dict else 1
185
+
186
+ for group , geometries in geometry_dict .items ():
187
+ if feedback .isCanceled ():
188
+ break
189
+
190
+ feature = self .createFeature (feedback , current , type , geometries , group )
191
+ sink .addFeature (feature , QgsFeatureSink .FastInsert )
192
+ geometry_dict [group ] = None
193
+
194
+ feedback .setProgress (50 + int (current * total ))
195
+ current += 1
165
196
else :
166
197
total = 80.0 / source .featureCount () if source .featureCount () else 1
167
198
features = source .getFeatures (QgsFeatureRequest ().setSubsetOfAttributes ([]))
168
199
geometry_queue = []
200
+ bounds = QgsRectangle ()
169
201
for current , f in enumerate (features ):
170
202
if feedback .isCanceled ():
171
203
break
172
204
173
205
if not f .hasGeometry ():
174
206
continue
175
207
176
- geometry_queue .append (f .geometry ())
208
+ if type == 0 :
209
+ # bounding boxes, calculate on the fly for efficiency
210
+ bounds .combineExtentWith (f .geometry ().boundingBox ())
211
+ else :
212
+ geometry_queue .append (f .geometry ())
177
213
feedback .setProgress (int (current * total ))
178
214
179
215
if not feedback .isCanceled ():
180
- feature = self .createFeature (feedback , 0 , type , geometry_queue )
216
+ if type == 0 :
217
+ feature = QgsFeature ()
218
+ feature .setGeometry (QgsGeometry .fromRect (bounds ))
219
+ feature .setAttributes ([0 , bounds .width (), bounds .height (), bounds .area (), bounds .perimeter ()])
220
+ else :
221
+ feature = self .createFeature (feedback , 0 , type , geometry_queue )
181
222
sink .addFeature (feature , QgsFeatureSink .FastInsert )
182
223
183
224
return {self .OUTPUT : dest_id }
0 commit comments