Skip to content

Commit b6e3542

Browse files
committed
Optimise calculation of envelopes for MinimumBoundingGeometry alg
It's more efficient to calculate these on the fly, rather then collecting all geometry points and then calculating.
1 parent 83affdc commit b6e3542

File tree

1 file changed

+64
-23
lines changed

1 file changed

+64
-23
lines changed

python/plugins/processing/algs/qgis/MinimumBoundingGeometry.py

+64-23
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
QgsWkbTypes,
3939
QgsFeatureRequest,
4040
QgsFields,
41+
QgsRectangle,
4142
QgsProcessingParameterFeatureSource,
4243
QgsProcessingParameterField,
4344
QgsProcessingParameterEnum,
@@ -53,7 +54,6 @@
5354

5455

5556
class MinimumBoundingGeometry(QgisAlgorithm):
56-
5757
INPUT = 'INPUT'
5858
OUTPUT = 'OUTPUT'
5959
TYPE = 'TYPE'
@@ -76,11 +76,13 @@ def initAlgorithm(self, config=None):
7676
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
7777
self.tr('Input layer')))
7878
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)'),
8081
parentLayerParameterName=self.INPUT, optional=True))
8182
self.addParameter(QgsProcessingParameterEnum(self.TYPE,
8283
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))
8486

8587
def name(self):
8688
return 'minimumboundinggeometry'
@@ -89,7 +91,9 @@ def displayName(self):
8991
return self.tr('Minimum bounding geometry')
9092

9193
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+
',')
9397

9498
def processAlgorithm(self, parameters, context, feedback):
9599
source = self.parameterAsSource(parameters, self.INPUT, context)
@@ -108,13 +112,13 @@ def processAlgorithm(self, parameters, context, feedback):
108112
if field_index >= 0:
109113
fields.append(source.fields()[field_index])
110114
if type == 0:
111-
#envelope
115+
# envelope
112116
fields.append(QgsField('width', QVariant.Double, '', 20, 6))
113117
fields.append(QgsField('height', QVariant.Double, '', 20, 6))
114118
fields.append(QgsField('area', QVariant.Double, '', 20, 6))
115119
fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6))
116120
elif type == 1:
117-
#oriented rect
121+
# oriented rect
118122
fields.append(QgsField('width', QVariant.Double, '', 20, 6))
119123
fields.append(QgsField('height', QVariant.Double, '', 20, 6))
120124
fields.append(QgsField('angle', QVariant.Double, '', 20, 6))
@@ -134,6 +138,7 @@ def processAlgorithm(self, parameters, context, feedback):
134138

135139
if field_index >= 0:
136140
geometry_dict = {}
141+
bounds_dict = {}
137142
total = 50.0 / source.featureCount() if source.featureCount() else 1
138143
features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([field_index]))
139144
for current, f in enumerate(features):
@@ -143,41 +148,77 @@ def processAlgorithm(self, parameters, context, feedback):
143148
if not f.hasGeometry():
144149
continue
145150

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())
148157
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())
150162

151163
feedback.setProgress(int(current * total))
152164

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
165196
else:
166197
total = 80.0 / source.featureCount() if source.featureCount() else 1
167198
features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]))
168199
geometry_queue = []
200+
bounds = QgsRectangle()
169201
for current, f in enumerate(features):
170202
if feedback.isCanceled():
171203
break
172204

173205
if not f.hasGeometry():
174206
continue
175207

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())
177213
feedback.setProgress(int(current * total))
178214

179215
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)
181222
sink.addFeature(feature, QgsFeatureSink.FastInsert)
182223

183224
return {self.OUTPUT: dest_id}

0 commit comments

Comments
 (0)