Skip to content

Commit

Permalink
QgsGeometryUtils::segmentizeArc(): fix full circle segmentization (fixes
Browse files Browse the repository at this point in the history
 #29895)

This fix display of full circles coming from PostGIS.
  • Loading branch information
rouault authored and nyalldawson committed May 30, 2019
1 parent 1956b8e commit 33e27e0
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 21 deletions.
6 changes: 4 additions & 2 deletions src/core/geometry/qgsgeometryutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,8 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co
if ( symmetric )
{
double angle = a3 - a1;
if ( angle < 0 ) angle += M_PI * 2;
// angle == 0 when full circle
if ( angle <= 0 ) angle += M_PI * 2;

/* Number of segments in output */
int segs = ceil( angle / increment );
Expand All @@ -879,7 +880,8 @@ void QgsGeometryUtils::segmentizeArc( const QgsPoint &p1, const QgsPoint &p2, co
}

/* Adjust a3 up so we can increment from a1 to a3 cleanly */
if ( a3 < a1 )
// a3 == a1 when full circle
if ( a3 <= a1 )
a3 += 2.0 * M_PI;
if ( a2 < a1 )
a2 += 2.0 * M_PI;
Expand Down
36 changes: 17 additions & 19 deletions tests/src/core/testqgsgeometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13702,66 +13702,64 @@ void TestQgsGeometry::multiSurface()

//as JSON
QgsMultiSurface exportC;
ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7, 17 ) << QgsPoint( QgsWkbTypes::Point, 3, 13 ) << QgsPoint( QgsWkbTypes::Point, 7, 17 ) ) ;
QgsLineString lineRing;
lineRing.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7, 17 ) << QgsPoint( QgsWkbTypes::Point, 7, 13 ) << QgsPoint( QgsWkbTypes::Point, 3, 13 ) << QgsPoint( QgsWkbTypes::Point, 7, 17 ) ) ;
part.clear();
part.setExteriorRing( ring.clone() );
part.setExteriorRing( lineRing.clone() );
exportC.addGeometry( part.clone() );
ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 27, 37 ) << QgsPoint( QgsWkbTypes::Point, 43, 43 ) << QgsPoint( QgsWkbTypes::Point, 27, 37 ) ) ;
lineRing.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 27, 37 ) << QgsPoint( QgsWkbTypes::Point, 27, 43 ) << QgsPoint( QgsWkbTypes::Point, 43, 43 ) << QgsPoint( QgsWkbTypes::Point, 27, 37 ) ) ;
part.clear();
part.setExteriorRing( ring.clone() );
part.setExteriorRing( lineRing.clone() );
exportC.addGeometry( part.clone() );

// GML document for compare
QDomDocument doc( "gml" );

// as GML2
QString expectedSimpleGML2( QStringLiteral( "<MultiPolygon xmlns=\"gml\"><polygonMember xmlns=\"gml\"><Polygon xmlns=\"gml\"><outerBoundaryIs xmlns=\"gml\"><LinearRing xmlns=\"gml\"><coordinates xmlns=\"gml\" cs=\",\" ts=\" \">7,17 7,17</coordinates></LinearRing></outerBoundaryIs></Polygon></polygonMember><polygonMember xmlns=\"gml\"><Polygon xmlns=\"gml\"><outerBoundaryIs xmlns=\"gml\"><LinearRing xmlns=\"gml\"><coordinates xmlns=\"gml\" cs=\",\" ts=\" \">27,37 27,37</coordinates></LinearRing></outerBoundaryIs></Polygon></polygonMember></MultiPolygon>" ) );
QString expectedSimpleGML2( QStringLiteral( "<MultiPolygon xmlns=\"gml\"><polygonMember xmlns=\"gml\"><Polygon xmlns=\"gml\"><outerBoundaryIs xmlns=\"gml\"><LinearRing xmlns=\"gml\"><coordinates xmlns=\"gml\" cs=\",\" ts=\" \">7,17 7,13 3,13 7,17</coordinates></LinearRing></outerBoundaryIs></Polygon></polygonMember><polygonMember xmlns=\"gml\"><Polygon xmlns=\"gml\"><outerBoundaryIs xmlns=\"gml\"><LinearRing xmlns=\"gml\"><coordinates xmlns=\"gml\" cs=\",\" ts=\" \">27,37 27,43 43,43 27,37</coordinates></LinearRing></outerBoundaryIs></Polygon></polygonMember></MultiPolygon>" ) );
QString res = elemToString( exportC.asGml2( doc, 1 ) );
QGSCOMPAREGML( res, expectedSimpleGML2 );
QString expectedGML2empty( QStringLiteral( "<MultiPolygon xmlns=\"gml\"/>" ) );
QGSCOMPAREGML( elemToString( QgsMultiSurface().asGml2( doc ) ), expectedGML2empty );

//as GML3
QString expectedSimpleGML3( QStringLiteral( "<MultiSurface xmlns=\"gml\"><surfaceMember xmlns=\"gml\"><Polygon xmlns=\"gml\"><exterior xmlns=\"gml\"><Curve xmlns=\"gml\"><segments xmlns=\"gml\"><ArcString xmlns=\"gml\"><posList xmlns=\"gml\" srsDimension=\"2\">7 17 3 13 7 17</posList></ArcString></segments></Curve></exterior></Polygon></surfaceMember><surfaceMember xmlns=\"gml\"><Polygon xmlns=\"gml\"><exterior xmlns=\"gml\"><Curve xmlns=\"gml\"><segments xmlns=\"gml\"><ArcString xmlns=\"gml\"><posList xmlns=\"gml\" srsDimension=\"2\">27 37 43 43 27 37</posList></ArcString></segments></Curve></exterior></Polygon></surfaceMember></MultiSurface>" ) );

QString expectedSimpleGML3( QStringLiteral( "<MultiSurface xmlns=\"gml\"><surfaceMember xmlns=\"gml\"><Polygon xmlns=\"gml\"><exterior xmlns=\"gml\"><LinearRing xmlns=\"gml\"><posList xmlns=\"gml\" srsDimension=\"2\">7 17 7 13 3 13 7 17</posList></LinearRing></exterior></Polygon></surfaceMember><surfaceMember xmlns=\"gml\"><Polygon xmlns=\"gml\"><exterior xmlns=\"gml\"><LinearRing xmlns=\"gml\"><posList xmlns=\"gml\" srsDimension=\"2\">27 37 27 43 43 43 27 37</posList></LinearRing></exterior></Polygon></surfaceMember></MultiSurface>" ) );
res = elemToString( exportC.asGml3( doc ) );
QCOMPARE( res, expectedSimpleGML3 );
QString expectedGML3empty( QStringLiteral( "<MultiSurface xmlns=\"gml\"/>" ) );
QGSCOMPAREGML( elemToString( QgsMultiSurface().asGml3( doc ) ), expectedGML3empty );

// as JSON
QString expectedSimpleJson( "{\"type\": \"MultiPolygon\", \"coordinates\": [[[ [7, 17], [7, 17]]], [[ [27, 37], [27, 37]]]] }" );
QString expectedSimpleJson( "{\"type\": \"MultiPolygon\", \"coordinates\": [[[ [7, 17], [7, 13], [3, 13], [7, 17]]], [[ [27, 37], [27, 43], [43, 43], [27, 37]]]] }" );
res = exportC.asJson( 1 );
QCOMPARE( res, expectedSimpleJson );

ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 17, 27 ) << QgsPoint( QgsWkbTypes::Point, 18, 28 ) << QgsPoint( QgsWkbTypes::Point, 17, 27 ) ) ;
part.addInteriorRing( ring.clone() );
lineRing.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 17, 27 ) << QgsPoint( QgsWkbTypes::Point, 17, 28 ) << QgsPoint( QgsWkbTypes::Point, 18, 28 ) << QgsPoint( QgsWkbTypes::Point, 17, 27 ) ) ;
part.addInteriorRing( lineRing.clone() );
exportC.addGeometry( part.clone() );

QString expectedJsonWithRings( "{\"type\": \"MultiPolygon\", \"coordinates\": [[[ [7, 17], [7, 17]]], [[ [27, 37], [27, 37]]], [[ [27, 37], [27, 37]], [ [17, 27], [17, 27]]]] }" );
QString expectedJsonWithRings( "{\"type\": \"MultiPolygon\", \"coordinates\": [[[ [7, 17], [7, 13], [3, 13], [7, 17]]], [[ [27, 37], [27, 43], [43, 43], [27, 37]]], [[ [27, 37], [27, 43], [43, 43], [27, 37]], [ [17, 27], [17, 28], [18, 28], [17, 27]]]] }" );
res = exportC.asJson( 1 );
QCOMPARE( res, expectedJsonWithRings );

QgsMultiSurface exportFloat;
ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7 / 3.0, 17 / 3.0 ) << QgsPoint( QgsWkbTypes::Point, 3 / 5.0, 13 / 3.0 ) << QgsPoint( QgsWkbTypes::Point, 7 / 3.0, 17 / 3.0 ) ) ;
lineRing.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 0.1234, 0.1234 ) << QgsPoint( QgsWkbTypes::Point, 0.1234, 1.2344 ) << QgsPoint( QgsWkbTypes::Point, 1.2344, 1.2344 ) << QgsPoint( QgsWkbTypes::Point, 0.1234, 0.1234 ) ) ;
part.clear();
part.setExteriorRing( ring.clone() );
exportFloat.addGeometry( part.clone() );
ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 27 / 3.0, 37 / 9.0 ) << QgsPoint( QgsWkbTypes::Point, 43 / 41.0, 43 / 42.0 ) << QgsPoint( QgsWkbTypes::Point, 27 / 3.0, 37 / 9.0 ) ) ;
part.clear();
part.setExteriorRing( ring.clone() );
part.setExteriorRing( lineRing.clone() );
exportFloat.addGeometry( part.clone() );

QString expectedJsonPrec3( QStringLiteral( "{\"type\": \"MultiPolygon\", \"coordinates\": [[[ [2.333, 5.667], [2.333, 5.667]]], [[ [9, 4.111], [9, 4.111]]]] }" ) );
QString expectedJsonPrec3( QStringLiteral( "{\"type\": \"MultiPolygon\", \"coordinates\": [[[ [0.123, 0.123], [0.123, 1.234], [1.234, 1.234], [0.123, 0.123]]]] }" ) );
res = exportFloat.asJson( 3 );
QCOMPARE( res, expectedJsonPrec3 );

// as GML2
QString expectedGML2prec3( QStringLiteral( "<MultiPolygon xmlns=\"gml\"><polygonMember xmlns=\"gml\"><Polygon xmlns=\"gml\"><outerBoundaryIs xmlns=\"gml\"><LinearRing xmlns=\"gml\"><coordinates xmlns=\"gml\" cs=\",\" ts=\" \">2.333,5.667 2.333,5.667</coordinates></LinearRing></outerBoundaryIs></Polygon></polygonMember><polygonMember xmlns=\"gml\"><Polygon xmlns=\"gml\"><outerBoundaryIs xmlns=\"gml\"><LinearRing xmlns=\"gml\"><coordinates xmlns=\"gml\" cs=\",\" ts=\" \">9,4.111 9,4.111</coordinates></LinearRing></outerBoundaryIs></Polygon></polygonMember></MultiPolygon>" ) );
QString expectedGML2prec3( QStringLiteral( "<MultiPolygon xmlns=\"gml\"><polygonMember xmlns=\"gml\"><Polygon xmlns=\"gml\"><outerBoundaryIs xmlns=\"gml\"><LinearRing xmlns=\"gml\"><coordinates xmlns=\"gml\" cs=\",\" ts=\" \">0.123,0.123 0.123,1.234 1.234,1.234 0.123,0.123</coordinates></LinearRing></outerBoundaryIs></Polygon></polygonMember></MultiPolygon>" ) );
res = elemToString( exportFloat.asGml2( doc, 3 ) );
QGSCOMPAREGML( res, expectedGML2prec3 );

//as GML3
QString expectedGML3prec3( QStringLiteral( "<MultiSurface xmlns=\"gml\"><surfaceMember xmlns=\"gml\"><Polygon xmlns=\"gml\"><exterior xmlns=\"gml\"><Curve xmlns=\"gml\"><segments xmlns=\"gml\"><ArcString xmlns=\"gml\"><posList xmlns=\"gml\" srsDimension=\"2\">2.333 5.667 0.6 4.333 2.333 5.667</posList></ArcString></segments></Curve></exterior></Polygon></surfaceMember><surfaceMember xmlns=\"gml\"><Polygon xmlns=\"gml\"><exterior xmlns=\"gml\"><Curve xmlns=\"gml\"><segments xmlns=\"gml\"><ArcString xmlns=\"gml\"><posList xmlns=\"gml\" srsDimension=\"2\">9 4.111 1.049 1.024 9 4.111</posList></ArcString></segments></Curve></exterior></Polygon></surfaceMember></MultiSurface>" ) );
QString expectedGML3prec3( QStringLiteral( "<MultiSurface xmlns=\"gml\"><surfaceMember xmlns=\"gml\"><Polygon xmlns=\"gml\"><exterior xmlns=\"gml\"><LinearRing xmlns=\"gml\"><posList xmlns=\"gml\" srsDimension=\"2\">0.123 0.123 0.123 1.234 1.234 1.234 0.123 0.123</posList></LinearRing></exterior></Polygon></surfaceMember></MultiSurface>" ) );
res = elemToString( exportFloat.asGml3( doc, 3 ) );
QCOMPARE( res, expectedGML3prec3 );

Expand Down
74 changes: 74 additions & 0 deletions tests/src/core/testqgsgeometryutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
* *
***************************************************************************/

#include <math.h>

#include "qgstest.h"
#include <QObject>
#include "qgsgeometryutils.h"
Expand Down Expand Up @@ -70,6 +72,9 @@ class TestQgsGeometryUtils: public QObject
void testInterpolatePointOnLineByValue();
void testPointOnLineWithDistance();
void interpolatePointOnArc();
void testSegmentizeArcHalfCircle();
void testSegmentizeArcHalfCircleOtherDirection();
void testSegmentizeArcFullCircle();
};


Expand Down Expand Up @@ -1231,5 +1236,74 @@ void TestQgsGeometryUtils::interpolatePointOnArc()
QGSCOMPARENEAR( p.y(), -2.0, 0.00001 );
}

void TestQgsGeometryUtils::testSegmentizeArcHalfCircle()
{
QgsPointSequence points;
const double xoff = 1;
const double yoff = 100;
QgsGeometryUtils::segmentizeArc( QgsPoint( xoff + 0, yoff + 0 ),
QgsPoint( xoff + 1, yoff + 1 ),
QgsPoint( xoff + 2, yoff + 0 ),
points, 0.1,
QgsAbstractGeometry::MaximumDifference, false, false );
QCOMPARE( points.size(), 5 );
QGSCOMPARENEAR( points[0].x(), xoff + 0.0, 0.00001 );
QGSCOMPARENEAR( points[0].y(), yoff + 0.0, 0.00001 );
QGSCOMPARENEAR( points[1].x(), xoff + 1 - sqrt( 2 ) / 2, 0.00001 );
QGSCOMPARENEAR( points[1].y(), yoff + sqrt( 2 ) / 2, 0.00001 );
QGSCOMPARENEAR( points[2].x(), xoff + 1.0, 0.00001 );
QGSCOMPARENEAR( points[2].y(), yoff + 1.0, 0.00001 );
QGSCOMPARENEAR( points[3].x(), xoff + 1 + sqrt( 2 ) / 2, 0.00001 );
QGSCOMPARENEAR( points[3].y(), yoff + sqrt( 2 ) / 2, 0.00001 );
QGSCOMPARENEAR( points[4].x(), xoff + 2.0, 0.00001 );
QGSCOMPARENEAR( points[4].y(), yoff + 0.0, 0.00001 );
}

void TestQgsGeometryUtils::testSegmentizeArcHalfCircleOtherDirection()
{
QgsPointSequence points;
const double xoff = 1;
const double yoff = 100;
QgsGeometryUtils::segmentizeArc( QgsPoint( xoff + 0, yoff + 0 ),
QgsPoint( xoff + 1, yoff - 1 ),
QgsPoint( xoff + 2, yoff + 0 ),
points, 0.1,
QgsAbstractGeometry::MaximumDifference, false, false );
QCOMPARE( points.size(), 5 );
QGSCOMPARENEAR( points[0].x(), xoff + 0.0, 0.00001 );
QGSCOMPARENEAR( points[0].y(), yoff + 0.0, 0.00001 );
QGSCOMPARENEAR( points[1].x(), xoff + 1 - sqrt( 2 ) / 2, 0.00001 );
QGSCOMPARENEAR( points[1].y(), yoff + -sqrt( 2 ) / 2, 0.00001 );
QGSCOMPARENEAR( points[2].x(), xoff + 1.0, 0.00001 );
QGSCOMPARENEAR( points[2].y(), yoff + -1.0, 0.00001 );
QGSCOMPARENEAR( points[3].x(), xoff + 1 + sqrt( 2 ) / 2, 0.00001 );
QGSCOMPARENEAR( points[3].y(), yoff + -sqrt( 2 ) / 2, 0.00001 );
QGSCOMPARENEAR( points[4].x(), xoff + 2.0, 0.00001 );
QGSCOMPARENEAR( points[4].y(), yoff + 0.0, 0.00001 );
}

void TestQgsGeometryUtils::testSegmentizeArcFullCircle()
{
QgsPointSequence points;
const double xoff = 1;
const double yoff = 100;
QgsGeometryUtils::segmentizeArc( QgsPoint( xoff + 0, yoff + 0 ),
QgsPoint( xoff + 2, yoff + 0 ),
QgsPoint( xoff + 0, yoff + 0 ),
points, 0.4,
QgsAbstractGeometry::MaximumDifference, false, false );
QCOMPARE( points.size(), 5 );
QGSCOMPARENEAR( points[0].x(), xoff + 0.0, 0.00001 );
QGSCOMPARENEAR( points[0].y(), yoff + 0.0, 0.00001 );
QGSCOMPARENEAR( points[1].x(), xoff + 1.0, 0.00001 );
QGSCOMPARENEAR( points[1].y(), yoff + -1.0, 0.00001 );
QGSCOMPARENEAR( points[2].x(), xoff + 2.0, 0.00001 );
QGSCOMPARENEAR( points[2].y(), yoff + 0.0, 0.00001 );
QGSCOMPARENEAR( points[3].x(), xoff + 1.0, 0.00001 );
QGSCOMPARENEAR( points[3].y(), yoff + 1.0, 0.00001 );
QGSCOMPARENEAR( points[4].x(), xoff + 0.0, 0.00001 );
QGSCOMPARENEAR( points[4].y(), yoff + 0.0, 0.00001 );
}

QGSTEST_MAIN( TestQgsGeometryUtils )
#include "testqgsgeometryutils.moc"

0 comments on commit 33e27e0

Please sign in to comment.