Skip to content

Commit

Permalink
Add QgsGeometry method to create wedge shaped buffers
Browse files Browse the repository at this point in the history
Creates a wedge shaped buffer using circular strings, with
parameters for azimuth, wedge width (in degrees), outer radius
and inner radius.
  • Loading branch information
nyalldawson committed Apr 23, 2018
1 parent 9a6d966 commit e047738
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 0 deletions.
18 changes: 18 additions & 0 deletions python/core/geometry/qgsgeometry.sip.in
Expand Up @@ -211,6 +211,24 @@ Creates a new geometry from a :py:class:`QgsRectangle`
Creates a new multipart geometry from a list of QgsGeometry objects
%End

static QgsGeometry createWedgeBuffer( const QgsPoint &center, double azimuth, double angularWidth,
double outerRadius, double innerRadius = 0 );
%Docstring
Creates a wedge shaped buffer from a ``center`` point.

The ``azimuth`` gives the angle (in degrees) for the middle of the wedge to point.
The buffer width (in degrees) is specified by the ``angularWidth`` parameter. Note that the
wedge will extend to half of the ``angularWidth`` either side of the ``azimuth`` direction.

The outer radius of the buffer is specified via ``outerRadius``, and optionally an
``innerRadius`` can also be specified.

The returned geometry will be a CurvePolygon geometry containing circular strings. It may
need to be segmentized to convert to a standard Polygon geometry.

.. versionadded:: 3.2
%End



void fromWkb( const QByteArray &wkb );
Expand Down
33 changes: 33 additions & 0 deletions src/core/geometry/qgsgeometry.cpp
Expand Up @@ -258,6 +258,39 @@ QgsGeometry QgsGeometry::collectGeometry( const QVector< QgsGeometry > &geometri
return collected;
}

QgsGeometry QgsGeometry::createWedgeBuffer( const QgsPoint &center, const double azimuth, const double angularWidth, const double outerRadius, const double innerRadius )
{
std::unique_ptr< QgsCompoundCurve > wedge = qgis::make_unique< QgsCompoundCurve >();

const double startAngle = azimuth - angularWidth * 0.5;
const double endAngle = azimuth + angularWidth * 0.5;

const QgsPoint outerP1 = center.project( outerRadius, startAngle );
const QgsPoint outerP2 = center.project( outerRadius, endAngle );

const bool useShortestArc = angularWidth <= 180.0;

wedge->addCurve( new QgsCircularString( QgsCircularString::fromTwoPointsAndCenter( outerP1, outerP2, center, useShortestArc ) ) );

if ( !qgsDoubleNear( innerRadius, 0.0 ) && innerRadius > 0 )
{
const QgsPoint innerP1 = center.project( innerRadius, startAngle );
const QgsPoint innerP2 = center.project( innerRadius, endAngle );
wedge->addCurve( new QgsLineString( outerP2, innerP2 ) );
wedge->addCurve( new QgsCircularString( QgsCircularString::fromTwoPointsAndCenter( innerP2, innerP1, center, useShortestArc ) ) );
wedge->addCurve( new QgsLineString( innerP1, outerP1 ) );
}
else
{
wedge->addCurve( new QgsLineString( outerP2, center ) );
wedge->addCurve( new QgsLineString( center, outerP1 ) );
}

std::unique_ptr< QgsCurvePolygon > cp = qgis::make_unique< QgsCurvePolygon >();
cp->setExteriorRing( wedge.release() );
return QgsGeometry( std::move( cp ) );
}

void QgsGeometry::fromWkb( unsigned char *wkb, int length )
{
QgsConstWkbPtr ptr( wkb, length );
Expand Down
18 changes: 18 additions & 0 deletions src/core/geometry/qgsgeometry.h
Expand Up @@ -256,6 +256,24 @@ class CORE_EXPORT QgsGeometry
//! Creates a new multipart geometry from a list of QgsGeometry objects
static QgsGeometry collectGeometry( const QVector<QgsGeometry> &geometries );

/**
* Creates a wedge shaped buffer from a \a center point.
*
* The \a azimuth gives the angle (in degrees) for the middle of the wedge to point.
* The buffer width (in degrees) is specified by the \a angularWidth parameter. Note that the
* wedge will extend to half of the \a angularWidth either side of the \a azimuth direction.
*
* The outer radius of the buffer is specified via \a outerRadius, and optionally an
* \a innerRadius can also be specified.
*
* The returned geometry will be a CurvePolygon geometry containing circular strings. It may
* need to be segmentized to convert to a standard Polygon geometry.
*
* \since QGIS 3.2
*/
static QgsGeometry createWedgeBuffer( const QgsPoint &center, double azimuth, double angularWidth,
double outerRadius, double innerRadius = 0 );

/**
* Set the geometry, feeding in a geometry in GEOS format.
* This class will take ownership of the buffer.
Expand Down
29 changes: 29 additions & 0 deletions tests/src/python/test_qgsgeometry.py
Expand Up @@ -4356,6 +4356,35 @@ def testClipped(self):
self.assertTrue(compareWkt(result, exp, 0.00001),
"clipped: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))

def testCreateWedgeBuffer(self):
tests = [[QgsPoint(1, 11), 0, 45, 2, 0, 'CurvePolygon (CompoundCurve (CircularString (0.23463313526982044 12.84775906502257392, 1 13, 1.76536686473017967 12.84775906502257392),(1.76536686473017967 12.84775906502257392, 1 11),(1 11, 0.23463313526982044 12.84775906502257392)))'],
[QgsPoint(1, 11), 90, 45, 2, 0,
'CurvePolygon (CompoundCurve (CircularString (2.84775906502257348 11.76536686473017923, 3 11, 2.84775906502257348 10.23463313526982077),(2.84775906502257348 10.23463313526982077, 1 11),(1 11, 2.84775906502257348 11.76536686473017923)))'],
[QgsPoint(1, 11), 180, 90, 2, 0,
'CurvePolygon (CompoundCurve (CircularString (2.41421356237309492 9.58578643762690419, 1.00000000000000022 9, -0.41421356237309492 9.58578643762690419),(-0.41421356237309492 9.58578643762690419, 1 11),(1 11, 2.41421356237309492 9.58578643762690419)))'],
[QgsPoint(1, 11), 0, 200, 2, 0,
'CurvePolygon (CompoundCurve (CircularString (-0.96961550602441604 10.65270364466613984, 0.99999999999999956 13, 2.96961550602441626 10.65270364466613984),(2.96961550602441626 10.65270364466613984, 1 11),(1 11, -0.96961550602441604 10.65270364466613984)))'],
[QgsPoint(1, 11), 0, 45, 2, 1,
'CurvePolygon (CompoundCurve (CircularString (0.23463313526982044 12.84775906502257392, 1 13, 1.76536686473017967 12.84775906502257392),(1.76536686473017967 12.84775906502257392, 1.38268343236508984 11.92387953251128607),CircularString (1.38268343236508984 11.92387953251128607, 0.99999999999999978 12, 0.61731656763491016 11.92387953251128607),(0.61731656763491016 11.92387953251128607, 0.23463313526982044 12.84775906502257392)))'],
[QgsPoint(1, 11), 0, 200, 2, 1,
'CurvePolygon (CompoundCurve (CircularString (-0.96961550602441604 10.65270364466613984, 0.99999999999999956 13, 2.96961550602441626 10.65270364466613984),(2.96961550602441626 10.65270364466613984, 1.98480775301220813 10.82635182233306992),CircularString (1.98480775301220813 10.82635182233306992, 0.99999999999999978 12, 0.01519224698779198 10.82635182233306992),(0.01519224698779198 10.82635182233306992, -0.96961550602441604 10.65270364466613984)))'],
[QgsPoint(1, 11, 3), 0, 45, 2, 0,
'CurvePolygonZ (CompoundCurveZ (CircularStringZ (0.23463313526982044 12.84775906502257392 3, 1 13 3, 1.76536686473017967 12.84775906502257392 3),(1.76536686473017967 12.84775906502257392 3, 1 11 3),(1 11 3, 0.23463313526982044 12.84775906502257392 3)))'],
[QgsPoint(1, 11, m=3), 0, 45, 2, 0,
'CurvePolygonM (CompoundCurveM (CircularStringM (0.23463313526982044 12.84775906502257392 3, 1 13 3, 1.76536686473017967 12.84775906502257392 3),(1.76536686473017967 12.84775906502257392 3, 1 11 3),(1 11 3, 0.23463313526982044 12.84775906502257392 3)))']
]
for t in tests:
input = t[0]
azimuth = t[1]
width = t[2]
outer = t[3]
inner = t[4]
o = QgsGeometry.createWedgeBuffer(input, azimuth, width, outer, inner)
exp = t[5]
result = o.asWkt()
self.assertTrue(compareWkt(result, exp, 0.01),
"wedge buffer: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))

def testHausdorff(self):
tests = [["POLYGON((0 0, 0 2, 1 2, 2 2, 2 0, 0 0))", "POLYGON((0.5 0.5, 0.5 2.5, 1.5 2.5, 2.5 2.5, 2.5 0.5, 0.5 0.5))", 0.707106781186548],
["LINESTRING (0 0, 2 1)", "LINESTRING (0 0, 2 0)", 1],
Expand Down

0 comments on commit e047738

Please sign in to comment.