Skip to content
Permalink
Browse files
Port oriented minimum bounds to new API
  • Loading branch information
nyalldawson committed Aug 18, 2017
1 parent 86949cb commit c4011ad
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 54 deletions.
@@ -33,19 +33,17 @@
QgsFeature,
QgsWkbTypes,
QgsFeatureRequest,
QgsApplication,
QgsProcessingUtils)
QgsProcessing,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterBoolean,
QgsProcessingParameterFeatureSink,
QgsProcessingException)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterBoolean
from processing.core.outputs import OutputVector
from processing.tools import dataobjects


class OrientedMinimumBoundingBox(QgisAlgorithm):

INPUT_LAYER = 'INPUT_LAYER'
INPUT = 'INPUT'
BY_FEATURE = 'BY_FEATURE'

OUTPUT = 'OUTPUT'
@@ -57,12 +55,12 @@ def __init__(self):
super().__init__()

def initAlgorithm(self, config=None):
self.addParameter(ParameterVector(self.INPUT_LAYER,
self.tr('Input layer')))
self.addParameter(ParameterBoolean(self.BY_FEATURE,
self.tr('Calculate OMBB for each feature separately'), True))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
self.tr('Input layer'), [QgsProcessing.TypeVectorAny]))
self.addParameter(QgsProcessingParameterBoolean(self.BY_FEATURE,
self.tr('Calculate bounds for each feature separately'), defaultValue=True))

self.addOutput(OutputVector(self.OUTPUT, self.tr('Oriented_MBBox'), datatype=[dataobjects.TYPE_VECTOR_POLYGON]))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Bounding boxes'), QgsProcessing.TypeVectorPolygon))

def name(self):
return 'orientedminimumboundingbox'
@@ -71,14 +69,14 @@ def displayName(self):
return self.tr('Oriented minimum bounding box')

def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context)
byFeature = self.getParameterValue(self.BY_FEATURE)
source = self.parameterAsSource(parameters, self.INPUT, context)
by_feature = self.parameterAsBool(parameters, self.BY_FEATURE, context)

if byFeature and layer.geometryType() == QgsWkbTypes.PointGeometry and layer.featureCount() <= 2:
raise GeoAlgorithmExecutionException(self.tr("Can't calculate an OMBB for each point, it's a point. The number of points must be greater than 2"))
if not by_feature and QgsWkbTypes.geometryType(source.wkbType()) == QgsWkbTypes.PointGeometry and source.featureCount() <= 2:
raise QgsProcessingException(self.tr("Can't calculate an OMBB for each point, it's a point. The number of points must be greater than 2"))

if byFeature:
fields = layer.fields()
if by_feature:
fields = source.fields()
else:
fields = QgsFields()
fields.append(QgsField('area', QVariant.Double))
@@ -87,29 +85,32 @@ def processAlgorithm(self, parameters, context, feedback):
fields.append(QgsField('width', QVariant.Double))
fields.append(QgsField('height', QVariant.Double))

writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Polygon, layer.crs(), context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, QgsWkbTypes.Polygon, source.sourceCrs())

if byFeature:
self.featureOmbb(layer, context, writer, feedback)
if by_feature:
self.featureOmbb(source, context, sink, feedback)
else:
self.layerOmmb(layer, context, writer, feedback)
self.layerOmmb(source, context, sink, feedback)

del writer
return {self.OUTPUT: dest_id}

def layerOmmb(self, layer, context, writer, feedback):
def layerOmmb(self, source, context, sink, feedback):
req = QgsFeatureRequest().setSubsetOfAttributes([])
features = QgsProcessingUtils.getFeatures(layer, context, req)
total = 100.0 / layer.featureCount() if layer.featureCount() else 0
features = source.getFeatures(req)
total = 100.0 / source.featureCount() if source.featureCount() else 0
newgeometry = QgsGeometry()
first = True
geometries = []
for current, inFeat in enumerate(features):
if first:
newgeometry = inFeat.geometry()
first = False
else:
newgeometry = newgeometry.combine(inFeat.geometry())
if feedback.isCanceled():
break

if inFeat.hasGeometry():
geometries.append(inFeat.geometry())
feedback.setProgress(int(current * total))

newgeometry = QgsGeometry.unaryUnion(geometries)
geometry, area, angle, width, height = newgeometry.orientedMinimumBoundingBox()

if geometry:
@@ -121,13 +122,16 @@ def layerOmmb(self, layer, context, writer, feedback):
angle,
width,
height])
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

def featureOmbb(self, layer, context, writer, feedback):
features = QgsProcessingUtils.getFeatures(layer, context)
total = 100.0 / layer.featureCount() if layer.featureCount() else 0
def featureOmbb(self, source, context, sink, feedback):
features = source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0
outFeat = QgsFeature()
for current, inFeat in enumerate(features):
if feedback.isCanceled():
break

geometry, area, angle, width, height = inFeat.geometry().orientedMinimumBoundingBox()
if geometry:
outFeat.setGeometry(geometry)
@@ -138,7 +142,7 @@ def featureOmbb(self, layer, context, writer, feedback):
width,
height])
outFeat.setAttributes(attrs)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
else:
feedback.pushInfo(self.tr("Can't calculate an OMBB for feature {0}.").format(inFeat.id()))
feedback.setProgress(int(current * total))
@@ -92,6 +92,7 @@
from .MergeLines import MergeLines
from .NearestNeighbourAnalysis import NearestNeighbourAnalysis
from .OffsetLine import OffsetLine
from .OrientedMinimumBoundingBox import OrientedMinimumBoundingBox
from .Orthogonalize import Orthogonalize
from .PointDistance import PointDistance
from .PointOnSurface import PointOnSurface
@@ -163,7 +164,6 @@
# from .SelectByAttributeSum import SelectByAttributeSum
# from .HypsometricCurves import HypsometricCurves
# from .Datasources2Vrt import Datasources2Vrt
# from .OrientedMinimumBoundingBox import OrientedMinimumBoundingBox
# from .DefineProjection import DefineProjection
# from .RectanglesOvalsDiamondsVariable import RectanglesOvalsDiamondsVariable
# from .RectanglesOvalsDiamondsFixed import RectanglesOvalsDiamondsFixed
@@ -195,7 +195,7 @@ def getAlgs(self):
# SetVectorStyle(), SetRasterStyle(),
# HypsometricCurves(),
# FieldsMapper(), SelectByAttributeSum(), Datasources2Vrt(),
# OrientedMinimumBoundingBox(),
#
# DefineProjection(),
# RectanglesOvalsDiamondsVariable(),
# RectanglesOvalsDiamondsFixed(),
@@ -256,6 +256,7 @@ def getAlgs(self):
MergeLines(),
NearestNeighbourAnalysis(),
OffsetLine(),
OrientedMinimumBoundingBox(),
Orthogonalize(),
PointDistance(),
PointOnSurface(),
@@ -2235,21 +2235,21 @@ tests:
# OUTPUT:
# hash: fe6e018be13c5a3c17f3f4d0f0dc7686c628cb440b74c4642aa0c939
# type: rasterhash
#
# - algorithm: qgis:orientedminimumboundingbox
# name: Oriented minimum bounding box polys
# params:
# BY_FEATURE: true
# INPUT_LAYER:
# name: custom/oriented_bbox.gml
# type: vector
# results:
# OUTPUT:
# name: expected/oriented_bounds.gml
# type: vector
# compare:
# geometry:
# precision: 7

- algorithm: qgis:orientedminimumboundingbox
name: Oriented minimum bounding box polys
params:
BY_FEATURE: true
INPUT:
name: custom/oriented_bbox.gml
type: vector
results:
OUTPUT:
name: expected/oriented_bounds.gml
type: vector
compare:
geometry:
precision: 7

- algorithm: qgis:orthogonalize
name: Orthogonalize polys

0 comments on commit c4011ad

Please sign in to comment.