mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-28 00:17:30 -05:00
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.
This commit is contained in:
parent
83affdc7f5
commit
b6e35428e2
@ -38,6 +38,7 @@ from qgis.core import (QgsField,
|
|||||||
QgsWkbTypes,
|
QgsWkbTypes,
|
||||||
QgsFeatureRequest,
|
QgsFeatureRequest,
|
||||||
QgsFields,
|
QgsFields,
|
||||||
|
QgsRectangle,
|
||||||
QgsProcessingParameterFeatureSource,
|
QgsProcessingParameterFeatureSource,
|
||||||
QgsProcessingParameterField,
|
QgsProcessingParameterField,
|
||||||
QgsProcessingParameterEnum,
|
QgsProcessingParameterEnum,
|
||||||
@ -53,7 +54,6 @@ pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
|
|||||||
|
|
||||||
|
|
||||||
class MinimumBoundingGeometry(QgisAlgorithm):
|
class MinimumBoundingGeometry(QgisAlgorithm):
|
||||||
|
|
||||||
INPUT = 'INPUT'
|
INPUT = 'INPUT'
|
||||||
OUTPUT = 'OUTPUT'
|
OUTPUT = 'OUTPUT'
|
||||||
TYPE = 'TYPE'
|
TYPE = 'TYPE'
|
||||||
@ -76,11 +76,13 @@ class MinimumBoundingGeometry(QgisAlgorithm):
|
|||||||
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
|
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
|
||||||
self.tr('Input layer')))
|
self.tr('Input layer')))
|
||||||
self.addParameter(QgsProcessingParameterField(self.FIELD,
|
self.addParameter(QgsProcessingParameterField(self.FIELD,
|
||||||
self.tr('Field (optional, set if features should be grouped by class)'),
|
self.tr(
|
||||||
|
'Field (optional, set if features should be grouped by class)'),
|
||||||
parentLayerParameterName=self.INPUT, optional=True))
|
parentLayerParameterName=self.INPUT, optional=True))
|
||||||
self.addParameter(QgsProcessingParameterEnum(self.TYPE,
|
self.addParameter(QgsProcessingParameterEnum(self.TYPE,
|
||||||
self.tr('Geometry type'), options=self.type_names))
|
self.tr('Geometry type'), options=self.type_names))
|
||||||
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Bounding geometry'), QgsProcessing.TypeVectorPolygon))
|
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Bounding geometry'),
|
||||||
|
QgsProcessing.TypeVectorPolygon))
|
||||||
|
|
||||||
def name(self):
|
def name(self):
|
||||||
return 'minimumboundinggeometry'
|
return 'minimumboundinggeometry'
|
||||||
@ -89,7 +91,9 @@ class MinimumBoundingGeometry(QgisAlgorithm):
|
|||||||
return self.tr('Minimum bounding geometry')
|
return self.tr('Minimum bounding geometry')
|
||||||
|
|
||||||
def tags(self):
|
def tags(self):
|
||||||
return self.tr('bounding,box,bounds,envelope,minimum,oriented,rectangle,enclosing,circle,convex,hull,generalization').split(',')
|
return self.tr(
|
||||||
|
'bounding,box,bounds,envelope,minimum,oriented,rectangle,enclosing,circle,convex,hull,generalization').split(
|
||||||
|
',')
|
||||||
|
|
||||||
def processAlgorithm(self, parameters, context, feedback):
|
def processAlgorithm(self, parameters, context, feedback):
|
||||||
source = self.parameterAsSource(parameters, self.INPUT, context)
|
source = self.parameterAsSource(parameters, self.INPUT, context)
|
||||||
@ -108,13 +112,13 @@ class MinimumBoundingGeometry(QgisAlgorithm):
|
|||||||
if field_index >= 0:
|
if field_index >= 0:
|
||||||
fields.append(source.fields()[field_index])
|
fields.append(source.fields()[field_index])
|
||||||
if type == 0:
|
if type == 0:
|
||||||
#envelope
|
# envelope
|
||||||
fields.append(QgsField('width', QVariant.Double, '', 20, 6))
|
fields.append(QgsField('width', QVariant.Double, '', 20, 6))
|
||||||
fields.append(QgsField('height', QVariant.Double, '', 20, 6))
|
fields.append(QgsField('height', QVariant.Double, '', 20, 6))
|
||||||
fields.append(QgsField('area', QVariant.Double, '', 20, 6))
|
fields.append(QgsField('area', QVariant.Double, '', 20, 6))
|
||||||
fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6))
|
fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6))
|
||||||
elif type == 1:
|
elif type == 1:
|
||||||
#oriented rect
|
# oriented rect
|
||||||
fields.append(QgsField('width', QVariant.Double, '', 20, 6))
|
fields.append(QgsField('width', QVariant.Double, '', 20, 6))
|
||||||
fields.append(QgsField('height', QVariant.Double, '', 20, 6))
|
fields.append(QgsField('height', QVariant.Double, '', 20, 6))
|
||||||
fields.append(QgsField('angle', QVariant.Double, '', 20, 6))
|
fields.append(QgsField('angle', QVariant.Double, '', 20, 6))
|
||||||
@ -134,6 +138,7 @@ class MinimumBoundingGeometry(QgisAlgorithm):
|
|||||||
|
|
||||||
if field_index >= 0:
|
if field_index >= 0:
|
||||||
geometry_dict = {}
|
geometry_dict = {}
|
||||||
|
bounds_dict = {}
|
||||||
total = 50.0 / source.featureCount() if source.featureCount() else 1
|
total = 50.0 / source.featureCount() if source.featureCount() else 1
|
||||||
features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([field_index]))
|
features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([field_index]))
|
||||||
for current, f in enumerate(features):
|
for current, f in enumerate(features):
|
||||||
@ -143,29 +148,56 @@ class MinimumBoundingGeometry(QgisAlgorithm):
|
|||||||
if not f.hasGeometry():
|
if not f.hasGeometry():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not f.attributes()[field_index] in geometry_dict:
|
if type == 0:
|
||||||
geometry_dict[f.attributes()[field_index]] = [f.geometry()]
|
# bounding boxes - calculate on the fly for efficiency
|
||||||
|
if not f.attributes()[field_index] in bounds_dict:
|
||||||
|
bounds_dict[f.attributes()[field_index]] = f.geometry().boundingBox()
|
||||||
|
else:
|
||||||
|
bounds_dict[f.attributes()[field_index]].combineExtentWith(f.geometry().boundingBox())
|
||||||
else:
|
else:
|
||||||
geometry_dict[f.attributes()[field_index]].append(f.geometry())
|
if not f.attributes()[field_index] in geometry_dict:
|
||||||
|
geometry_dict[f.attributes()[field_index]] = [f.geometry()]
|
||||||
|
else:
|
||||||
|
geometry_dict[f.attributes()[field_index]].append(f.geometry())
|
||||||
|
|
||||||
feedback.setProgress(int(current * total))
|
feedback.setProgress(int(current * total))
|
||||||
|
|
||||||
current = 0
|
if type == 0:
|
||||||
total = 50.0 / len(geometry_dict) if geometry_dict else 1
|
# bounding boxes
|
||||||
for group, geometries in geometry_dict.items():
|
current = 0
|
||||||
if feedback.isCanceled():
|
total = 50.0 / len(bounds_dict) if bounds_dict else 1
|
||||||
break
|
for group, rect in bounds_dict.items():
|
||||||
|
if feedback.isCanceled():
|
||||||
|
break
|
||||||
|
|
||||||
feature = self.createFeature(feedback, current, type, geometries, group)
|
# envelope
|
||||||
sink.addFeature(feature, QgsFeatureSink.FastInsert)
|
feature = QgsFeature()
|
||||||
geometry_dict[group] = None
|
feature.setGeometry(QgsGeometry.fromRect(rect))
|
||||||
|
feature.setAttributes([current, group, rect.width(), rect.height(), rect.area(), rect.perimeter()])
|
||||||
|
sink.addFeature(feature, QgsFeatureSink.FastInsert)
|
||||||
|
geometry_dict[group] = None
|
||||||
|
|
||||||
feedback.setProgress(50 + int(current * total))
|
feedback.setProgress(50 + int(current * total))
|
||||||
current += 1
|
current += 1
|
||||||
|
else:
|
||||||
|
current = 0
|
||||||
|
total = 50.0 / len(geometry_dict) if geometry_dict else 1
|
||||||
|
|
||||||
|
for group, geometries in geometry_dict.items():
|
||||||
|
if feedback.isCanceled():
|
||||||
|
break
|
||||||
|
|
||||||
|
feature = self.createFeature(feedback, current, type, geometries, group)
|
||||||
|
sink.addFeature(feature, QgsFeatureSink.FastInsert)
|
||||||
|
geometry_dict[group] = None
|
||||||
|
|
||||||
|
feedback.setProgress(50 + int(current * total))
|
||||||
|
current += 1
|
||||||
else:
|
else:
|
||||||
total = 80.0 / source.featureCount() if source.featureCount() else 1
|
total = 80.0 / source.featureCount() if source.featureCount() else 1
|
||||||
features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]))
|
features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]))
|
||||||
geometry_queue = []
|
geometry_queue = []
|
||||||
|
bounds = QgsRectangle()
|
||||||
for current, f in enumerate(features):
|
for current, f in enumerate(features):
|
||||||
if feedback.isCanceled():
|
if feedback.isCanceled():
|
||||||
break
|
break
|
||||||
@ -173,11 +205,20 @@ class MinimumBoundingGeometry(QgisAlgorithm):
|
|||||||
if not f.hasGeometry():
|
if not f.hasGeometry():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
geometry_queue.append(f.geometry())
|
if type == 0:
|
||||||
|
# bounding boxes, calculate on the fly for efficiency
|
||||||
|
bounds.combineExtentWith(f.geometry().boundingBox())
|
||||||
|
else:
|
||||||
|
geometry_queue.append(f.geometry())
|
||||||
feedback.setProgress(int(current * total))
|
feedback.setProgress(int(current * total))
|
||||||
|
|
||||||
if not feedback.isCanceled():
|
if not feedback.isCanceled():
|
||||||
feature = self.createFeature(feedback, 0, type, geometry_queue)
|
if type == 0:
|
||||||
|
feature = QgsFeature()
|
||||||
|
feature.setGeometry(QgsGeometry.fromRect(bounds))
|
||||||
|
feature.setAttributes([0, bounds.width(), bounds.height(), bounds.area(), bounds.perimeter()])
|
||||||
|
else:
|
||||||
|
feature = self.createFeature(feedback, 0, type, geometry_queue)
|
||||||
sink.addFeature(feature, QgsFeatureSink.FastInsert)
|
sink.addFeature(feature, QgsFeatureSink.FastInsert)
|
||||||
|
|
||||||
return {self.OUTPUT: dest_id}
|
return {self.OUTPUT: dest_id}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user