Skip to content
Permalink
Browse files

Port processing oriented minimum bounding box alg to QgsGeometry

  • Loading branch information
nyalldawson committed Dec 7, 2016
1 parent 75f51bc commit c975764c12a182a9be37c1cce125ecc924da4e5f
@@ -405,9 +405,21 @@ class QgsGeometry
*/
QgsGeometry makeDifference( const QgsGeometry& other ) const;

/** Returns the bounding box of this feature*/
/**
* Returns the bounding box of the geometry.
* @see orientedMinimumBoundingBox()
*/
QgsRectangle boundingBox() const;

/**
* Returns the oriented minimum bounding box for the geometry, which is the smallest (by area)
* rotated rectangle which fully encompasses the geometry. The area, angle (clockwise in degrees from North),
* width and height of the rotated bounding box will also be returned.
* @note added in QGIS 3.0
* @see boundingBox()
*/
QgsGeometry orientedMinimumBoundingBox( double& area /Out/, double &angle /Out/, double& width /Out/, double& height /Out/ ) const;

/** Test for intersection with a rectangle (uses GEOS) */
bool intersects( const QgsRectangle& r ) const;

@@ -27,7 +27,7 @@

from math import degrees, atan2
from qgis.PyQt.QtCore import QVariant
from qgis.core import Qgis, QgsField, QgsPoint, QgsGeometry, QgsFeature, QgsWkbTypes
from qgis.core import Qgis, QgsField, QgsFields, QgsPoint, QgsGeometry, QgsFeature, QgsWkbTypes, QgsFeatureRequest
from processing.core.GeoAlgorithm import GeoAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterVector
@@ -62,13 +62,15 @@ def processAlgorithm(self, progress):
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"))

fields = [
QgsField('AREA', QVariant.Double),
QgsField('PERIMETER', QVariant.Double),
QgsField('ANGLE', QVariant.Double),
QgsField('WIDTH', QVariant.Double),
QgsField('HEIGHT', QVariant.Double),
]
if byFeature:
fields = layer.fields()
else:
fields = QgsFields()
fields.append(QgsField('area', QVariant.Double))
fields.append(QgsField('perimeter', QVariant.Double))
fields.append(QgsField('angle', QVariant.Double))
fields.append(QgsField('width', QVariant.Double))
fields.append(QgsField('height', QVariant.Double))

writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields,
QgsWkbTypes.Polygon, layer.crs())
@@ -81,30 +83,27 @@ def processAlgorithm(self, progress):
del writer

def layerOmmb(self, layer, writer, progress):
current = 0

fit = layer.getFeatures()
inFeat = QgsFeature()
total = 100.0 / layer.featureCount()
req = QgsFeatureRequest().setSubsetOfAttributes([])
features = vector.features(layer, req)
total = 100.0 / len(features)
newgeometry = QgsGeometry()
first = True
while fit.nextFeature(inFeat):
for current, inFeat in enumerate(features):
if first:
newgeometry = inFeat.geometry()
first = False
else:
newgeometry = newgeometry.combine(inFeat.geometry())
current += 1
progress.setPercentage(int(current * total))

geometry, area, perim, angle, width, height = self.OMBBox(newgeometry)
geometry, area, angle, width, height = newgeometry.orientedMinimumBoundingBox()

if geometry:
outFeat = QgsFeature()

outFeat.setGeometry(geometry)
outFeat.setAttributes([area,
perim,
width * 2 + height * 2,
angle,
width,
height])
@@ -115,62 +114,17 @@ def featureOmbb(self, layer, writer, progress):
total = 100.0 / len(features)
outFeat = QgsFeature()
for current, inFeat in enumerate(features):
geometry, area, perim, angle, width, height = self.OMBBox(
inFeat.geometry())
geometry, area, angle, width, height = inFeat.geometry().orientedMinimumBoundingBox()
if geometry:
outFeat.setGeometry(geometry)
outFeat.setAttributes([area,
perim,
angle,
width,
height])
attrs = inFeat.attributes()
attrs.extend([area,
width * 2 + height * 2,
angle,
width,
height])
outFeat.setAttributes(attrs)
writer.addFeature(outFeat)
else:
progress.setInfo(self.tr("Can't calculate an OMBB for feature {0}.").format(inFeat.id()))
progress.setPercentage(int(current * total))

def GetAngleOfLineBetweenTwoPoints(self, p1, p2, angle_unit="degrees"):
xDiff = p2.x() - p1.x()
yDiff = p2.y() - p1.y()
if angle_unit == "radians":
return atan2(yDiff, xDiff)
else:
return degrees(atan2(yDiff, xDiff))

def OMBBox(self, geom):
g = geom.convexHull()

if g.type() != QgsWkbTypes.PolygonGeometry:
return None, None, None, None, None, None
r = g.asPolygon()[0]

p0 = QgsPoint(r[0][0], r[0][1])

i = 0
l = len(r)
OMBBox = QgsGeometry()
gBBox = g.boundingBox()
OMBBox_area = gBBox.height() * gBBox.width()
OMBBox_angle = 0
OMBBox_width = 0
OMBBox_heigth = 0
OMBBox_perim = 0
while i < l - 1:
x = QgsGeometry(g)
angle = self.GetAngleOfLineBetweenTwoPoints(r[i], r[i + 1])
x.rotate(angle, p0)
bbox = x.boundingBox()
bb = QgsGeometry.fromWkt(bbox.asWktPolygon())
bb.rotate(-angle, p0)

areabb = bb.area()
if areabb <= OMBBox_area:
OMBBox = QgsGeometry(bb)
OMBBox_area = areabb
OMBBox_angle = angle
OMBBox_width = bbox.width()
OMBBox_heigth = bbox.height()
OMBBox_perim = 2 * OMBBox_width + 2 * OMBBox_heigth
i += 1

return OMBBox, OMBBox_area, OMBBox_perim, OMBBox_angle, OMBBox_width, OMBBox_heigth
@@ -0,0 +1,32 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>oriented_bbox</Name>
<ElementPath>oriented_bbox</ElementPath>
<!--POLYGON-->
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>6</FeatureCount>
<ExtentXMin>-1.00000</ExtentXMin>
<ExtentXMax>10.00000</ExtentXMax>
<ExtentYMin>-3.00000</ExtentYMin>
<ExtentYMax>6.00000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>intval</Name>
<ElementPath>intval</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
<PropertyDefn>
<Name>floatval</Name>
<ElementPath>floatval</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>name</Name>
<ElementPath>name</ElementPath>
<Type>String</Type>
<Width>5</Width>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=""
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>-1</gml:X><gml:Y>-3</gml:Y></gml:coord>
<gml:coord><gml:X>10</gml:X><gml:Y>6</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:oriented_bbox fid="polys.4">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3.8,2.2 6,1 6,-3 2.4,-1.0 3.8,2.2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:intval>120</ogr:intval>
<ogr:floatval>-100291.43213</ogr:floatval>
</ogr:oriented_bbox>
</gml:featureMember>
<gml:featureMember>
<ogr:oriented_bbox fid="polys.1">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5.4,5.0 6,4 5.2,3.8 4,4 5.4,5.0</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>Aaaaa</ogr:name>
<ogr:intval>-33</ogr:intval>
<ogr:floatval>0</ogr:floatval>
</ogr:oriented_bbox>
</gml:featureMember>
<gml:featureMember>
<ogr:oriented_bbox fid="polys.0">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-1,-1 -1,3 3,3 3,2 2,2 2,-1 -1,-1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>aaaaa</ogr:name>
<ogr:intval>33</ogr:intval>
<ogr:floatval>44.12346</ogr:floatval>
</ogr:oriented_bbox>
</gml:featureMember>
<gml:featureMember>
<ogr:oriented_bbox fid="polys.3">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>6.8,1.8 10,1 9.6,-2.2 6.4,-3.0 7.2,-0.6 6.8,1.8</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>8.0,-0.6 7,-2 9,-2 9,0 8.0,-0.6</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>ASDF</ogr:name>
<ogr:intval>0</ogr:intval>
</ogr:oriented_bbox>
</gml:featureMember>
<gml:featureMember>
<ogr:oriented_bbox fid="polys.2">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>1.6,4.8 2,6 3,6 1.6,4.8</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>bbaaa</ogr:name>
<ogr:floatval>0.123</ogr:floatval>
</ogr:oriented_bbox>
</gml:featureMember>
<gml:featureMember>
<ogr:oriented_bbox fid="polys.5">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3.8,2.2 6,1 6,-3 2.4,-1.0 3.8,2.2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>elim</ogr:name>
<ogr:intval>2</ogr:intval>
<ogr:floatval>3.33</ogr:floatval>
</ogr:oriented_bbox>
</gml:featureMember>
</ogr:FeatureCollection>
@@ -0,0 +1,57 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>oriented_bounds</Name>
<ElementPath>oriented_bounds</ElementPath>
<!--POLYGON-->
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>6</FeatureCount>
<ExtentXMin>-1.00000</ExtentXMin>
<ExtentXMax>10.04414</ExtentXMax>
<ExtentYMin>-3.27034</ExtentYMin>
<ExtentYMax>6.44118</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>intval</Name>
<ElementPath>intval</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
<PropertyDefn>
<Name>floatval</Name>
<ElementPath>floatval</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>area</Name>
<ElementPath>area</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>perimeter</Name>
<ElementPath>perimeter</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>angle</Name>
<ElementPath>angle</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>width</Name>
<ElementPath>width</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>height</Name>
<ElementPath>height</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>name</Name>
<ElementPath>name</ElementPath>
<Type>String</Type>
<Width>5</Width>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>

0 comments on commit c975764

Please sign in to comment.
You can’t perform that action at this time.