Skip to content

Commit ad3338c

Browse files
committedDec 2, 2018
Move forceRHR to QgsGeometry, avoid duplicate code
(cherry picked from commit c0cd6e6)
1 parent 320adf4 commit ad3338c

File tree

4 files changed

+77
-0
lines changed

4 files changed

+77
-0
lines changed
 

‎python/core/auto_generated/geometry/qgsgeometry.sip.in

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,6 +1562,15 @@ by calling `error()` on the returned geometry.
15621562

15631563

15641564
.. versionadded:: 3.0
1565+
%End
1566+
1567+
QgsGeometry forceRHR() const;
1568+
%Docstring
1569+
Forces geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon
1570+
is to the right of the boundary. In particular, the exterior ring is oriented in a clockwise direction
1571+
and the interior rings in a counter-clockwise direction.
1572+
1573+
.. versionadded:: 3.6
15651574
%End
15661575

15671576
class Error

‎src/core/geometry/qgsgeometry.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2412,6 +2412,47 @@ QgsGeometry QgsGeometry::makeValid() const
24122412
return result;
24132413
}
24142414

2415+
QgsGeometry QgsGeometry::forceRHR() const
2416+
{
2417+
if ( !d->geometry )
2418+
return QgsGeometry();
2419+
2420+
if ( isMultipart() )
2421+
{
2422+
const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( d->geometry.get() );
2423+
std::unique_ptr< QgsGeometryCollection > newCollection( collection->createEmptyWithSameType() );
2424+
for ( int i = 0; i < collection->numGeometries(); ++i )
2425+
{
2426+
const QgsAbstractGeometry *g = collection->geometryN( i );
2427+
if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( g ) )
2428+
{
2429+
std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
2430+
corrected->forceRHR();
2431+
newCollection->addGeometry( corrected.release() );
2432+
}
2433+
else
2434+
{
2435+
newCollection->addGeometry( g->clone() );
2436+
}
2437+
}
2438+
return QgsGeometry( std::move( newCollection ) );
2439+
}
2440+
else
2441+
{
2442+
if ( const QgsCurvePolygon *cp = qgsgeometry_cast< const QgsCurvePolygon * >( d->geometry.get() ) )
2443+
{
2444+
std::unique_ptr< QgsCurvePolygon > corrected( cp->clone() );
2445+
corrected->forceRHR();
2446+
return QgsGeometry( std::move( corrected ) );
2447+
}
2448+
else
2449+
{
2450+
// not a curve polygon, so return unchanged
2451+
return *this;
2452+
}
2453+
}
2454+
}
2455+
24152456

24162457
void QgsGeometry::validateGeometry( QVector<QgsGeometry::Error> &errors, ValidationMethod method ) const
24172458
{

‎src/core/geometry/qgsgeometry.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,6 +1550,15 @@ class CORE_EXPORT QgsGeometry
15501550
*/
15511551
QgsGeometry makeValid() const;
15521552

1553+
/**
1554+
* Forces geometries to respect the Right-Hand-Rule, in which the area that is bounded by a polygon
1555+
* is to the right of the boundary. In particular, the exterior ring is oriented in a clockwise direction
1556+
* and the interior rings in a counter-clockwise direction.
1557+
*
1558+
* \since QGIS 3.6
1559+
*/
1560+
QgsGeometry forceRHR() const;
1561+
15531562
/**
15541563
* \ingroup core
15551564
*/

‎tests/src/python/test_qgsgeometry.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4611,6 +4611,24 @@ def testOffsetCurve(self):
46114611
"mismatch for {} to {}, expected:\n{}\nGot:\n{}\n".format(t[0], t[1],
46124612
t[2], res.asWkt(1)))
46134613

4614+
def testForceRHR(self):
4615+
tests = [
4616+
["", ""],
4617+
["Point (100 100)", "Point (100 100)"],
4618+
["LINESTRING (0 0, 0 100, 100 100)", "LineString (0 0, 0 100, 100 100)"],
4619+
["LINESTRING (100 100, 0 100, 0 0)", "LineString (100 100, 0 100, 0 0)"],
4620+
["POLYGON((-1 -1, 4 0, 4 2, 0 2, -1 -1))", "Polygon ((-1 -1, 0 2, 4 2, 4 0, -1 -1))"],
4621+
["MULTIPOLYGON(Polygon((-1 -1, 4 0, 4 2, 0 2, -1 -1)),Polygon((100 100, 200 100, 200 200, 100 200, 100 100)))", "MultiPolygon (((-1 -1, 0 2, 4 2, 4 0, -1 -1)),((100 100, 100 200, 200 200, 200 100, 100 100)))"],
4622+
[
4623+
"GeometryCollection(Polygon((-1 -1, 4 0, 4 2, 0 2, -1 -1)),Polygon((100 100, 200 100, 200 200, 100 200, 100 100)))",
4624+
"GeometryCollection (Polygon ((-1 -1, 0 2, 4 2, 4 0, -1 -1)),Polygon ((100 100, 100 200, 200 200, 200 100, 100 100)))"]
4625+
]
4626+
for t in tests:
4627+
g1 = QgsGeometry.fromWkt(t[0])
4628+
res = g1.forceRHR()
4629+
self.assertEqual(res.asWkt(1), t[1],
4630+
"mismatch for {}, expected:\n{}\nGot:\n{}\n".format(t[0], t[1], res.asWkt(1)))
4631+
46144632
def renderGeometry(self, geom, use_pen, as_polygon=False, as_painter_path=False):
46154633
image = QImage(200, 200, QImage.Format_RGB32)
46164634
image.fill(QColor(0, 0, 0))

0 commit comments

Comments
 (0)
Please sign in to comment.