Skip to content

Commit

Permalink
Add method to QgsCurvePolygon to force RHR, ensuring standard ring or…
Browse files Browse the repository at this point in the history
…ientation

(cherry picked from commit 158283d)
  • Loading branch information
nyalldawson committed Dec 2, 2018
1 parent 73cf308 commit 6d8a479
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 0 deletions.
11 changes: 11 additions & 0 deletions python/core/auto_generated/geometry/qgscurvepolygon.sip.in
Expand Up @@ -136,6 +136,17 @@ For example, this removes unclosed rings and rings with less than 4 vertices.
.. versionadded:: 3.0
%End


void forceRHR();
%Docstring
Forces the geometry to respect the Right-Hand-Rule, in which the area that is
bounded by the polygon is to the right of the boundary. In particular, the exterior
ring is oriented in a clockwise direction and the interior rings in a counter-clockwise
direction.

.. versionadded:: 3.6
%End

virtual void draw( QPainter &p ) const;

virtual void transform( const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d = QgsCoordinateTransform::ForwardTransform, bool transformZ = false ) throw( QgsCsException );
Expand Down
27 changes: 27 additions & 0 deletions src/core/geometry/qgscurvepolygon.cpp
Expand Up @@ -705,6 +705,33 @@ void QgsCurvePolygon::removeInvalidRings()
mInteriorRings = validRings;
}

void QgsCurvePolygon::forceRHR()
{
if ( mExteriorRing && mExteriorRing->orientation() != QgsCurve::Clockwise )
{
// flip exterior ring orientation
std::unique_ptr< QgsCurve > flipped( mExteriorRing->reversed() );
mExteriorRing = std::move( flipped );
}

QVector<QgsCurve *> validRings;
for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
{
if ( curve && curve->orientation() != QgsCurve::CounterClockwise )
{
// flip interior ring orientation
QgsCurve *flipped = curve->reversed();
validRings << flipped;
delete curve;
}
else
{
validRings << curve;
}
}
mInteriorRings = validRings;
}

void QgsCurvePolygon::draw( QPainter &p ) const
{
if ( !mExteriorRing )
Expand Down
11 changes: 11 additions & 0 deletions src/core/geometry/qgscurvepolygon.h
Expand Up @@ -133,6 +133,17 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
*/
void removeInvalidRings();


/**
* Forces the geometry to respect the Right-Hand-Rule, in which the area that is
* bounded by the polygon is to the right of the boundary. In particular, the exterior
* ring is oriented in a clockwise direction and the interior rings in a counter-clockwise
* direction.
*
* \since QGIS 3.6
*/
void forceRHR();

void draw( QPainter &p ) const override;
void transform( const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d = QgsCoordinateTransform::ForwardTransform, bool transformZ = false ) override SIP_THROW( QgsCsException );
void transform( const QTransform &t, double zTranslate = 0.0, double zScale = 1.0, double mTranslate = 0.0, double mScale = 1.0 ) override;
Expand Down
19 changes: 19 additions & 0 deletions tests/src/core/testqgsgeometry.cpp
Expand Up @@ -6624,6 +6624,25 @@ void TestQgsGeometry::polygon()
invalidRingPolygon.addInteriorRing( removeInvalidPolygonRing.clone() );
invalidRingPolygon.removeInvalidRings();
QCOMPARE( invalidRingPolygon.asWkt( 2 ), QStringLiteral( "PolygonZM ((11 2 3 4, 4 12 13 14, 11 12 13 14, 11 22 23 24, 11 2 3 4),(1 2 5 6, 11.01 2.01 15 16, 11 2.01 25 26, 1 2 5 6))" ) );

// force RHR
QgsPolygon rhr;
rhr.forceRHR(); // no crash
rhr.fromWkt( QStringLiteral( "PolygonZM ((0 0 3 4, 0 100 13 14, 100 100 13 14, 100 0 23 24, 0 0 3 4))" ) );
rhr.forceRHR();
QCOMPARE( rhr.asWkt( 2 ), QStringLiteral( "PolygonZM ((0 0 3 4, 0 100 13 14, 100 100 13 14, 100 0 23 24, 0 0 3 4))" ) );
rhr.fromWkt( QStringLiteral( "PolygonZM ((0 0 3 4, 100 0 13 14, 100 100 23 24, 0 100 23 24, 0 0 3 4))" ) );
rhr.forceRHR();
QCOMPARE( rhr.asWkt( 2 ), QStringLiteral( "PolygonZM ((0 0 3 4, 0 100 23 24, 100 100 23 24, 100 0 13 14, 0 0 3 4))" ) );
rhr.fromWkt( QStringLiteral( "PolygonZM ((0 0 3 4, 0 100 13 14, 100 100 13 14, 100 0 23 24, 0 0 3 4),(10 10 1 2, 20 10 3 4, 20 20 4, 5, 10 20 6 8, 10 10 1 2))" ) );
rhr.forceRHR();
QCOMPARE( rhr.asWkt( 2 ), QStringLiteral( "PolygonZM ((0 0 3 4, 0 100 13 14, 100 100 13 14, 100 0 23 24, 0 0 3 4),(10 10 1 2, 20 10 3 4, 10 20 6 8, 10 10 1 2))" ) );
rhr.fromWkt( QStringLiteral( "PolygonZM ((0 0 3 4, 100 0 13 14, 100 100 13 14, 0 100 23 24, 0 0 3 4),(10 10 1 2, 20 10 3 4, 20 20 4, 5, 10 20 6 8, 10 10 1 2))" ) );
rhr.forceRHR();
QCOMPARE( rhr.asWkt( 2 ), QStringLiteral( "PolygonZM ((0 0 3 4, 0 100 23 24, 100 100 13 14, 100 0 13 14, 0 0 3 4),(10 10 1 2, 20 10 3 4, 10 20 6 8, 10 10 1 2))" ) );
rhr.fromWkt( QStringLiteral( "PolygonZM ((0 0 3 4, 0 100 13 14, 100 100 13 14, 100 0 23 24, 0 0 3 4),(10 10 1 2, 10 20 3 4, 20 10 6 8, 10 10 1 2))" ) );
rhr.forceRHR();
QCOMPARE( rhr.asWkt( 2 ), QStringLiteral( "PolygonZM ((0 0 3 4, 0 100 13 14, 100 100 13 14, 100 0 23 24, 0 0 3 4),(10 10 1 2, 20 10 6 8, 10 20 3 4, 10 10 1 2))" ) );
}

void TestQgsGeometry::triangle()
Expand Down

0 comments on commit 6d8a479

Please sign in to comment.