Skip to content

Commit 6d8a479

Browse files
committedDec 2, 2018
Add method to QgsCurvePolygon to force RHR, ensuring standard ring orientation
(cherry picked from commit 158283d)
1 parent 73cf308 commit 6d8a479

File tree

4 files changed

+68
-0
lines changed

4 files changed

+68
-0
lines changed
 

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,17 @@ For example, this removes unclosed rings and rings with less than 4 vertices.
136136
.. versionadded:: 3.0
137137
%End
138138

139+
140+
void forceRHR();
141+
%Docstring
142+
Forces the geometry to respect the Right-Hand-Rule, in which the area that is
143+
bounded by the polygon is to the right of the boundary. In particular, the exterior
144+
ring is oriented in a clockwise direction and the interior rings in a counter-clockwise
145+
direction.
146+
147+
.. versionadded:: 3.6
148+
%End
149+
139150
virtual void draw( QPainter &p ) const;
140151

141152
virtual void transform( const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d = QgsCoordinateTransform::ForwardTransform, bool transformZ = false ) throw( QgsCsException );

‎src/core/geometry/qgscurvepolygon.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,33 @@ void QgsCurvePolygon::removeInvalidRings()
705705
mInteriorRings = validRings;
706706
}
707707

708+
void QgsCurvePolygon::forceRHR()
709+
{
710+
if ( mExteriorRing && mExteriorRing->orientation() != QgsCurve::Clockwise )
711+
{
712+
// flip exterior ring orientation
713+
std::unique_ptr< QgsCurve > flipped( mExteriorRing->reversed() );
714+
mExteriorRing = std::move( flipped );
715+
}
716+
717+
QVector<QgsCurve *> validRings;
718+
for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
719+
{
720+
if ( curve && curve->orientation() != QgsCurve::CounterClockwise )
721+
{
722+
// flip interior ring orientation
723+
QgsCurve *flipped = curve->reversed();
724+
validRings << flipped;
725+
delete curve;
726+
}
727+
else
728+
{
729+
validRings << curve;
730+
}
731+
}
732+
mInteriorRings = validRings;
733+
}
734+
708735
void QgsCurvePolygon::draw( QPainter &p ) const
709736
{
710737
if ( !mExteriorRing )

‎src/core/geometry/qgscurvepolygon.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,17 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
133133
*/
134134
void removeInvalidRings();
135135

136+
137+
/**
138+
* Forces the geometry to respect the Right-Hand-Rule, in which the area that is
139+
* bounded by the polygon is to the right of the boundary. In particular, the exterior
140+
* ring is oriented in a clockwise direction and the interior rings in a counter-clockwise
141+
* direction.
142+
*
143+
* \since QGIS 3.6
144+
*/
145+
void forceRHR();
146+
136147
void draw( QPainter &p ) const override;
137148
void transform( const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection d = QgsCoordinateTransform::ForwardTransform, bool transformZ = false ) override SIP_THROW( QgsCsException );
138149
void transform( const QTransform &t, double zTranslate = 0.0, double zScale = 1.0, double mTranslate = 0.0, double mScale = 1.0 ) override;

‎tests/src/core/testqgsgeometry.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6624,6 +6624,25 @@ void TestQgsGeometry::polygon()
66246624
invalidRingPolygon.addInteriorRing( removeInvalidPolygonRing.clone() );
66256625
invalidRingPolygon.removeInvalidRings();
66266626
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))" ) );
6627+
6628+
// force RHR
6629+
QgsPolygon rhr;
6630+
rhr.forceRHR(); // no crash
6631+
rhr.fromWkt( QStringLiteral( "PolygonZM ((0 0 3 4, 0 100 13 14, 100 100 13 14, 100 0 23 24, 0 0 3 4))" ) );
6632+
rhr.forceRHR();
6633+
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))" ) );
6634+
rhr.fromWkt( QStringLiteral( "PolygonZM ((0 0 3 4, 100 0 13 14, 100 100 23 24, 0 100 23 24, 0 0 3 4))" ) );
6635+
rhr.forceRHR();
6636+
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))" ) );
6637+
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))" ) );
6638+
rhr.forceRHR();
6639+
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))" ) );
6640+
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))" ) );
6641+
rhr.forceRHR();
6642+
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))" ) );
6643+
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))" ) );
6644+
rhr.forceRHR();
6645+
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))" ) );
66276646
}
66286647

66296648
void TestQgsGeometry::triangle()

0 commit comments

Comments
 (0)
Please sign in to comment.