From 79fb064c99712b332e39177a0efef82a288a2483 Mon Sep 17 00:00:00 2001 From: lbartoletti Date: Tue, 26 Sep 2017 21:23:53 +0200 Subject: [PATCH] resolve conflicts --- tests/src/core/testqgsgeometry.cpp | 9878 +++++++++++++++++++++++++++- 1 file changed, 9777 insertions(+), 101 deletions(-) diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index 79442610104..fe057d9f268 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -44,6 +44,7 @@ #include "qgscircularstring.h" #include "qgsgeometrycollection.h" #include "qgsgeometryfactory.h" +#include "qgscurvepolygon.h" //qgs unit test utility class #include "qgsrenderchecker.h" @@ -72,14 +73,19 @@ class TestQgsGeometry : public QObject // geometry types void point(); //test QgsPointV2 void lineString(); //test QgsLineString + void circularString(); void polygon(); //test QgsPolygonV2 + void curvePolygon(); void triangle(); + void triangle2(); void circle(); void ellipse(); void regularPolygon(); void compoundCurve(); //test QgsCompoundCurve void multiPoint(); void multiLineString(); + void multiCurve(); + void multiSurface(); void multiPolygon(); void geometryCollection(); @@ -131,7 +137,7 @@ class TestQgsGeometry : public QObject private: //! A helper method to do a render check to see if the geometry op is as expected - bool renderCheck( const QString &testName, const QString &comment = QLatin1String( QLatin1String( "" ) ), int mismatchCount = 0 ); + bool renderCheck( const QString &testName, const QString &comment = QString(), int mismatchCount = 0 ); //! A helper method to dump to qdebug the geometry of a multipolygon void dumpMultiPolygon( QgsMultiPolygon &multiPolygon ); //! A helper method to dump to qdebug the geometry of a polygon @@ -477,6 +483,20 @@ void TestQgsGeometry::point() QCOMPARE( p7.wkbType(), QgsWkbTypes::PointZM ); QCOMPARE( p7.wktTypeStr(), QString( "PointZM" ) ); + QgsPoint noZ( QgsWkbTypes::PointM, 11.0, 13.0, 15.0, 17.0 ); + QCOMPARE( noZ.x(), 11.0 ); + QCOMPARE( noZ.y(), 13.0 ); + QVERIFY( std::isnan( noZ.z() ) ); + QCOMPARE( noZ.m(), 17.0 ); + QCOMPARE( noZ.wkbType(), QgsWkbTypes::PointM ); + + QgsPoint noM( QgsWkbTypes::PointZ, 11.0, 13.0, 17.0, 18.0 ); + QCOMPARE( noM.x(), 11.0 ); + QCOMPARE( noM.y(), 13.0 ); + QVERIFY( std::isnan( noM.m() ) ); + QCOMPARE( noM.z(), 17.0 ); + QCOMPARE( noM.wkbType(), QgsWkbTypes::PointZ ); + QgsPoint p8( QgsWkbTypes::Point25D, 21.0, 23.0, 25.0 ); QCOMPARE( p8.x(), 21.0 ); QCOMPARE( p8.y(), 23.0 ); @@ -562,6 +582,10 @@ void TestQgsGeometry::point() std::unique_ptr< QgsPoint >clone( p10.clone() ); QVERIFY( p10 == *clone ); + //toCurveType + clone.reset( p10.toCurveType() ); + QVERIFY( p10 == *clone ); + //assignment QgsPoint original( QgsWkbTypes::PointZM, 1.0, 2.0, 3.0, -4.0 ); QgsPoint assigned( 6.0, 7.0 ); @@ -611,6 +635,7 @@ void TestQgsGeometry::point() //bad WKT QVERIFY( !p14.fromWkt( "Polygon()" ) ); + QVERIFY( !p14.fromWkt( "Point(1 )" ) ); //asGML2 QgsPoint exportPoint( 1, 2 ); @@ -626,6 +651,9 @@ void TestQgsGeometry::point() QCOMPARE( elemToString( exportPoint.asGML3( doc ) ), expectedGML3 ); QString expectedGML3prec3( QStringLiteral( "0.333 0.667" ) ); QCOMPARE( elemToString( exportPointFloat.asGML3( doc, 3 ) ), expectedGML3prec3 ); + QgsPoint exportPointZ( 1, 2, 3 ); + QString expectedGML2Z( QStringLiteral( "1 2 3" ) ); + QGSCOMPAREGML( elemToString( exportPointZ.asGML3( doc, 3 ) ), expectedGML2Z ); //asJSON QString expectedJson( QStringLiteral( "{\"type\": \"Point\", \"coordinates\": [1, 2]}" ) ); @@ -721,7 +749,7 @@ void TestQgsGeometry::point() QgsPoint closest; QgsVertexId after; // return error - points have no segments - QVERIFY( p20.closestSegment( QgsPoint( 4.0, 6.0 ), closest, after, 0, 0 ) < 0 ); + QVERIFY( p20.closestSegment( QgsPoint( 4.0, 6.0 ), closest, after ) < 0 ); //nextVertex QgsPoint p21( 3.0, 4.0 ); @@ -961,6 +989,1282 @@ void TestQgsGeometry::point() } +void TestQgsGeometry::circularString() +{ + //test constructors + QgsCircularString l1; + QVERIFY( l1.isEmpty() ); + QCOMPARE( l1.numPoints(), 0 ); + QCOMPARE( l1.vertexCount(), 0 ); + QCOMPARE( l1.nCoordinates(), 0 ); + QCOMPARE( l1.ringCount(), 0 ); + QCOMPARE( l1.partCount(), 0 ); + QVERIFY( !l1.is3D() ); + QVERIFY( !l1.isMeasure() ); + QCOMPARE( l1.wkbType(), QgsWkbTypes::CircularString ); + QCOMPARE( l1.wktTypeStr(), QString( "CircularString" ) ); + QCOMPARE( l1.geometryType(), QString( "CircularString" ) ); + QCOMPARE( l1.dimension(), 1 ); + QVERIFY( l1.hasCurvedSegments() ); + QCOMPARE( l1.area(), 0.0 ); + QCOMPARE( l1.perimeter(), 0.0 ); + QgsPointSequence pts; + l1.points( pts ); + QVERIFY( pts.empty() ); + + //setPoints + QgsCircularString l2; + l2.setPoints( QgsPointSequence() << QgsPoint( 1.0, 2.0 ) ); + QVERIFY( !l2.isEmpty() ); + QCOMPARE( l2.numPoints(), 1 ); + QCOMPARE( l2.vertexCount(), 1 ); + QCOMPARE( l2.nCoordinates(), 1 ); + QCOMPARE( l2.ringCount(), 1 ); + QCOMPARE( l2.partCount(), 1 ); + QVERIFY( !l2.is3D() ); + QVERIFY( !l2.isMeasure() ); + QCOMPARE( l2.wkbType(), QgsWkbTypes::CircularString ); + QVERIFY( l2.hasCurvedSegments() ); + QCOMPARE( l2.area(), 0.0 ); + QCOMPARE( l2.perimeter(), 0.0 ); + l2.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( 1.0, 2.0 ) ); + + //setting first vertex should set linestring z/m type + QgsCircularString l3; + l3.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1.0, 2.0, 3.0 ) ); + QVERIFY( !l3.isEmpty() ); + QVERIFY( l3.is3D() ); + QVERIFY( !l3.isMeasure() ); + QCOMPARE( l3.wkbType(), QgsWkbTypes::CircularStringZ ); + QCOMPARE( l3.wktTypeStr(), QString( "CircularStringZ" ) ); + l3.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1.0, 2.0, 3.0 ) ); + + QgsCircularString l4; + l4.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1.0, 2.0, 0.0, 3.0 ) ); + QVERIFY( !l4.isEmpty() ); + QVERIFY( !l4.is3D() ); + QVERIFY( l4.isMeasure() ); + QCOMPARE( l4.wkbType(), QgsWkbTypes::CircularStringM ); + QCOMPARE( l4.wktTypeStr(), QString( "CircularStringM" ) ); + l4.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1.0, 2.0, 0.0, 3.0 ) ); + + QgsCircularString l5; + l5.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1.0, 2.0, 3.0, 4.0 ) ); + QVERIFY( !l5.isEmpty() ); + QVERIFY( l5.is3D() ); + QVERIFY( l5.isMeasure() ); + QCOMPARE( l5.wkbType(), QgsWkbTypes::CircularStringZM ); + QCOMPARE( l5.wktTypeStr(), QString( "CircularStringZM" ) ); + l5.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1.0, 2.0, 3.0, 4.0 ) ); + + //clear + l5.clear(); + QVERIFY( l5.isEmpty() ); + QCOMPARE( l5.numPoints(), 0 ); + QCOMPARE( l5.vertexCount(), 0 ); + QCOMPARE( l5.nCoordinates(), 0 ); + QCOMPARE( l5.ringCount(), 0 ); + QCOMPARE( l5.partCount(), 0 ); + QVERIFY( !l5.is3D() ); + QVERIFY( !l5.isMeasure() ); + QCOMPARE( l5.wkbType(), QgsWkbTypes::CircularString ); + + //setPoints + QgsCircularString l8; + l8.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 2, 3 ) << QgsPoint( 3, 4 ) ); + QVERIFY( !l8.isEmpty() ); + QCOMPARE( l8.numPoints(), 3 ); + QCOMPARE( l8.vertexCount(), 3 ); + QCOMPARE( l8.nCoordinates(), 3 ); + QCOMPARE( l8.ringCount(), 1 ); + QCOMPARE( l8.partCount(), 1 ); + QVERIFY( !l8.is3D() ); + QVERIFY( !l8.isMeasure() ); + QCOMPARE( l8.wkbType(), QgsWkbTypes::CircularString ); + QVERIFY( l8.hasCurvedSegments() ); + l8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 2, 3 ) << QgsPoint( 3, 4 ) ); + + //setPoints with empty list, should clear linestring + l8.setPoints( QgsPointSequence() ); + QVERIFY( l8.isEmpty() ); + QCOMPARE( l8.numPoints(), 0 ); + QCOMPARE( l8.vertexCount(), 0 ); + QCOMPARE( l8.nCoordinates(), 0 ); + QCOMPARE( l8.ringCount(), 0 ); + QCOMPARE( l8.partCount(), 0 ); + QCOMPARE( l8.wkbType(), QgsWkbTypes::CircularString ); + l8.points( pts ); + QVERIFY( pts.empty() ); + + //setPoints with z + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 2, 3, 4 ) ); + QCOMPARE( l8.numPoints(), 2 ); + QVERIFY( l8.is3D() ); + QVERIFY( !l8.isMeasure() ); + QCOMPARE( l8.wkbType(), QgsWkbTypes::CircularStringZ ); + l8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 2, 3, 4 ) ); + + //setPoints with m + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) << QgsPoint( QgsWkbTypes::PointM, 2, 3, 0, 4 ) ); + QCOMPARE( l8.numPoints(), 2 ); + QVERIFY( !l8.is3D() ); + QVERIFY( l8.isMeasure() ); + QCOMPARE( l8.wkbType(), QgsWkbTypes::CircularStringM ); + l8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) << QgsPoint( QgsWkbTypes::PointM, 2, 3, 0, 4 ) ); + + //setPoints with zm + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 4, 5 ) << QgsPoint( QgsWkbTypes::PointZM, 2, 3, 4, 5 ) ); + QCOMPARE( l8.numPoints(), 2 ); + QVERIFY( l8.is3D() ); + QVERIFY( l8.isMeasure() ); + QCOMPARE( l8.wkbType(), QgsWkbTypes::CircularStringZM ); + l8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 4, 5 ) << QgsPoint( QgsWkbTypes::PointZM, 2, 3, 4, 5 ) ); + + //setPoints with MIXED dimensionality of points + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 4, 5 ) << QgsPoint( QgsWkbTypes::PointM, 2, 3, 0, 5 ) ); + QCOMPARE( l8.numPoints(), 2 ); + QVERIFY( l8.is3D() ); + QVERIFY( l8.isMeasure() ); + QCOMPARE( l8.wkbType(), QgsWkbTypes::CircularStringZM ); + l8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 4, 5 ) << QgsPoint( QgsWkbTypes::PointZM, 2, 3, 0, 5 ) ); + + //test point + QCOMPARE( l8.pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 1, 2, 4, 5 ) ); + QCOMPARE( l8.pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 2, 3, 0, 5 ) ); + + //out of range - just want no crash here + QgsPoint bad = l8.pointN( -1 ); + bad = l8.pointN( 100 ); + + //test getters/setters + QgsCircularString l9; + l9.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) + << QgsPoint( QgsWkbTypes::PointZM, 21, 22, 23, 24 ) ); + QCOMPARE( l9.xAt( 0 ), 1.0 ); + QCOMPARE( l9.xAt( 1 ), 11.0 ); + QCOMPARE( l9.xAt( 2 ), 21.0 ); + QCOMPARE( l9.xAt( -1 ), 0.0 ); //out of range + QCOMPARE( l9.xAt( 11 ), 0.0 ); //out of range + + l9.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 51.0, 2 ) ); + QCOMPARE( l9.xAt( 0 ), 51.0 ); + l9.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 61.0, 12 ) ); + QCOMPARE( l9.xAt( 1 ), 61.0 ); + l9.moveVertex( QgsVertexId( 0, 0, -1 ), QgsPoint( 71.0, 2 ) ); //out of range + l9.moveVertex( QgsVertexId( 0, 0, 11 ), QgsPoint( 71.0, 2 ) ); //out of range + + QCOMPARE( l9.yAt( 0 ), 2.0 ); + QCOMPARE( l9.yAt( 1 ), 12.0 ); + QCOMPARE( l9.yAt( 2 ), 22.0 ); + QCOMPARE( l9.yAt( -1 ), 0.0 ); //out of range + QCOMPARE( l9.yAt( 11 ), 0.0 ); //out of range + + l9.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 51.0, 52 ) ); + QCOMPARE( l9.yAt( 0 ), 52.0 ); + l9.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 61.0, 62 ) ); + QCOMPARE( l9.yAt( 1 ), 62.0 ); + + QCOMPARE( l9.pointN( 0 ).z(), 3.0 ); + QCOMPARE( l9.pointN( 1 ).z(), 13.0 ); + QCOMPARE( l9.pointN( 2 ).z(), 23.0 ); + + l9.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 71.0, 2, 53 ) ); + QCOMPARE( l9.pointN( 0 ).z(), 53.0 ); + l9.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 71.0, 2, 63 ) ); + QCOMPARE( l9.pointN( 1 ).z(), 63.0 ); + + QCOMPARE( l9.pointN( 0 ).m(), 4.0 ); + QCOMPARE( l9.pointN( 1 ).m(), 14.0 ); + QCOMPARE( l9.pointN( 2 ).m(), 24.0 ); + + l9.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 71.0, 2, 53, 54 ) ); + QCOMPARE( l9.pointN( 0 ).m(), 54.0 ); + l9.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 71.0, 2, 53, 64 ) ); + QCOMPARE( l9.pointN( 1 ).m(), 64.0 ); + + //check zAt/setZAt with non-3d linestring + l9.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) + << QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 14 ) + << QgsPoint( QgsWkbTypes::PointM, 21, 22, 0, 24 ) ); + + //basically we just don't want these to crash + QVERIFY( std::isnan( l9.pointN( 0 ).z() ) ); + QVERIFY( std::isnan( l9.pointN( 1 ).z() ) ); + l9.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 71.0, 2, 53 ) ); + l9.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 71.0, 2, 63 ) ); + + //check mAt/setMAt with non-measure linestring + l9.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 12 ) + << QgsPoint( 21, 22 ) ); + + //basically we just don't want these to crash + QVERIFY( std::isnan( l9.pointN( 0 ).m() ) ); + QVERIFY( std::isnan( l9.pointN( 1 ).m() ) ); + l9.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 71.0, 2, 0, 53 ) ); + l9.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 71.0, 2, 0, 63 ) ); + + //equality + QgsCircularString e1; + QgsCircularString e2; + QVERIFY( e1 == e2 ); + QVERIFY( !( e1 != e2 ) ); + e1.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) ); + QVERIFY( !( e1 == e2 ) ); //different number of vertices + QVERIFY( e1 != e2 ); + e2.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) ); + QVERIFY( e1 == e2 ); + QVERIFY( !( e1 != e2 ) ); + e1.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 1 / 3.0, 4 / 3.0 ) ); + e2.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 2 / 6.0, 8 / 6.0 ) ); + QVERIFY( e1 == e2 ); //check non-integer equality + QVERIFY( !( e1 != e2 ) ); + e1.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 1 / 3.0, 4 / 3.0 ) << QgsPoint( 7, 8 ) ); + e2.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 2 / 6.0, 8 / 6.0 ) << QgsPoint( 6, 9 ) ); + QVERIFY( !( e1 == e2 ) ); //different coordinates + QVERIFY( e1 != e2 ); + QgsCircularString e3; + e3.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 0 ) + << QgsPoint( QgsWkbTypes::PointZ, 1 / 3.0, 4 / 3.0, 0 ) + << QgsPoint( QgsWkbTypes::PointZ, 7, 8, 0 ) ); + QVERIFY( !( e1 == e3 ) ); //different dimension + QVERIFY( e1 != e3 ); + QgsCircularString e4; + e4.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 2 ) + << QgsPoint( QgsWkbTypes::PointZ, 1 / 3.0, 4 / 3.0, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 7, 8, 4 ) ); + QVERIFY( !( e3 == e4 ) ); //different z coordinates + QVERIFY( e3 != e4 ); + QgsCircularString e5; + e5.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 1 ) + << QgsPoint( QgsWkbTypes::PointM, 1 / 3.0, 4 / 3.0, 0, 2 ) + << QgsPoint( QgsWkbTypes::PointM, 7, 8, 0, 3 ) ); + QgsCircularString e6; + e6.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 11 ) + << QgsPoint( QgsWkbTypes::PointM, 1 / 3.0, 4 / 3.0, 0, 12 ) + << QgsPoint( QgsWkbTypes::PointM, 7, 8, 0, 13 ) ); + QVERIFY( !( e5 == e6 ) ); //different m values + QVERIFY( e5 != e6 ); + + QVERIFY( e6 != QgsLineString() ); + + //isClosed + QgsCircularString l11; + QVERIFY( !l11.isClosed() ); + l11.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 2 ) + << QgsPoint( 11, 22 ) + << QgsPoint( 1, 22 ) ); + QVERIFY( !l11.isClosed() ); + QCOMPARE( l11.numPoints(), 4 ); + QCOMPARE( l11.area(), 0.0 ); + QCOMPARE( l11.perimeter(), 0.0 ); + + //test that m values aren't considered when testing for closedness + l11.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) + << QgsPoint( QgsWkbTypes::PointM, 11, 2, 0, 4 ) + << QgsPoint( QgsWkbTypes::PointM, 11, 22, 0, 5 ) + << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 6 ) ); + QVERIFY( l11.isClosed() ); + + //polygonf + QgsCircularString l13; + l13.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 2, 11, 14 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 22, 21, 24 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 22, 31, 34 ) ); + + QPolygonF poly = l13.asQPolygonF(); + QCOMPARE( poly.count(), 4 ); + QCOMPARE( poly.at( 0 ).x(), 1.0 ); + QCOMPARE( poly.at( 0 ).y(), 2.0 ); + QCOMPARE( poly.at( 1 ).x(), 11.0 ); + QCOMPARE( poly.at( 1 ).y(), 2.0 ); + QCOMPARE( poly.at( 2 ).x(), 11.0 ); + QCOMPARE( poly.at( 2 ).y(), 22.0 ); + QCOMPARE( poly.at( 3 ).x(), 1.0 ); + QCOMPARE( poly.at( 3 ).y(), 22.0 ); + + // clone tests + QgsCircularString l14; + l14.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 2 ) + << QgsPoint( 11, 22 ) + << QgsPoint( 1, 22 ) ); + std::unique_ptr cloned( l14.clone() ); + QCOMPARE( cloned->numPoints(), 4 ); + QCOMPARE( cloned->vertexCount(), 4 ); + QCOMPARE( cloned->ringCount(), 1 ); + QCOMPARE( cloned->partCount(), 1 ); + QCOMPARE( cloned->wkbType(), QgsWkbTypes::CircularString ); + QVERIFY( !cloned->is3D() ); + QVERIFY( !cloned->isMeasure() ); + QCOMPARE( cloned->pointN( 0 ), l14.pointN( 0 ) ); + QCOMPARE( cloned->pointN( 1 ), l14.pointN( 1 ) ); + QCOMPARE( cloned->pointN( 2 ), l14.pointN( 2 ) ); + QCOMPARE( cloned->pointN( 3 ), l14.pointN( 3 ) ); + + //clone with Z/M + l14.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 2, 11, 14 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 22, 21, 24 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 22, 31, 34 ) ); + cloned.reset( l14.clone() ); + QCOMPARE( cloned->numPoints(), 4 ); + QCOMPARE( cloned->wkbType(), QgsWkbTypes::CircularStringZM ); + QVERIFY( cloned->is3D() ); + QVERIFY( cloned->isMeasure() ); + QCOMPARE( cloned->pointN( 0 ), l14.pointN( 0 ) ); + QCOMPARE( cloned->pointN( 1 ), l14.pointN( 1 ) ); + QCOMPARE( cloned->pointN( 2 ), l14.pointN( 2 ) ); + QCOMPARE( cloned->pointN( 3 ), l14.pointN( 3 ) ); + + //clone an empty line + l14.clear(); + cloned.reset( l14.clone() ); + QVERIFY( cloned->isEmpty() ); + QCOMPARE( cloned->numPoints(), 0 ); + QVERIFY( !cloned->is3D() ); + QVERIFY( !cloned->isMeasure() ); + QCOMPARE( cloned->wkbType(), QgsWkbTypes::CircularString ); + + //segmentize tests + QgsCircularString toSegment; + toSegment.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 10 ) << QgsPoint( 21, 2 ) ); + std::unique_ptr segmentized( static_cast< QgsLineString * >( toSegment.segmentize() ) ); + QCOMPARE( segmentized->numPoints(), 156 ); + QCOMPARE( segmentized->vertexCount(), 156 ); + QCOMPARE( segmentized->ringCount(), 1 ); + QCOMPARE( segmentized->partCount(), 1 ); + QCOMPARE( segmentized->wkbType(), QgsWkbTypes::LineString ); + QVERIFY( !segmentized->is3D() ); + QVERIFY( !segmentized->isMeasure() ); + QCOMPARE( segmentized->pointN( 0 ), toSegment.pointN( 0 ) ); + QCOMPARE( segmentized->pointN( segmentized->numPoints() - 1 ), toSegment.pointN( toSegment.numPoints() - 1 ) ); + + //segmentize with Z/M + toSegment.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 10, 11, 14 ) + << QgsPoint( QgsWkbTypes::PointZM, 21, 2, 21, 24 ) ); + segmentized.reset( static_cast< QgsLineString * >( toSegment.segmentize() ) ); + QCOMPARE( segmentized->numPoints(), 156 ); + QCOMPARE( segmentized->vertexCount(), 156 ); + QCOMPARE( segmentized->ringCount(), 1 ); + QCOMPARE( segmentized->partCount(), 1 ); + QCOMPARE( segmentized->wkbType(), QgsWkbTypes::LineStringZM ); + QVERIFY( segmentized->is3D() ); + QVERIFY( segmentized->isMeasure() ); + QCOMPARE( segmentized->pointN( 0 ), toSegment.pointN( 0 ) ); + QCOMPARE( segmentized->pointN( segmentized->numPoints() - 1 ), toSegment.pointN( toSegment.numPoints() - 1 ) ); + + //segmentize an empty line + toSegment.clear(); + segmentized.reset( static_cast< QgsLineString * >( toSegment.segmentize() ) ); + QVERIFY( segmentized->isEmpty() ); + QCOMPARE( segmentized->numPoints(), 0 ); + QVERIFY( !segmentized->is3D() ); + QVERIFY( !segmentized->isMeasure() ); + QCOMPARE( segmentized->wkbType(), QgsWkbTypes::LineString ); + + + //to/from WKB + QgsCircularString l15; + l15.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 2, 11, 14 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 22, 21, 24 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 22, 31, 34 ) ); + QByteArray wkb15 = l15.asWkb(); + QgsCircularString l16; + QgsConstWkbPtr wkb15ptr( wkb15 ); + l16.fromWkb( wkb15ptr ); + QCOMPARE( l16.numPoints(), 4 ); + QCOMPARE( l16.vertexCount(), 4 ); + QCOMPARE( l16.nCoordinates(), 4 ); + QCOMPARE( l16.ringCount(), 1 ); + QCOMPARE( l16.partCount(), 1 ); + QCOMPARE( l16.wkbType(), QgsWkbTypes::CircularStringZM ); + QVERIFY( l16.is3D() ); + QVERIFY( l16.isMeasure() ); + QCOMPARE( l16.pointN( 0 ), l15.pointN( 0 ) ); + QCOMPARE( l16.pointN( 1 ), l15.pointN( 1 ) ); + QCOMPARE( l16.pointN( 2 ), l15.pointN( 2 ) ); + QCOMPARE( l16.pointN( 3 ), l15.pointN( 3 ) ); + + //bad WKB - check for no crash + l16.clear(); + QgsConstWkbPtr nullPtr( nullptr, 0 ); + QVERIFY( !l16.fromWkb( nullPtr ) ); + QCOMPARE( l16.wkbType(), QgsWkbTypes::CircularString ); + QgsPoint point( 1, 2 ); + QByteArray wkb16 = point.asWkb(); + QgsConstWkbPtr wkb16ptr( wkb16 ); + QVERIFY( !l16.fromWkb( wkb16ptr ) ); + QCOMPARE( l16.wkbType(), QgsWkbTypes::CircularString ); + + //to/from WKT + QgsCircularString l17; + l17.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 2, 11, 14 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 22, 21, 24 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 22, 31, 34 ) ); + + QString wkt = l17.asWkt(); + QVERIFY( !wkt.isEmpty() ); + QgsCircularString l18; + QVERIFY( l18.fromWkt( wkt ) ); + QCOMPARE( l18.numPoints(), 4 ); + QCOMPARE( l18.wkbType(), QgsWkbTypes::CircularStringZM ); + QVERIFY( l18.is3D() ); + QVERIFY( l18.isMeasure() ); + QCOMPARE( l18.pointN( 0 ), l17.pointN( 0 ) ); + QCOMPARE( l18.pointN( 1 ), l17.pointN( 1 ) ); + QCOMPARE( l18.pointN( 2 ), l17.pointN( 2 ) ); + QCOMPARE( l18.pointN( 3 ), l17.pointN( 3 ) ); + + //bad WKT + QVERIFY( !l18.fromWkt( "Polygon()" ) ); + QVERIFY( l18.isEmpty() ); + QCOMPARE( l18.numPoints(), 0 ); + QVERIFY( !l18.is3D() ); + QVERIFY( !l18.isMeasure() ); + QCOMPARE( l18.wkbType(), QgsWkbTypes::CircularString ); + + //asGML2 + QgsCircularString exportLine; + exportLine.setPoints( QgsPointSequence() << QgsPoint( 31, 32 ) + << QgsPoint( 41, 42 ) + << QgsPoint( 51, 52 ) ); + QgsCircularString exportLineFloat; + exportLineFloat.setPoints( QgsPointSequence() << QgsPoint( 1 / 3.0, 2 / 3.0 ) + << QgsPoint( 1 + 1 / 3.0, 1 + 2 / 3.0 ) + << QgsPoint( 2 + 1 / 3.0, 2 + 2 / 3.0 ) ); + QDomDocument doc( QStringLiteral( "gml" ) ); + QString expectedGML2( QStringLiteral( "31,32 41,42 51,52" ) ); + QGSCOMPAREGML( elemToString( exportLine.asGML2( doc ) ), expectedGML2 ); + QString expectedGML2prec3( QStringLiteral( "0.333,0.667 1.333,1.667 2.333,2.667" ) ); + QGSCOMPAREGML( elemToString( exportLineFloat.asGML2( doc, 3 ) ), expectedGML2prec3 ); + + //asGML3 + QString expectedGML3( QStringLiteral( "31 32 41 42 51 52" ) ); + QCOMPARE( elemToString( exportLine.asGML3( doc ) ), expectedGML3 ); + QString expectedGML3prec3( QStringLiteral( "0.333 0.667 1.333 1.667 2.333 2.667" ) ); + QCOMPARE( elemToString( exportLineFloat.asGML3( doc, 3 ) ), expectedGML3prec3 ); + + //asJSON + QString expectedJson( QStringLiteral( "{\"type\": \"LineString\", \"coordinates\": [ [31, 32], [41, 42], [51, 52]]}" ) ); + QCOMPARE( exportLine.asJSON(), expectedJson ); + QString expectedJsonPrec3( QStringLiteral( "{\"type\": \"LineString\", \"coordinates\": [ [0.333, 0.667], [1.333, 1.667], [2.333, 2.667]]}" ) ); + QCOMPARE( exportLineFloat.asJSON( 3 ), expectedJsonPrec3 ); + + //length + QgsCircularString l19; + QCOMPARE( l19.length(), 0.0 ); + l19.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 10, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) ); + QGSCOMPARENEAR( l19.length(), 26.1433, 0.001 ); + + //startPoint + QCOMPARE( l19.startPoint(), QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 ) ); + + //endPoint + QCOMPARE( l19.endPoint(), QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) ); + + //bad start/end points. Test that this doesn't crash. + l19.clear(); + QCOMPARE( l19.startPoint(), QgsPoint() ); + QCOMPARE( l19.endPoint(), QgsPoint() ); + + //curveToLine - no segmentation required, so should return a clone + l19.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 10, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) ); + segmentized.reset( l19.curveToLine() ); + QCOMPARE( segmentized->numPoints(), 181 ); + QCOMPARE( segmentized->wkbType(), QgsWkbTypes::LineStringZM ); + QVERIFY( segmentized->is3D() ); + QVERIFY( segmentized->isMeasure() ); + QCOMPARE( segmentized->pointN( 0 ), l19.pointN( 0 ) ); + QCOMPARE( segmentized->pointN( segmentized->numPoints() - 1 ), l19.pointN( l19.numPoints() - 1 ) ); + + // points + QgsCircularString l20; + QgsPointSequence points; + l20.points( points ); + QVERIFY( l20.isEmpty() ); + l20.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 10, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) ); + l20.points( points ); + QCOMPARE( points.count(), 3 ); + QCOMPARE( points.at( 0 ), QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 ) ); + QCOMPARE( points.at( 1 ), QgsPoint( QgsWkbTypes::PointZM, 1, 10, 4, 5 ) ); + QCOMPARE( points.at( 2 ), QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) ); + + //CRS transform + QgsCoordinateReferenceSystem sourceSrs; + sourceSrs.createFromSrid( 3994 ); + QgsCoordinateReferenceSystem destSrs; + destSrs.createFromSrid( 4202 ); // want a transform with ellipsoid change + QgsCoordinateTransform tr( sourceSrs, destSrs ); + + // 2d CRS transform + QgsCircularString l21; + l21.setPoints( QgsPointSequence() << QgsPoint( 6374985, -3626584 ) + << QgsPoint( 6474985, -3526584 ) ); + l21.transform( tr, QgsCoordinateTransform::ForwardTransform ); + QGSCOMPARENEAR( l21.pointN( 0 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( l21.pointN( 0 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( l21.pointN( 1 ).x(), 176.959, 0.001 ); + QGSCOMPARENEAR( l21.pointN( 1 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( l21.boundingBox().xMinimum(), 175.771, 0.001 ); + QGSCOMPARENEAR( l21.boundingBox().yMinimum(), -39.724, 0.001 ); + QGSCOMPARENEAR( l21.boundingBox().xMaximum(), 176.959, 0.001 ); + QGSCOMPARENEAR( l21.boundingBox().yMaximum(), -38.7999, 0.001 ); + + //3d CRS transform + QgsCircularString l22; + l22.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 6374985, -3626584, 1, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 6474985, -3526584, 3, 4 ) ); + l22.transform( tr, QgsCoordinateTransform::ForwardTransform ); + QGSCOMPARENEAR( l22.pointN( 0 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( l22.pointN( 0 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( l22.pointN( 0 ).z(), 1.0, 0.001 ); + QCOMPARE( l22.pointN( 0 ).m(), 2.0 ); + QGSCOMPARENEAR( l22.pointN( 1 ).x(), 176.959, 0.001 ); + QGSCOMPARENEAR( l22.pointN( 1 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( l22.pointN( 1 ).z(), 3.0, 0.001 ); + QCOMPARE( l22.pointN( 1 ).m(), 4.0 ); + + //reverse transform + l22.transform( tr, QgsCoordinateTransform::ReverseTransform ); + QGSCOMPARENEAR( l22.pointN( 0 ).x(), 6374985, 0.01 ); + QGSCOMPARENEAR( l22.pointN( 0 ).y(), -3626584, 0.01 ); + QGSCOMPARENEAR( l22.pointN( 0 ).z(), 1, 0.001 ); + QCOMPARE( l22.pointN( 0 ).m(), 2.0 ); + QGSCOMPARENEAR( l22.pointN( 1 ).x(), 6474985, 0.01 ); + QGSCOMPARENEAR( l22.pointN( 1 ).y(), -3526584, 0.01 ); + QGSCOMPARENEAR( l22.pointN( 1 ).z(), 3, 0.001 ); + QCOMPARE( l22.pointN( 1 ).m(), 4.0 ); + + //z value transform + l22.transform( tr, QgsCoordinateTransform::ForwardTransform, true ); + QGSCOMPARENEAR( l22.pointN( 0 ).z(), -19.249066, 0.001 ); + QGSCOMPARENEAR( l22.pointN( 1 ).z(), -21.092128, 0.001 ); + l22.transform( tr, QgsCoordinateTransform::ReverseTransform, true ); + QGSCOMPARENEAR( l22.pointN( 0 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( l22.pointN( 1 ).z(), 3.0, 0.001 ); + + //QTransform transform + QTransform qtr = QTransform::fromScale( 2, 3 ); + QgsCircularString l23; + l23.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) ); + l23.transform( qtr ); + QCOMPARE( l23.pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 2, 6, 3, 4 ) ); + QCOMPARE( l23.pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 22, 36, 13, 14 ) ); + QCOMPARE( l23.boundingBox(), QgsRectangle( 2, 6, 22, 36 ) ); + + //insert vertex + //cannot insert vertex in empty line + QgsCircularString l24; + QVERIFY( !l24.insertVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QCOMPARE( l24.numPoints(), 0 ); + + //2d line + l24.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 12 ) << QgsPoint( 1, 22 ) ); + QVERIFY( l24.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 4.0, 7.0 ) ) ); + QCOMPARE( l24.numPoints(), 5 ); + QVERIFY( !l24.is3D() ); + QVERIFY( !l24.isMeasure() ); + QCOMPARE( l24.wkbType(), QgsWkbTypes::CircularString ); + QCOMPARE( l24.pointN( 0 ), QgsPoint( 1.0, 2.0 ) ); + QCOMPARE( l24.pointN( 1 ), QgsPoint( 4.0, 7.0 ) ); + QGSCOMPARENEAR( l24.pointN( 2 ).x(), 7.192236, 0.01 ); + QGSCOMPARENEAR( l24.pointN( 2 ).y(), 9.930870, 0.01 ); + QCOMPARE( l24.pointN( 3 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( l24.pointN( 4 ), QgsPoint( 1.0, 22.0 ) ); + + QVERIFY( l24.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 8.0, 9.0 ) ) ); + QVERIFY( l24.insertVertex( QgsVertexId( 0, 0, 2 ), QgsPoint( 18.0, 16.0 ) ) ); + QCOMPARE( l24.numPoints(), 9 ); + QCOMPARE( l24.pointN( 0 ), QgsPoint( 1.0, 2.0 ) ); + QGSCOMPARENEAR( l24.pointN( 1 ).x(), 4.363083, 0.01 ); + QGSCOMPARENEAR( l24.pointN( 1 ).y(), 5.636917, 0.01 ); + QCOMPARE( l24.pointN( 2 ), QgsPoint( 8.0, 9.0 ) ); + QCOMPARE( l24.pointN( 3 ), QgsPoint( 18.0, 16.0 ) ); + QGSCOMPARENEAR( l24.pointN( 4 ).x(), 5.876894, 0.01 ); + QGSCOMPARENEAR( l24.pointN( 4 ).y(), 8.246211, 0.01 ); + QCOMPARE( l24.pointN( 5 ), QgsPoint( 4.0, 7.0 ) ); + QGSCOMPARENEAR( l24.pointN( 6 ).x(), 7.192236, 0.01 ); + QGSCOMPARENEAR( l24.pointN( 6 ).y(), 9.930870, 0.01 ); + QCOMPARE( l24.pointN( 7 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( l24.pointN( 8 ), QgsPoint( 1.0, 22.0 ) ); + + //insert vertex at end + QVERIFY( !l24.insertVertex( QgsVertexId( 0, 0, 9 ), QgsPoint( 31.0, 32.0 ) ) ); + + //insert vertex past end + QVERIFY( !l24.insertVertex( QgsVertexId( 0, 0, 10 ), QgsPoint( 41.0, 42.0 ) ) ); + QCOMPARE( l24.numPoints(), 9 ); + + //insert vertex before start + QVERIFY( !l24.insertVertex( QgsVertexId( 0, 0, -18 ), QgsPoint( 41.0, 42.0 ) ) ); + QCOMPARE( l24.numPoints(), 9 ); + + //insert 4d vertex in 4d line + l24.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 10, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) ); + QVERIFY( l24.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) ) ); + QCOMPARE( l24.numPoints(), 5 ); + QCOMPARE( l24.pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) ); + + //insert 2d vertex in 4d line + QVERIFY( l24.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 101, 102 ) ) ); + QCOMPARE( l24.numPoints(), 7 ); + QCOMPARE( l24.wkbType(), QgsWkbTypes::CircularStringZM ); + QCOMPARE( l24.pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 101, 102 ) ); + + //insert 4d vertex in 2d line + l24.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 12 ) << QgsPoint( 1, 22 ) ); + QVERIFY( l24.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( QgsWkbTypes::PointZM, 2, 4, 103, 104 ) ) ); + QCOMPARE( l24.numPoints(), 5 ); + QCOMPARE( l24.wkbType(), QgsWkbTypes::CircularString ); + QCOMPARE( l24.pointN( 1 ), QgsPoint( QgsWkbTypes::Point, 2, 4 ) ); + + //move vertex + + //empty line + QgsCircularString l25; + QVERIFY( !l25.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( l25.isEmpty() ); + + //valid line + l25.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) ); + QVERIFY( l25.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( l25.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 16.0, 17.0 ) ) ); + QVERIFY( l25.moveVertex( QgsVertexId( 0, 0, 2 ), QgsPoint( 26.0, 27.0 ) ) ); + QCOMPARE( l25.pointN( 0 ), QgsPoint( 6.0, 7.0 ) ); + QCOMPARE( l25.pointN( 1 ), QgsPoint( 16.0, 17.0 ) ); + QCOMPARE( l25.pointN( 2 ), QgsPoint( 26.0, 27.0 ) ); + + //out of range + QVERIFY( !l25.moveVertex( QgsVertexId( 0, 0, -1 ), QgsPoint( 3.0, 4.0 ) ) ); + QVERIFY( !l25.moveVertex( QgsVertexId( 0, 0, 10 ), QgsPoint( 3.0, 4.0 ) ) ); + QCOMPARE( l25.pointN( 0 ), QgsPoint( 6.0, 7.0 ) ); + QCOMPARE( l25.pointN( 1 ), QgsPoint( 16.0, 17.0 ) ); + QCOMPARE( l25.pointN( 2 ), QgsPoint( 26.0, 27.0 ) ); + + //move 4d point in 4d line + l25.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 10, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) ); + QVERIFY( l25.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( QgsWkbTypes::PointZM, 6, 7, 12, 13 ) ) ); + QCOMPARE( l25.pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 6, 7, 12, 13 ) ); + + //move 2d point in 4d line, existing z/m should be maintained + QVERIFY( l25.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 34, 35 ) ) ); + QCOMPARE( l25.pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 34, 35, 12, 13 ) ); + + //move 4d point in 2d line + l25.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) ); + QVERIFY( l25.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( QgsWkbTypes::PointZM, 3, 4, 2, 3 ) ) ); + QCOMPARE( l25.pointN( 0 ), QgsPoint( 3, 4 ) ); + + + //delete vertex + + //empty line + QgsCircularString l26; + QVERIFY( l26.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QVERIFY( l26.isEmpty() ); + + //valid line + l26.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 21, 22, 6, 7 ) + << QgsPoint( QgsWkbTypes::PointZM, 31, 32, 6, 7 ) ); + //out of range vertices + QVERIFY( !l26.deleteVertex( QgsVertexId( 0, 0, -1 ) ) ); + QVERIFY( !l26.deleteVertex( QgsVertexId( 0, 0, 100 ) ) ); + + //valid vertices + QVERIFY( l26.deleteVertex( QgsVertexId( 0, 0, 1 ) ) ); + QCOMPARE( l26.numPoints(), 2 ); + QCOMPARE( l26.pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) ); + QCOMPARE( l26.pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 31, 32, 6, 7 ) ); + + //removing the next vertex removes all remaining vertices + QVERIFY( l26.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( l26.numPoints(), 0 ); + QVERIFY( l26.isEmpty() ); + QVERIFY( l26.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QVERIFY( l26.isEmpty() ); + + //reversed + QgsCircularString l27; + std::unique_ptr< QgsCircularString > reversed( l27.reversed() ); + QVERIFY( reversed->isEmpty() ); + l27.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 21, 22, 6, 7 ) ); + reversed.reset( l27.reversed() ); + QCOMPARE( reversed->numPoints(), 3 ); + QCOMPARE( reversed->wkbType(), QgsWkbTypes::CircularStringZM ); + QVERIFY( reversed->is3D() ); + QVERIFY( reversed->isMeasure() ); + QCOMPARE( reversed->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 21, 22, 6, 7 ) ); + QCOMPARE( reversed->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 11, 12, 4, 5 ) ); + QCOMPARE( reversed->pointN( 2 ), QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) ); + + //addZValue + + QgsCircularString l28; + QCOMPARE( l28.wkbType(), QgsWkbTypes::CircularString ); + QVERIFY( l28.addZValue() ); + QCOMPARE( l28.wkbType(), QgsWkbTypes::CircularStringZ ); + l28.clear(); + QVERIFY( l28.addZValue() ); + QCOMPARE( l28.wkbType(), QgsWkbTypes::CircularStringZ ); + //2d line + l28.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) ); + QVERIFY( l28.addZValue( 2 ) ); + QVERIFY( l28.is3D() ); + QCOMPARE( l28.wkbType(), QgsWkbTypes::CircularStringZ ); + QCOMPARE( l28.pointN( 0 ), QgsPoint( QgsWkbTypes::PointZ, 1, 2, 2 ) ); + QCOMPARE( l28.pointN( 1 ), QgsPoint( QgsWkbTypes::PointZ, 11, 12, 2 ) ); + QVERIFY( !l28.addZValue( 4 ) ); //already has z value, test that existing z is unchanged + QCOMPARE( l28.pointN( 0 ), QgsPoint( QgsWkbTypes::PointZ, 1, 2, 2 ) ); + QCOMPARE( l28.pointN( 1 ), QgsPoint( QgsWkbTypes::PointZ, 11, 12, 2 ) ); + //linestring with m + l28.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) << QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 4 ) ); + QVERIFY( l28.addZValue( 5 ) ); + QVERIFY( l28.is3D() ); + QVERIFY( l28.isMeasure() ); + QCOMPARE( l28.wkbType(), QgsWkbTypes::CircularStringZM ); + QCOMPARE( l28.pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 1, 2, 5, 3 ) ); + QCOMPARE( l28.pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 11, 12, 5, 4 ) ); + + //addMValue + + QgsCircularString l29; + QCOMPARE( l29.wkbType(), QgsWkbTypes::CircularString ); + QVERIFY( l29.addMValue() ); + QCOMPARE( l29.wkbType(), QgsWkbTypes::CircularStringM ); + l29.clear(); + QVERIFY( l29.addMValue() ); + QCOMPARE( l29.wkbType(), QgsWkbTypes::CircularStringM ); + //2d line + l29.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) ); + QVERIFY( l29.addMValue( 2 ) ); + QVERIFY( !l29.is3D() ); + QVERIFY( l29.isMeasure() ); + QCOMPARE( l29.wkbType(), QgsWkbTypes::CircularStringM ); + QCOMPARE( l29.pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 2 ) ); + QCOMPARE( l29.pointN( 1 ), QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 2 ) ); + QVERIFY( !l29.addMValue( 4 ) ); //already has m value, test that existing m is unchanged + QCOMPARE( l29.pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 2 ) ); + QCOMPARE( l29.pointN( 1 ), QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 2 ) ); + //linestring with z + l29.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 11, 12, 4 ) ); + QVERIFY( l29.addMValue( 5 ) ); + QVERIFY( l29.is3D() ); + QVERIFY( l29.isMeasure() ); + QCOMPARE( l29.wkbType(), QgsWkbTypes::CircularStringZM ); + QCOMPARE( l29.pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 5 ) ); + QCOMPARE( l29.pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 11, 12, 4, 5 ) ); + + //dropZValue + QgsCircularString l28d; + QVERIFY( !l28d.dropZValue() ); + l28d.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) ); + QVERIFY( !l28d.dropZValue() ); + l28d.addZValue( 1.0 ); + QCOMPARE( l28d.wkbType(), QgsWkbTypes::CircularStringZ ); + QVERIFY( l28d.is3D() ); + QVERIFY( l28d.dropZValue() ); + QVERIFY( !l28d.is3D() ); + QCOMPARE( l28d.wkbType(), QgsWkbTypes::CircularString ); + QCOMPARE( l28d.pointN( 0 ), QgsPoint( QgsWkbTypes::Point, 1, 2 ) ); + QCOMPARE( l28d.pointN( 1 ), QgsPoint( QgsWkbTypes::Point, 11, 12 ) ); + QVERIFY( !l28d.dropZValue() ); //already dropped + //linestring with m + l28d.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 3, 4 ) ); + QVERIFY( l28d.dropZValue() ); + QVERIFY( !l28d.is3D() ); + QVERIFY( l28d.isMeasure() ); + QCOMPARE( l28d.wkbType(), QgsWkbTypes::CircularStringM ); + QCOMPARE( l28d.pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) ); + QCOMPARE( l28d.pointN( 1 ), QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 4 ) ); + + //dropMValue + l28d.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) ); + QVERIFY( !l28d.dropMValue() ); + l28d.addMValue( 1.0 ); + QCOMPARE( l28d.wkbType(), QgsWkbTypes::CircularStringM ); + QVERIFY( l28d.isMeasure() ); + QVERIFY( l28d.dropMValue() ); + QVERIFY( !l28d.isMeasure() ); + QCOMPARE( l28d.wkbType(), QgsWkbTypes::CircularString ); + QCOMPARE( l28d.pointN( 0 ), QgsPoint( QgsWkbTypes::Point, 1, 2 ) ); + QCOMPARE( l28d.pointN( 1 ), QgsPoint( QgsWkbTypes::Point, 11, 12 ) ); + QVERIFY( !l28d.dropMValue() ); //already dropped + //linestring with z + l28d.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 3, 4 ) ); + QVERIFY( l28d.dropMValue() ); + QVERIFY( !l28d.isMeasure() ); + QVERIFY( l28d.is3D() ); + QCOMPARE( l28d.wkbType(), QgsWkbTypes::CircularStringZ ); + QCOMPARE( l28d.pointN( 0 ), QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3, 0 ) ); + QCOMPARE( l28d.pointN( 1 ), QgsPoint( QgsWkbTypes::PointZ, 11, 12, 3, 0 ) ); + + //convertTo + l28d.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) ); + QVERIFY( l28d.convertTo( QgsWkbTypes::CircularString ) ); + QCOMPARE( l28d.wkbType(), QgsWkbTypes::CircularString ); + QVERIFY( l28d.convertTo( QgsWkbTypes::CircularStringZ ) ); + QCOMPARE( l28d.wkbType(), QgsWkbTypes::CircularStringZ ); + QCOMPARE( l28d.pointN( 0 ), QgsPoint( QgsWkbTypes::PointZ, 1, 2 ) ); + + QVERIFY( l28d.convertTo( QgsWkbTypes::CircularStringZM ) ); + QCOMPARE( l28d.wkbType(), QgsWkbTypes::CircularStringZM ); + QCOMPARE( l28d.pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 1, 2 ) ); + l28d.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 1, 2, 5 ) ); + QCOMPARE( l28d.pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 1, 2, 5.0 ) ); + //l28d.setMAt( 0, 6.0 ); + QVERIFY( l28d.convertTo( QgsWkbTypes::CircularStringM ) ); + QCOMPARE( l28d.wkbType(), QgsWkbTypes::CircularStringM ); + QCOMPARE( l28d.pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 1, 2 ) ); + l28d.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 1, 2, 0, 6 ) ); + QCOMPARE( l28d.pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 1, 2, 0.0, 6.0 ) ); + QVERIFY( l28d.convertTo( QgsWkbTypes::CircularString ) ); + QCOMPARE( l28d.wkbType(), QgsWkbTypes::CircularString ); + QCOMPARE( l28d.pointN( 0 ), QgsPoint( 1, 2 ) ); + QVERIFY( !l28d.convertTo( QgsWkbTypes::Polygon ) ); + + //isRing + QgsCircularString l30; + QVERIFY( !l30.isRing() ); + l30.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 2 ) ); + QVERIFY( !l30.isRing() ); //<4 points + l30.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) << QgsPoint( 31, 32 ) ); + QVERIFY( !l30.isRing() ); //not closed + l30.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) << QgsPoint( 1, 2 ) ); + QVERIFY( l30.isRing() ); + + //coordinateSequence + QgsCircularString l31; + QgsCoordinateSequence coords = l31.coordinateSequence(); + QCOMPARE( coords.count(), 1 ); + QCOMPARE( coords.at( 0 ).count(), 1 ); + QVERIFY( coords.at( 0 ).at( 0 ).isEmpty() ); + l31.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 21, 22, 6, 7 ) ); + coords = l31.coordinateSequence(); + QCOMPARE( coords.count(), 1 ); + QCOMPARE( coords.at( 0 ).count(), 1 ); + QCOMPARE( coords.at( 0 ).at( 0 ).count(), 3 ); + QCOMPARE( coords.at( 0 ).at( 0 ).at( 0 ), QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) ); + QCOMPARE( coords.at( 0 ).at( 0 ).at( 1 ), QgsPoint( QgsWkbTypes::PointZM, 11, 12, 4, 5 ) ); + QCOMPARE( coords.at( 0 ).at( 0 ).at( 2 ), QgsPoint( QgsWkbTypes::PointZM, 21, 22, 6, 7 ) ); + + //nextVertex + + QgsCircularString l32; + QgsVertexId v; + QgsPoint p; + QVERIFY( !l32.nextVertex( v, p ) ); + v = QgsVertexId( 0, 0, -2 ); + QVERIFY( !l32.nextVertex( v, p ) ); + v = QgsVertexId( 0, 0, 10 ); + QVERIFY( !l32.nextVertex( v, p ) ); + //CircularString + l32.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) ); + v = QgsVertexId( 0, 0, 2 ); //out of range + QVERIFY( !l32.nextVertex( v, p ) ); + v = QgsVertexId( 0, 0, -5 ); + QVERIFY( l32.nextVertex( v, p ) ); + v = QgsVertexId( 0, 0, -1 ); + QVERIFY( l32.nextVertex( v, p ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 0 ) ); + QCOMPARE( p, QgsPoint( 1, 2 ) ); + QVERIFY( l32.nextVertex( v, p ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( p, QgsPoint( 11, 12 ) ); + QVERIFY( !l32.nextVertex( v, p ) ); + v = QgsVertexId( 0, 1, 0 ); + QVERIFY( l32.nextVertex( v, p ) ); + QCOMPARE( v, QgsVertexId( 0, 1, 1 ) ); //test that ring number is maintained + QCOMPARE( p, QgsPoint( 11, 12 ) ); + v = QgsVertexId( 1, 0, 0 ); + QVERIFY( l32.nextVertex( v, p ) ); + QCOMPARE( v, QgsVertexId( 1, 0, 1 ) ); //test that part number is maintained + QCOMPARE( p, QgsPoint( 11, 12 ) ); + + //CircularStringZ + l32.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) ); + v = QgsVertexId( 0, 0, -1 ); + QVERIFY( l32.nextVertex( v, p ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 0 ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) ); + QVERIFY( l32.nextVertex( v, p ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) ); + QVERIFY( !l32.nextVertex( v, p ) ); + //CircularStringM + l32.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 14 ) ); + v = QgsVertexId( 0, 0, -1 ); + QVERIFY( l32.nextVertex( v, p ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 0 ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) ); + QVERIFY( l32.nextVertex( v, p ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 14 ) ); + QVERIFY( !l32.nextVertex( v, p ) ); + //CircularStringZM + l32.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) ); + v = QgsVertexId( 0, 0, -1 ); + QVERIFY( l32.nextVertex( v, p ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 0 ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) ); + QVERIFY( l32.nextVertex( v, p ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) ); + QVERIFY( !l32.nextVertex( v, p ) ); + + //vertexAt and pointAt + QgsCircularString l33; + l33.vertexAt( QgsVertexId( 0, 0, -10 ) ); //out of bounds, check for no crash + l33.vertexAt( QgsVertexId( 0, 0, 10 ) ); //out of bounds, check for no crash + QgsVertexId::VertexType type; + QVERIFY( !l33.pointAt( -10, p, type ) ); + QVERIFY( !l33.pointAt( 10, p, type ) ); + //CircularString + l33.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 22 ) ); + l33.vertexAt( QgsVertexId( 0, 0, -10 ) ); + l33.vertexAt( QgsVertexId( 0, 0, 10 ) ); //out of bounds, check for no crash + QCOMPARE( l33.vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( 1, 2 ) ); + QCOMPARE( l33.vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( 11, 12 ) ); + QCOMPARE( l33.vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( 1, 22 ) ); + QVERIFY( !l33.pointAt( -10, p, type ) ); + QVERIFY( !l33.pointAt( 10, p, type ) ); + QVERIFY( l33.pointAt( 0, p, type ) ); + QCOMPARE( p, QgsPoint( 1, 2 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + QVERIFY( l33.pointAt( 1, p, type ) ); + QCOMPARE( p, QgsPoint( 11, 12 ) ); + QCOMPARE( type, QgsVertexId::CurveVertex ); + QVERIFY( l33.pointAt( 2, p, type ) ); + QCOMPARE( p, QgsPoint( 1, 22 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + //CircularStringZ + l33.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) << QgsPoint( QgsWkbTypes::PointZ, 1, 22, 23 ) ); + QCOMPARE( l33.vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) ); + QCOMPARE( l33.vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) ); + QCOMPARE( l33.vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( QgsWkbTypes::PointZ, 1, 22, 23 ) ); + QVERIFY( l33.pointAt( 0, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + QVERIFY( l33.pointAt( 1, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) ); + QCOMPARE( type, QgsVertexId::CurveVertex ); + QVERIFY( l33.pointAt( 2, p, type ) ); + QCOMPARE( p, QgsPoint( 1, 22, 23 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + //CircularStringM + l33.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 14 ) << QgsPoint( QgsWkbTypes::PointM, 1, 22, 0, 24 ) ); + QCOMPARE( l33.vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) ); + QCOMPARE( l33.vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 14 ) ); + QCOMPARE( l33.vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( QgsWkbTypes::PointM, 1, 22, 0, 24 ) ); + QVERIFY( l33.pointAt( 0, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + QVERIFY( l33.pointAt( 1, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 14 ) ); + QCOMPARE( type, QgsVertexId::CurveVertex ); + QVERIFY( l33.pointAt( 2, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointM, 1, 22, 0, 24 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + //CircularStringZM + l33.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) << QgsPoint( QgsWkbTypes::PointZM, 1, 22, 23, 24 ) ); + QCOMPARE( l33.vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) ); + QCOMPARE( l33.vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) ); + QCOMPARE( l33.vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( QgsWkbTypes::PointZM, 1, 22, 23, 24 ) ); + QVERIFY( l33.pointAt( 0, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + QVERIFY( l33.pointAt( 1, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) ); + QCOMPARE( type, QgsVertexId::CurveVertex ); + QVERIFY( l33.pointAt( 2, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZM, 1, 22, 23, 24 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + + //centroid + QgsCircularString l34; + QCOMPARE( l34.centroid(), QgsPoint() ); + l34.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) ); + QCOMPARE( l34.centroid(), QgsPoint( 5, 10 ) ); + l34.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 20, 10 ) << QgsPoint( 2, 9 ) ); + QgsPoint centroid = l34.centroid(); + QGSCOMPARENEAR( centroid.x(), 7.333, 0.001 ); + QGSCOMPARENEAR( centroid.y(), 6.333, 0.001 ); + + //closest segment + QgsCircularString l35; + bool leftOf = false; + p = QgsPoint(); // reset all coords to zero + ( void )l35.closestSegment( QgsPoint( 1, 2 ), p, v ); //empty line, just want no crash + l35.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) ); + QVERIFY( l35.closestSegment( QgsPoint( 5, 10 ), p, v ) < 0 ); + l35.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 7, 12 ) << QgsPoint( 5, 15 ) ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 4, 11 ), p, v, &leftOf ), 2.0, 0.0001 ); + QCOMPARE( p, QgsPoint( 5, 10 ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, true ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 8, 11 ), p, v, &leftOf ), 1.583512, 0.0001 ); + QGSCOMPARENEAR( p.x(), 6.84, 0.01 ); + QGSCOMPARENEAR( p.y(), 11.49, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 5.5, 11.5 ), p, v, &leftOf ), 1.288897, 0.0001 ); + QGSCOMPARENEAR( p.x(), 6.302776, 0.01 ); + QGSCOMPARENEAR( p.y(), 10.7, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, true ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 7, 16 ), p, v, &leftOf ), 3.068288, 0.0001 ); + QGSCOMPARENEAR( p.x(), 5.981872, 0.01 ); + QGSCOMPARENEAR( p.y(), 14.574621, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 5.5, 13.5 ), p, v, &leftOf ), 1.288897, 0.0001 ); + QGSCOMPARENEAR( p.x(), 6.302776, 0.01 ); + QGSCOMPARENEAR( p.y(), 14.3, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, true ); + // point directly on segment + QCOMPARE( l35.closestSegment( QgsPoint( 5, 15 ), p, v, &leftOf ), 0.0 ); + QCOMPARE( p, QgsPoint( 5, 15 ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + + //clockwise string + l35.setPoints( QgsPointSequence() << QgsPoint( 5, 15 ) << QgsPoint( 7, 12 ) << QgsPoint( 5, 10 ) ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 4, 11 ), p, v, &leftOf ), 2, 0.0001 ); + QGSCOMPARENEAR( p.x(), 5, 0.01 ); + QGSCOMPARENEAR( p.y(), 10, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 8, 11 ), p, v, &leftOf ), 1.583512, 0.0001 ); + QGSCOMPARENEAR( p.x(), 6.84, 0.01 ); + QGSCOMPARENEAR( p.y(), 11.49, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, true ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 5.5, 11.5 ), p, v, &leftOf ), 1.288897, 0.0001 ); + QGSCOMPARENEAR( p.x(), 6.302776, 0.01 ); + QGSCOMPARENEAR( p.y(), 10.7, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 7, 16 ), p, v, &leftOf ), 3.068288, 0.0001 ); + QGSCOMPARENEAR( p.x(), 5.981872, 0.01 ); + QGSCOMPARENEAR( p.y(), 14.574621, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, true ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 5.5, 13.5 ), p, v, &leftOf ), 1.288897, 0.0001 ); + QGSCOMPARENEAR( p.x(), 6.302776, 0.01 ); + QGSCOMPARENEAR( p.y(), 14.3, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, false ); + // point directly on segment + QCOMPARE( l35.closestSegment( QgsPoint( 5, 15 ), p, v, &leftOf ), 0.0 ); + QCOMPARE( p, QgsPoint( 5, 15 ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + + //sumUpArea + QgsCircularString l36; + double area = 1.0; //sumUpArea adds to area, so start with non-zero value + l36.sumUpArea( area ); + QCOMPARE( area, 1.0 ); + l36.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) ); + l36.sumUpArea( area ); + QCOMPARE( area, 1.0 ); + l36.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 10, 10 ) ); + l36.sumUpArea( area ); + QCOMPARE( area, 1.0 ); + l36.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 2, 0 ) << QgsPoint( 2, 2 ) ); + l36.sumUpArea( area ); + QGSCOMPARENEAR( area, 4.141593, 0.0001 ); + l36.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 2, 0 ) << QgsPoint( 2, 2 ) << QgsPoint( 0, 2 ) ); + l36.sumUpArea( area ); + QGSCOMPARENEAR( area, 7.283185, 0.0001 ); + // full circle + l36.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 4, 0 ) << QgsPoint( 0, 0 ) ); + area = 0.0; + l36.sumUpArea( area ); + QGSCOMPARENEAR( area, 12.566370614359172, 0.0001 ); + + //boundingBox - test that bounding box is updated after every modification to the circular string + QgsCircularString l37; + QVERIFY( l37.boundingBox().isNull() ); + l37.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 10, 15 ) ); + QCOMPARE( l37.boundingBox(), QgsRectangle( 5, 10, 10, 15 ) ); + l37.setPoints( QgsPointSequence() << QgsPoint( -5, -10 ) << QgsPoint( -6, -10 ) << QgsPoint( -5.5, -9 ) ); + QCOMPARE( l37.boundingBox(), QgsRectangle( -6.125, -10.25, -5, -9 ) ); + QByteArray wkbToAppend = l37.asWkb(); + l37.clear(); + QVERIFY( l37.boundingBox().isNull() ); + QgsConstWkbPtr wkbToAppendPtr( wkbToAppend ); + l37.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 10, 15 ) ); + QCOMPARE( l37.boundingBox(), QgsRectangle( 5, 10, 10, 15 ) ); + l37.fromWkb( wkbToAppendPtr ); + QCOMPARE( l37.boundingBox(), QgsRectangle( -6.125, -10.25, -5, -9 ) ); + l37.fromWkt( QStringLiteral( "CircularString( 5 10, 6 10, 5.5 9 )" ) ); + QCOMPARE( l37.boundingBox(), QgsRectangle( 5, 9, 6.125, 10.25 ) ); + l37.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( -1, 7 ) ); + QgsRectangle r = l37.boundingBox(); + QGSCOMPARENEAR( r.xMinimum(), -3.014, 0.01 ); + QGSCOMPARENEAR( r.xMaximum(), 14.014, 0.01 ); + QGSCOMPARENEAR( r.yMinimum(), -7.0146, 0.01 ); + QGSCOMPARENEAR( r.yMaximum(), 12.4988, 0.01 ); + l37.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( -3, 10 ) ); + r = l37.boundingBox(); + QGSCOMPARENEAR( r.xMinimum(), -10.294, 0.01 ); + QGSCOMPARENEAR( r.xMaximum(), 12.294, 0.01 ); + QGSCOMPARENEAR( r.yMinimum(), 9, 0.01 ); + QGSCOMPARENEAR( r.yMaximum(), 31.856, 0.01 ); + l37.deleteVertex( QgsVertexId( 0, 0, 1 ) ); + r = l37.boundingBox(); + QGSCOMPARENEAR( r.xMinimum(), 5, 0.01 ); + QGSCOMPARENEAR( r.xMaximum(), 6.125, 0.01 ); + QGSCOMPARENEAR( r.yMinimum(), 9, 0.01 ); + QGSCOMPARENEAR( r.yMaximum(), 10.25, 0.01 ); + + //angle + QgsCircularString l38; + ( void )l38.vertexAngle( QgsVertexId() ); //just want no crash + ( void )l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ); //just want no crash + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) ); + ( void )l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ); //just want no crash, any answer is meaningless + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) ); + ( void )l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ); //just want no crash, any answer is meaningless + ( void )l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ); //just want no crash, any answer is meaningless + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 1, 1 ) << QgsPoint( 0, 2 ) ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 0, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 4.712389, 0.0001 ); + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 2 ) << QgsPoint( 1, 1 ) << QgsPoint( 0, 0 ) ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 3.141593, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 4.712389, 0.0001 ); + ( void )l38.vertexAngle( QgsVertexId( 0, 0, 20 ) ); // no crash + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 1, 1 ) << QgsPoint( 0, 2 ) + << QgsPoint( -1, 3 ) << QgsPoint( 0, 4 ) ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 0, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 4.712389, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 3 ) ), 0, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 4 ) ), 1.5708, 0.0001 ); + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 4 ) << QgsPoint( -1, 3 ) << QgsPoint( 0, 2 ) + << QgsPoint( 1, 1 ) << QgsPoint( 0, 0 ) ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 4.712389, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 3.141592, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 3 ) ), 3.141592, 0.0001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 4 ) ), 4.712389, 0.0001 ); + + //closed circular string + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) << QgsPoint( 0, 0 ) ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 0, 0.00001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 3.141592, 0.00001 ); + QGSCOMPARENEAR( l38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 0, 0.00001 ); + + //removing a vertex from a 3 point circular string should remove the whole line + QgsCircularString l39; + l39.setPoints( QList() << QgsPoint( 0, 0 ) << QgsPoint( 1, 1 ) << QgsPoint( 0, 2 ) ); + QCOMPARE( l39.numPoints(), 3 ); + l39.deleteVertex( QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( l39.numPoints(), 0 ); + + //boundary + QgsCircularString boundary1; + QVERIFY( !boundary1.boundary() ); + boundary1.setPoints( QList() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) << QgsPoint( 1, 1 ) ); + QgsAbstractGeometry *boundary = boundary1.boundary(); + QgsMultiPointV2 *mpBoundary = dynamic_cast< QgsMultiPointV2 * >( boundary ); + QVERIFY( mpBoundary ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->x(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->y(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->x(), 1.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->y(), 1.0 ); + delete boundary; + + // closed string = no boundary + boundary1.setPoints( QList() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) << QgsPoint( 1, 1 ) << QgsPoint( 0, 0 ) ); + QVERIFY( !boundary1.boundary() ); + + //boundary with z + boundary1.setPoints( QList() << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 10 ) << QgsPoint( QgsWkbTypes::PointZ, 1, 0, 15 ) << QgsPoint( QgsWkbTypes::PointZ, 1, 1, 20 ) ); + boundary = boundary1.boundary(); + mpBoundary = dynamic_cast< QgsMultiPointV2 * >( boundary ); + QVERIFY( mpBoundary ); + QCOMPARE( mpBoundary->geometryN( 0 )->wkbType(), QgsWkbTypes::PointZ ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->x(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->y(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->z(), 10.0 ); + QCOMPARE( mpBoundary->geometryN( 1 )->wkbType(), QgsWkbTypes::PointZ ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->x(), 1.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->y(), 1.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->z(), 20.0 ); + delete boundary; + + // addToPainterPath (note most tests are in test_qgsgeometry.py) + QgsCircularString path; + QPainterPath pPath; + path.addToPainterPath( pPath ); + QVERIFY( pPath.isEmpty() ); + path.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) + << QgsPoint( QgsWkbTypes::PointZ, 21, 2, 3 ) ); + path.addToPainterPath( pPath ); + QGSCOMPARENEAR( pPath.currentPosition().x(), 21.0, 0.01 ); + QGSCOMPARENEAR( pPath.currentPosition().y(), 2.0, 0.01 ); + QVERIFY( !pPath.isEmpty() ); + + // even number of points - should still work + pPath = QPainterPath(); + path.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) ); + path.addToPainterPath( pPath ); + QGSCOMPARENEAR( pPath.currentPosition().x(), 11.0, 0.01 ); + QGSCOMPARENEAR( pPath.currentPosition().y(), 12.0, 0.01 ); + QVERIFY( !pPath.isEmpty() ); + + // toCurveType + QgsCircularString curveLine1; + curveLine1.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 22 ) ); + std::unique_ptr< QgsCurve > curveType( curveLine1.toCurveType() ); + QCOMPARE( curveType->wkbType(), QgsWkbTypes::CircularString ); + QCOMPARE( curveType->numPoints(), 3 ); + QCOMPARE( curveType->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( 1, 2 ) ); + QCOMPARE( curveType->vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( 11, 12 ) ); + QCOMPARE( curveType->vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( 1, 22 ) ); +} + + void TestQgsGeometry::lineString() { //test constructors @@ -1042,6 +2346,20 @@ void TestQgsGeometry::lineString() QCOMPARE( fromArray5.yAt( 1 ), 12.0 ); QCOMPARE( fromArray5.xAt( 2 ), 3.0 ); QCOMPARE( fromArray5.yAt( 2 ), 13.0 ); + // unbalanced -> z truncated + zz = QVector< double >() << 21 << 22 << 23 << 24; + fromArray5 = QgsLineString( xx, yy, zz ); + QCOMPARE( fromArray5.wkbType(), QgsWkbTypes::LineStringZ ); + QCOMPARE( fromArray5.numPoints(), 3 ); + QCOMPARE( fromArray5.xAt( 0 ), 1.0 ); + QCOMPARE( fromArray5.yAt( 0 ), 11.0 ); + QCOMPARE( fromArray5.zAt( 0 ), 21.0 ); + QCOMPARE( fromArray5.xAt( 1 ), 2.0 ); + QCOMPARE( fromArray5.yAt( 1 ), 12.0 ); + QCOMPARE( fromArray5.zAt( 1 ), 22.0 ); + QCOMPARE( fromArray5.xAt( 2 ), 3.0 ); + QCOMPARE( fromArray5.yAt( 2 ), 13.0 ); + QCOMPARE( fromArray5.zAt( 2 ), 23.0 ); // with m QVector< double > mm; xx = QVector< double >() << 1 << 2 << 3; @@ -1070,6 +2388,20 @@ void TestQgsGeometry::lineString() QCOMPARE( fromArray7.yAt( 1 ), 12.0 ); QCOMPARE( fromArray7.xAt( 2 ), 3.0 ); QCOMPARE( fromArray7.yAt( 2 ), 13.0 ); + // unbalanced -> m truncated + mm = QVector< double >() << 21 << 22 << 23 << 24; + fromArray7 = QgsLineString( xx, yy, QVector< double >(), mm ); + QCOMPARE( fromArray7.wkbType(), QgsWkbTypes::LineStringM ); + QCOMPARE( fromArray7.numPoints(), 3 ); + QCOMPARE( fromArray7.xAt( 0 ), 1.0 ); + QCOMPARE( fromArray7.yAt( 0 ), 11.0 ); + QCOMPARE( fromArray7.mAt( 0 ), 21.0 ); + QCOMPARE( fromArray7.xAt( 1 ), 2.0 ); + QCOMPARE( fromArray7.yAt( 1 ), 12.0 ); + QCOMPARE( fromArray7.mAt( 1 ), 22.0 ); + QCOMPARE( fromArray7.xAt( 2 ), 3.0 ); + QCOMPARE( fromArray7.yAt( 2 ), 13.0 ); + QCOMPARE( fromArray7.mAt( 2 ), 23.0 ); // zm xx = QVector< double >() << 1 << 2 << 3; yy = QVector< double >() << 11 << 12 << 13; @@ -1092,6 +2424,14 @@ void TestQgsGeometry::lineString() QCOMPARE( fromArray8.mAt( 2 ), 33.0 ); // from QList + QgsLineString fromPtsA = QgsLineString( QVector< QgsPoint >() ); + QVERIFY( fromPtsA.isEmpty() ); + QCOMPARE( fromPtsA.wkbType(), QgsWkbTypes::LineString ); + + fromPtsA = QgsLineString( QVector< QgsPoint >() << QgsPoint( 1, 2, 0, 4, QgsWkbTypes::PointM ) ); + QCOMPARE( fromPtsA.numPoints(), 1 ); + QCOMPARE( fromPtsA.wkbType(), QgsWkbTypes::LineStringM ); + QList ptsA; ptsA << QgsPointXY( 1, 2 ) << QgsPointXY( 11, 12 ) << QgsPointXY( 21, 22 ); QgsLineString fromPts( ptsA ); @@ -1228,6 +2568,9 @@ void TestQgsGeometry::lineString() QVERIFY( !l8.isMeasure() ); QCOMPARE( l8.wkbType(), QgsWkbTypes::LineString ); QVERIFY( !l8.hasCurvedSegments() ); + QgsPointSequence pts; + l8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 2, 3 ) << QgsPoint( 3, 4 ) ); //setPoints with empty list, should clear linestring l8.setPoints( QgsPointSequence() ); @@ -1238,6 +2581,8 @@ void TestQgsGeometry::lineString() QCOMPARE( l8.ringCount(), 0 ); QCOMPARE( l8.partCount(), 0 ); QCOMPARE( l8.wkbType(), QgsWkbTypes::LineString ); + l8.points( pts ); + QVERIFY( pts.isEmpty() ); //setPoints with z l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 2, 3, 4 ) ); @@ -1245,6 +2590,8 @@ void TestQgsGeometry::lineString() QVERIFY( l8.is3D() ); QVERIFY( !l8.isMeasure() ); QCOMPARE( l8.wkbType(), QgsWkbTypes::LineStringZ ); + l8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 2, 3, 4 ) ); //setPoints with 25d l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point25D, 1, 2, 4 ) << QgsPoint( QgsWkbTypes::Point25D, 2, 3, 4 ) ); @@ -1253,6 +2600,8 @@ void TestQgsGeometry::lineString() QVERIFY( !l8.isMeasure() ); QCOMPARE( l8.wkbType(), QgsWkbTypes::LineString25D ); QCOMPARE( l8.pointN( 0 ), QgsPoint( QgsWkbTypes::Point25D, 1, 2, 4 ) ); + l8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::Point25D, 1, 2, 4 ) << QgsPoint( QgsWkbTypes::Point25D, 2, 3, 4 ) ); //setPoints with m l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) << QgsPoint( QgsWkbTypes::PointM, 2, 3, 0, 4 ) ); @@ -1260,6 +2609,8 @@ void TestQgsGeometry::lineString() QVERIFY( !l8.is3D() ); QVERIFY( l8.isMeasure() ); QCOMPARE( l8.wkbType(), QgsWkbTypes::LineStringM ); + l8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) << QgsPoint( QgsWkbTypes::PointM, 2, 3, 0, 4 ) ); //setPoints with zm l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 4, 5 ) << QgsPoint( QgsWkbTypes::PointZM, 2, 3, 4, 5 ) ); @@ -1267,6 +2618,8 @@ void TestQgsGeometry::lineString() QVERIFY( l8.is3D() ); QVERIFY( l8.isMeasure() ); QCOMPARE( l8.wkbType(), QgsWkbTypes::LineStringZM ); + l8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 4, 5 ) << QgsPoint( QgsWkbTypes::PointZM, 2, 3, 4, 5 ) ); //setPoints with MIXED dimensionality of points l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 4, 5 ) << QgsPoint( QgsWkbTypes::PointM, 2, 3, 0, 5 ) ); @@ -1274,6 +2627,8 @@ void TestQgsGeometry::lineString() QVERIFY( l8.is3D() ); QVERIFY( l8.isMeasure() ); QCOMPARE( l8.wkbType(), QgsWkbTypes::LineStringZM ); + l8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 4, 5 ) << QgsPoint( QgsWkbTypes::PointZM, 2, 3, 0, 5 ) ); //test point QCOMPARE( l8.pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 1, 2, 4, 5 ) ); @@ -1537,6 +2892,8 @@ void TestQgsGeometry::lineString() QVERIFY( !( e5 == e6 ) ); //different m values QVERIFY( e5 != e6 ); + QVERIFY( e6 != QgsCircularString() ); + //close/isClosed QgsLineString l11; QVERIFY( !l11.isClosed() ); @@ -2357,30 +3714,30 @@ void TestQgsGeometry::lineString() QgsLineString l35; bool leftOf = false; p = QgsPoint(); // reset all coords to zero - ( void )l35.closestSegment( QgsPoint( 1, 2 ), p, v, 0, 0 ); //empty line, just want no crash + ( void )l35.closestSegment( QgsPoint( 1, 2 ), p, v ); //empty line, just want no crash l35.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) ); - QVERIFY( l35.closestSegment( QgsPoint( 5, 10 ), p, v, 0, 0 ) < 0 ); + QVERIFY( l35.closestSegment( QgsPoint( 5, 10 ), p, v ) < 0 ); l35.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 10, 10 ) ); - QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 4, 11 ), p, v, &leftOf, 0 ), 2.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 4, 11 ), p, v, &leftOf ), 2.0, 4 * DBL_EPSILON ); QCOMPARE( p, QgsPoint( 5, 10 ) ); QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); QCOMPARE( leftOf, true ); - QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 8, 11 ), p, v, &leftOf, 0 ), 1.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 8, 11 ), p, v, &leftOf ), 1.0, 4 * DBL_EPSILON ); QCOMPARE( p, QgsPoint( 8, 10 ) ); QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); QCOMPARE( leftOf, true ); - QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 8, 9 ), p, v, &leftOf, 0 ), 1.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 8, 9 ), p, v, &leftOf ), 1.0, 4 * DBL_EPSILON ); QCOMPARE( p, QgsPoint( 8, 10 ) ); QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); QCOMPARE( leftOf, false ); - QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 11, 9 ), p, v, &leftOf, 0 ), 2.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 11, 9 ), p, v, &leftOf ), 2.0, 4 * DBL_EPSILON ); QCOMPARE( p, QgsPoint( 10, 10 ) ); QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); QCOMPARE( leftOf, false ); l35.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 10, 10 ) << QgsPoint( 10, 15 ) ); - QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 11, 12 ), p, v, &leftOf, 0 ), 1.0, 4 * DBL_EPSILON ); + QGSCOMPARENEAR( l35.closestSegment( QgsPoint( 11, 12 ), p, v, &leftOf ), 1.0, 4 * DBL_EPSILON ); QCOMPARE( p, QgsPoint( 10, 12 ) ); QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); QCOMPARE( leftOf, false ); @@ -2525,8 +3882,26 @@ void TestQgsGeometry::lineString() QCOMPARE( extend1.pointN( 0 ), QgsPoint( QgsWkbTypes::Point, -1, 0 ) ); QCOMPARE( extend1.pointN( 1 ), QgsPoint( QgsWkbTypes::Point, 1, 0 ) ); QCOMPARE( extend1.pointN( 2 ), QgsPoint( QgsWkbTypes::Point, 1, 3 ) ); -} + // addToPainterPath (note most tests are in test_qgsgeometry.py) + QgsLineString path; + QPainterPath pPath; + path.addToPainterPath( pPath ); + QVERIFY( pPath.isEmpty() ); + path.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) ); + path.addToPainterPath( pPath ); + QVERIFY( !pPath.isEmpty() ); + + // toCurveType + QgsLineString curveLine1; + curveLine1.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) ); + std::unique_ptr< QgsCompoundCurve > curveType( curveLine1.toCurveType() ); + QCOMPARE( curveType->wkbType(), QgsWkbTypes::CompoundCurve ); + QCOMPARE( curveType->numPoints(), 2 ); + QCOMPARE( curveType->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( 1, 2 ) ); + QCOMPARE( curveType->vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( 11, 12 ) ); + +} void TestQgsGeometry::polygon() { //test constructor @@ -2685,6 +4060,13 @@ void TestQgsGeometry::polygon() QCOMPARE( p6.interiorRing( 0 ), ring ); QVERIFY( !p6.interiorRing( 1 ) ); + QgsCoordinateSequence seq = p6.coordinateSequence(); + QCOMPARE( seq, QgsCoordinateSequence() << ( QgsRingSequence() << ( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) + << QgsPoint( 10, 0 ) << QgsPoint( 0, 0 ) ) + << ( QgsPointSequence() << QgsPoint( 1, 1 ) << QgsPoint( 1, 9 ) << QgsPoint( 9, 9 ) + << QgsPoint( 9, 1 ) << QgsPoint( 1, 1 ) ) ) ); + QCOMPARE( p6.nCoordinates(), 10 ); + //add non-closed interior ring, should be closed automatically ring = new QgsLineString(); ring->setPoints( QgsPointSequence() << QgsPoint( 0.1, 0.1 ) << QgsPoint( 0.1, 0.9 ) << QgsPoint( 0.9, 0.9 ) @@ -3006,6 +4388,7 @@ void TestQgsGeometry::polygon() p10.addInteriorRing( ring ); QVERIFY( !( p10 == p10b ) ); QVERIFY( p10 != p10b ); + ring = new QgsLineString(); ring->setPoints( QgsPointSequence() << QgsPoint( 2, 1 ) << QgsPoint( 2, 9 ) << QgsPoint( 9, 9 ) @@ -3063,6 +4446,26 @@ void TestQgsGeometry::polygon() //surfaceToPolygon - should be identical given polygon has no curves std::unique_ptr< QgsPolygonV2 > surface( p12.surfaceToPolygon() ); QCOMPARE( *surface, p12 ); + //toPolygon - should be identical given polygon has no curves + std::unique_ptr< QgsPolygonV2 > toP( p12.toPolygon() ); + QCOMPARE( *toP, p12 ); + + //toCurveType + std::unique_ptr< QgsCurvePolygon > curveType( p12.toCurveType() ); + QCOMPARE( curveType->wkbType(), QgsWkbTypes::CurvePolygonZM ); + QCOMPARE( curveType->exteriorRing()->numPoints(), 5 ); + QCOMPARE( curveType->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 5 ) ); + QCOMPARE( curveType->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( QgsWkbTypes::PointZM, 0, 10, 2, 6 ) ); + QCOMPARE( curveType->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( QgsWkbTypes::PointZM, 10, 10, 3, 7 ) ); + QCOMPARE( curveType->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 3 ) ), QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4, 8 ) ); + QCOMPARE( curveType->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 4 ) ), QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 9 ) ); + QCOMPARE( curveType->numInteriorRings(), 1 ); + QCOMPARE( curveType->interiorRing( 0 )->numPoints(), 5 ); + QCOMPARE( curveType->interiorRing( 0 )->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 2 ) ); + QCOMPARE( curveType->interiorRing( 0 )->vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( QgsWkbTypes::PointZM, 1, 9, 2, 3 ) ); + QCOMPARE( curveType->interiorRing( 0 )->vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( QgsWkbTypes::PointZM, 9, 9, 3, 6 ) ); + QCOMPARE( curveType->interiorRing( 0 )->vertexAt( QgsVertexId( 0, 0, 3 ) ), QgsPoint( QgsWkbTypes::PointZM, 9, 1, 4, 4 ) ); + QCOMPARE( curveType->interiorRing( 0 )->vertexAt( QgsVertexId( 0, 0, 4 ) ), QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 7 ) ); //to/fromWKB QgsPolygonV2 p16; @@ -3199,7 +4602,7 @@ void TestQgsGeometry::polygon() exportPolygon.setExteriorRing( ext ); // GML document for compare - QDomDocument doc( "gml" ); + QDomDocument doc( QStringLiteral( "gml" ) ); // as GML2 QString expectedSimpleGML2( QStringLiteral( "0,0 0,10 10,10 10,0 0,0" ) ); @@ -3210,7 +4613,7 @@ void TestQgsGeometry::polygon() QCOMPARE( elemToString( exportPolygon.asGML3( doc ) ), expectedSimpleGML3 ); // as JSON - QString expectedSimpleJson( "{\"type\": \"Polygon\", \"coordinates\": [[ [0, 0], [0, 10], [10, 10], [10, 0], [0, 0]]] }" ); + QString expectedSimpleJson( QStringLiteral( "{\"type\": \"Polygon\", \"coordinates\": [[ [0, 0], [0, 10], [10, 10], [10, 0], [0, 0]]] }" ) ); QCOMPARE( exportPolygon.asJSON(), expectedSimpleJson ); ring = new QgsLineString(); @@ -3376,7 +4779,7 @@ void TestQgsGeometry::polygon() QgsLineString removeRingsRing1; removeRingsRing1.setPoints( QList() << QgsPoint( 0.1, 0.1 ) << QgsPoint( 0.2, 0.1 ) << QgsPoint( 0.2, 0.2 ) << QgsPoint( 0.1, 0.1 ) ); QgsLineString removeRingsRing2; - removeRingsRing1.setPoints( QList() << QgsPoint( 0.6, 0.8 ) << QgsPoint( 0.9, 0.8 ) << QgsPoint( 0.9, 0.9 ) << QgsPoint( 0.6, 0.8 ) ); + removeRingsRing2.setPoints( QList() << QgsPoint( 0.6, 0.8 ) << QgsPoint( 0.9, 0.8 ) << QgsPoint( 0.9, 0.9 ) << QgsPoint( 0.6, 0.8 ) ); removeRings1.setInteriorRings( QList< QgsCurve * >() << removeRingsRing1.clone() << removeRingsRing2.clone() ); // remove ring with size filter @@ -3387,6 +4790,680 @@ void TestQgsGeometry::polygon() removeRings1.removeInteriorRings(); QCOMPARE( removeRings1.numInteriorRings(), 0 ); + // cast + QVERIFY( !QgsPolygonV2().cast( nullptr ) ); + QgsPolygonV2 pCast; + QVERIFY( QgsPolygonV2().cast( &pCast ) ); + QgsPolygonV2 pCast2; + pCast2.fromWkt( QStringLiteral( "PolygonZ((0 0 0, 0 1 1, 1 0 2, 0 0 0))" ) ); + QVERIFY( QgsPolygonV2().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "PolygonM((0 0 1, 0 1 2, 1 0 3, 0 0 1))" ) ); + QVERIFY( QgsPolygonV2().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "PolygonZM((0 0 0 1, 0 1 1 2, 1 0 2 3, 0 0 0 1))" ) ); + QVERIFY( QgsPolygonV2().cast( &pCast2 ) ); + + //transform + //CRS transform + QgsCoordinateReferenceSystem sourceSrs; + sourceSrs.createFromSrid( 3994 ); + QgsCoordinateReferenceSystem destSrs; + destSrs.createFromSrid( 4202 ); // want a transform with ellipsoid change + QgsCoordinateTransform tr( sourceSrs, destSrs ); + + // 2d CRS transform + QgsPolygonV2 pTransform; + QgsLineString l21; + l21.setPoints( QgsPointSequence() << QgsPoint( 6374985, -3626584 ) + << QgsPoint( 6274985, -3526584 ) + << QgsPoint( 6474985, -3526584 ) + << QgsPoint( 6374985, -3626584 ) ); + pTransform.setExteriorRing( l21.clone() ); + pTransform.addInteriorRing( l21.clone() ); + pTransform.transform( tr, QgsCoordinateTransform::ForwardTransform ); + const QgsLineString *extR = static_cast< const QgsLineString * >( pTransform.exteriorRing() ); + QGSCOMPARENEAR( extR->pointN( 0 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 0 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).x(), 174.581448, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).x(), 176.958633, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( pTransform.exteriorRing()->boundingBox().xMinimum(), 174.581448, 0.001 ); + QGSCOMPARENEAR( pTransform.exteriorRing()->boundingBox().yMinimum(), -39.724, 0.001 ); + QGSCOMPARENEAR( pTransform.exteriorRing()->boundingBox().xMaximum(), 176.959, 0.001 ); + QGSCOMPARENEAR( pTransform.exteriorRing()->boundingBox().yMaximum(), -38.7999, 0.001 ); + const QgsLineString *intR = static_cast< const QgsLineString * >( pTransform.interiorRing( 0 ) ); + QGSCOMPARENEAR( intR->pointN( 0 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 0 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).x(), 174.581448, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).x(), 176.958633, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( pTransform.interiorRing( 0 )->boundingBox().xMinimum(), 174.581448, 0.001 ); + QGSCOMPARENEAR( pTransform.interiorRing( 0 )->boundingBox().yMinimum(), -39.724, 0.001 ); + QGSCOMPARENEAR( pTransform.interiorRing( 0 )->boundingBox().xMaximum(), 176.959, 0.001 ); + QGSCOMPARENEAR( pTransform.interiorRing( 0 )->boundingBox().yMaximum(), -38.7999, 0.001 ); + + //3d CRS transform + QgsLineString l22; + l22.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 6374985, -3626584, 1, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 6274985, -3526584, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 6474985, -3526584, 5, 6 ) + << QgsPoint( QgsWkbTypes::PointZM, 6374985, -3626584, 1, 2 ) ); + pTransform.clear(); + pTransform.setExteriorRing( l22.clone() ); + pTransform.addInteriorRing( l22.clone() ); + pTransform.transform( tr, QgsCoordinateTransform::ForwardTransform ); + extR = static_cast< const QgsLineString * >( pTransform.exteriorRing() ); + QGSCOMPARENEAR( extR->pointN( 0 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 0 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 0 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 0 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).x(), 174.581448, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).x(), 176.958633, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).z(), 5.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).m(), 6.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( pTransform.exteriorRing()->boundingBox().xMinimum(), 174.581448, 0.001 ); + QGSCOMPARENEAR( pTransform.exteriorRing()->boundingBox().yMinimum(), -39.724, 0.001 ); + QGSCOMPARENEAR( pTransform.exteriorRing()->boundingBox().xMaximum(), 176.959, 0.001 ); + QGSCOMPARENEAR( pTransform.exteriorRing()->boundingBox().yMaximum(), -38.7999, 0.001 ); + intR = static_cast< const QgsLineString * >( pTransform.interiorRing( 0 ) ); + QGSCOMPARENEAR( intR->pointN( 0 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 0 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 0 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 0 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).x(), 174.581448, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).x(), 176.958633, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).z(), 5.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).m(), 6.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( pTransform.interiorRing( 0 )->boundingBox().xMinimum(), 174.581448, 0.001 ); + QGSCOMPARENEAR( pTransform.interiorRing( 0 )->boundingBox().yMinimum(), -39.724, 0.001 ); + QGSCOMPARENEAR( pTransform.interiorRing( 0 )->boundingBox().xMaximum(), 176.959, 0.001 ); + QGSCOMPARENEAR( pTransform.interiorRing( 0 )->boundingBox().yMaximum(), -38.7999, 0.001 ); + + //reverse transform + pTransform.transform( tr, QgsCoordinateTransform::ReverseTransform ); + extR = static_cast< const QgsLineString * >( pTransform.exteriorRing() ); + QGSCOMPARENEAR( extR->pointN( 0 ).x(), 6374984, 100 ); + QGSCOMPARENEAR( extR->pointN( 0 ).y(), -3626584, 100 ); + QGSCOMPARENEAR( extR->pointN( 0 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 0 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).x(), 6274984, 100 ); + QGSCOMPARENEAR( extR->pointN( 1 ).y(), -3526584, 100 ); + QGSCOMPARENEAR( extR->pointN( 1 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).x(), 6474984, 100 ); + QGSCOMPARENEAR( extR->pointN( 2 ).y(), -3526584, 100 ); + QGSCOMPARENEAR( extR->pointN( 2 ).z(), 5.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).m(), 6.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).x(), 6374984, 100 ); + QGSCOMPARENEAR( extR->pointN( 3 ).y(), -3626584, 100 ); + QGSCOMPARENEAR( extR->pointN( 3 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( pTransform.exteriorRing()->boundingBox().xMinimum(), 6274984, 100 ); + QGSCOMPARENEAR( pTransform.exteriorRing()->boundingBox().yMinimum(), -3626584, 100 ); + QGSCOMPARENEAR( pTransform.exteriorRing()->boundingBox().xMaximum(), 6474984, 100 ); + QGSCOMPARENEAR( pTransform.exteriorRing()->boundingBox().yMaximum(), -3526584, 100 ); + intR = static_cast< const QgsLineString * >( pTransform.interiorRing( 0 ) ); + QGSCOMPARENEAR( intR->pointN( 0 ).x(), 6374984, 100 ); + QGSCOMPARENEAR( intR->pointN( 0 ).y(), -3626584, 100 ); + QGSCOMPARENEAR( intR->pointN( 0 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 0 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).x(), 6274984, 100 ); + QGSCOMPARENEAR( intR->pointN( 1 ).y(), -3526584, 100 ); + QGSCOMPARENEAR( intR->pointN( 1 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).x(), 6474984, 100 ); + QGSCOMPARENEAR( intR->pointN( 2 ).y(), -3526584, 100 ); + QGSCOMPARENEAR( intR->pointN( 2 ).z(), 5.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).m(), 6.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).x(), 6374984, 100 ); + QGSCOMPARENEAR( intR->pointN( 3 ).y(), -3626584, 100 ); + QGSCOMPARENEAR( intR->pointN( 3 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().xMinimum(), 6274984, 100 ); + QGSCOMPARENEAR( intR->boundingBox().yMinimum(), -3626584, 100 ); + QGSCOMPARENEAR( intR->boundingBox().xMaximum(), 6474984, 100 ); + QGSCOMPARENEAR( intR->boundingBox().yMaximum(), -3526584, 100 ); + + //z value transform + pTransform.transform( tr, QgsCoordinateTransform::ForwardTransform, true ); + extR = static_cast< const QgsLineString * >( pTransform.exteriorRing() ); + QGSCOMPARENEAR( extR->pointN( 0 ).z(), -19.249066, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).z(), -19.148357, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).z(), -19.092128, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).z(), -19.249066, 0.001 ); + intR = static_cast< const QgsLineString * >( pTransform.interiorRing( 0 ) ); + QGSCOMPARENEAR( intR->pointN( 0 ).z(), -19.249066, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).z(), -19.148357, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).z(), -19.092128, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).z(), -19.249066, 0.001 ); + pTransform.transform( tr, QgsCoordinateTransform::ReverseTransform, true ); + extR = static_cast< const QgsLineString * >( pTransform.exteriorRing() ); + QGSCOMPARENEAR( extR->pointN( 0 ).z(), 1, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).z(), 3, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).z(), 5, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).z(), 1, 0.001 ); + intR = static_cast< const QgsLineString * >( pTransform.interiorRing( 0 ) ); + QGSCOMPARENEAR( intR->pointN( 0 ).z(), 1, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).z(), 3, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).z(), 5, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).z(), 1, 0.001 ); + + //QTransform transform + QTransform qtr = QTransform::fromScale( 2, 3 ); + QgsLineString l23; + l23.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 12, 23, 24 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) ); + QgsPolygonV2 pTransform2; + pTransform2.setExteriorRing( l23.clone() ); + pTransform2.addInteriorRing( l23.clone() ); + pTransform2.transform( qtr ); + + extR = static_cast< const QgsLineString * >( pTransform2.exteriorRing() ); + QGSCOMPARENEAR( extR->pointN( 0 ).x(), 2, 100 ); + QGSCOMPARENEAR( extR->pointN( 0 ).y(), 6, 100 ); + QGSCOMPARENEAR( extR->pointN( 0 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 0 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).x(), 22, 100 ); + QGSCOMPARENEAR( extR->pointN( 1 ).y(), 36, 100 ); + QGSCOMPARENEAR( extR->pointN( 1 ).z(), 13.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).m(), 14.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).x(), 2, 100 ); + QGSCOMPARENEAR( extR->pointN( 2 ).y(), 36, 100 ); + QGSCOMPARENEAR( extR->pointN( 2 ).z(), 23.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).m(), 24.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).x(), 2, 100 ); + QGSCOMPARENEAR( extR->pointN( 3 ).y(), 6, 100 ); + QGSCOMPARENEAR( extR->pointN( 3 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( pTransform2.exteriorRing()->boundingBox().xMinimum(), 2, 0.001 ); + QGSCOMPARENEAR( pTransform2.exteriorRing()->boundingBox().yMinimum(), 6, 0.001 ); + QGSCOMPARENEAR( pTransform2.exteriorRing()->boundingBox().xMaximum(), 22, 0.001 ); + QGSCOMPARENEAR( pTransform2.exteriorRing()->boundingBox().yMaximum(), 36, 0.001 ); + intR = static_cast< const QgsLineString * >( pTransform2.interiorRing( 0 ) ); + QGSCOMPARENEAR( intR->pointN( 0 ).x(), 2, 100 ); + QGSCOMPARENEAR( intR->pointN( 0 ).y(), 6, 100 ); + QGSCOMPARENEAR( intR->pointN( 0 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 0 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).x(), 22, 100 ); + QGSCOMPARENEAR( intR->pointN( 1 ).y(), 36, 100 ); + QGSCOMPARENEAR( intR->pointN( 1 ).z(), 13.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).m(), 14.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).x(), 2, 100 ); + QGSCOMPARENEAR( intR->pointN( 2 ).y(), 36, 100 ); + QGSCOMPARENEAR( intR->pointN( 2 ).z(), 23.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).m(), 24.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).x(), 2, 100 ); + QGSCOMPARENEAR( intR->pointN( 3 ).y(), 6, 100 ); + QGSCOMPARENEAR( intR->pointN( 3 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().xMinimum(), 2, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().yMinimum(), 6, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().xMaximum(), 22, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().yMaximum(), 36, 0.001 ); + + // closestSegment + QgsPoint pt; + QgsVertexId v; + bool leftOf = false; + QgsPolygonV2 empty; + ( void )empty.closestSegment( QgsPoint( 1, 2 ), pt, v ); // empty polygon, just want no crash + + QgsPolygonV2 p21; + QgsLineString p21ls; + p21ls.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 7, 12 ) << QgsPoint( 5, 15 ) << QgsPoint( 5, 10 ) ); + p21.setExteriorRing( p21ls.clone() ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 4, 11 ), pt, v, &leftOf ), 1.0, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5, 0.01 ); + QGSCOMPARENEAR( pt.y(), 11, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 3 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 8, 11 ), pt, v, &leftOf ), 2.0, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 7, 0.01 ); + QGSCOMPARENEAR( pt.y(), 12, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 6, 11.5 ), pt, v, &leftOf ), 0.125000, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 6.25, 0.01 ); + QGSCOMPARENEAR( pt.y(), 11.25, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, true ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 7, 16 ), pt, v, &leftOf ), 4.923077, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5.153846, 0.01 ); + QGSCOMPARENEAR( pt.y(), 14.769231, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 5.5, 13.5 ), pt, v, &leftOf ), 0.173077, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5.846154, 0.01 ); + QGSCOMPARENEAR( pt.y(), 13.730769, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, true ); + // point directly on segment + QCOMPARE( p21.closestSegment( QgsPoint( 5, 15 ), pt, v, &leftOf ), 0.0 ); + QCOMPARE( pt, QgsPoint( 5, 15 ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + // with interior ring + p21ls.setPoints( QgsPointSequence() << QgsPoint( 6, 11.5 ) << QgsPoint( 6.5, 12 ) << QgsPoint( 6, 13 ) << QgsPoint( 6, 11.5 ) ); + p21.addInteriorRing( p21ls.clone() ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 4, 11 ), pt, v, &leftOf ), 1.0, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5, 0.01 ); + QGSCOMPARENEAR( pt.y(), 11, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 3 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 8, 11 ), pt, v, &leftOf ), 2.0, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 7, 0.01 ); + QGSCOMPARENEAR( pt.y(), 12, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 6, 11.4 ), pt, v, &leftOf ), 0.01, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 6.0, 0.01 ); + QGSCOMPARENEAR( pt.y(), 11.5, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 1, 1 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 7, 16 ), pt, v, &leftOf ), 4.923077, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5.153846, 0.01 ); + QGSCOMPARENEAR( pt.y(), 14.769231, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 5.5, 13.5 ), pt, v, &leftOf ), 0.173077, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5.846154, 0.01 ); + QGSCOMPARENEAR( pt.y(), 13.730769, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, true ); + // point directly on segment + QCOMPARE( p21.closestSegment( QgsPoint( 6, 13 ), pt, v, &leftOf ), 0.0 ); + QCOMPARE( pt, QgsPoint( 6, 13 ) ); + QCOMPARE( v, QgsVertexId( 0, 1, 2 ) ); + + //nextVertex + QgsPolygonV2 p22; + QVERIFY( !p22.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 0, -2 ); + QVERIFY( !p22.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 0, 10 ); + QVERIFY( !p22.nextVertex( v, pt ) ); + QgsLineString lp22; + lp22.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 12 ) << QgsPoint( 1, 2 ) ); + p22.setExteriorRing( lp22.clone() ); + v = QgsVertexId( 0, 0, 4 ); //out of range + QVERIFY( !p22.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 0, -5 ); + QVERIFY( p22.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 0, -1 ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 0 ) ); + QCOMPARE( pt, QgsPoint( 1, 2 ) ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( pt, QgsPoint( 11, 12 ) ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( pt, QgsPoint( 1, 12 ) ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 3 ) ); + QCOMPARE( pt, QgsPoint( 1, 2 ) ); + v = QgsVertexId( 0, 1, 0 ); + QVERIFY( !p22.nextVertex( v, pt ) ); + v = QgsVertexId( 1, 0, 0 ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 1, 0, 1 ) ); //test that part number is maintained + QCOMPARE( pt, QgsPoint( 11, 12 ) ); + // add interior ring + lp22.setPoints( QgsPointSequence() << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) << QgsPoint( 11, 22 ) << QgsPoint( 11, 12 ) ); + p22.addInteriorRing( lp22.clone() ); + v = QgsVertexId( 0, 1, 4 ); //out of range + QVERIFY( !p22.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 1, -5 ); + QVERIFY( p22.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 1, -1 ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 1, 0 ) ); + QCOMPARE( pt, QgsPoint( 11, 12 ) ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 1, 1 ) ); + QCOMPARE( pt, QgsPoint( 21, 22 ) ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 1, 2 ) ); + QCOMPARE( pt, QgsPoint( 11, 22 ) ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 1, 3 ) ); + QCOMPARE( pt, QgsPoint( 11, 12 ) ); + v = QgsVertexId( 0, 2, 0 ); + QVERIFY( !p22.nextVertex( v, pt ) ); + v = QgsVertexId( 1, 1, 0 ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 1, 1, 1 ) ); //test that part number is maintained + QCOMPARE( pt, QgsPoint( 21, 22 ) ); + + // dropZValue + QgsPolygonV2 p23; + p23.dropZValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::Polygon ); + QgsLineString lp23; + lp23.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 12 ) << QgsPoint( 1, 2 ) ); + p23.setExteriorRing( lp23.clone() ); + p23.addInteriorRing( lp23.clone() ); + QCOMPARE( p23.wkbType(), QgsWkbTypes::Polygon ); + p23.dropZValue(); // not z + QCOMPARE( p23.wkbType(), QgsWkbTypes::Polygon ); + QCOMPARE( p23.exteriorRing()->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.exteriorRing() )->pointN( 0 ), QgsPoint( 1, 2 ) ); + QCOMPARE( p23.interiorRing( 0 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + // with z + lp23.setPoints( QgsPointSequence() << QgsPoint( 1, 2, 3 ) << QgsPoint( 11, 12, 13 ) << QgsPoint( 1, 12, 23 ) << QgsPoint( 1, 2, 3 ) ); + p23.clear(); + p23.setExteriorRing( lp23.clone() ); + p23.addInteriorRing( lp23.clone() ); + QCOMPARE( p23.wkbType(), QgsWkbTypes::PolygonZ ); + p23.dropZValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::Polygon ); + QCOMPARE( p23.exteriorRing()->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.exteriorRing() )->pointN( 0 ), QgsPoint( 1, 2 ) ); + QCOMPARE( p23.interiorRing( 0 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + // with zm + lp23.setPoints( QgsPointSequence() << QgsPoint( 1, 2, 3, 4 ) << QgsPoint( 11, 12, 13, 14 ) << QgsPoint( 1, 12, 23, 24 ) << QgsPoint( 1, 2, 3, 4 ) ); + p23.clear(); + p23.setExteriorRing( lp23.clone() ); + p23.addInteriorRing( lp23.clone() ); + QCOMPARE( p23.wkbType(), QgsWkbTypes::PolygonZM ); + p23.dropZValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::PolygonM ); + QCOMPARE( p23.exteriorRing()->wkbType(), QgsWkbTypes::LineStringM ); + QCOMPARE( static_cast< const QgsLineString *>( p23.exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) ); + QCOMPARE( p23.interiorRing( 0 )->wkbType(), QgsWkbTypes::LineStringM ); + QCOMPARE( static_cast< const QgsLineString *>( p23.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) ); + + // dropMValue + p23.clear(); + p23.dropMValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::Polygon ); + lp23.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 12 ) << QgsPoint( 1, 2 ) ); + p23.setExteriorRing( lp23.clone() ); + p23.addInteriorRing( lp23.clone() ); + QCOMPARE( p23.wkbType(), QgsWkbTypes::Polygon ); + p23.dropMValue(); // not zm + QCOMPARE( p23.wkbType(), QgsWkbTypes::Polygon ); + QCOMPARE( p23.exteriorRing()->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.exteriorRing() )->pointN( 0 ), QgsPoint( 1, 2 ) ); + QCOMPARE( p23.interiorRing( 0 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + // with m + lp23.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) << QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 13 ) << QgsPoint( QgsWkbTypes::PointM, 1, 12, 0, 23 ) << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) ); + p23.clear(); + p23.setExteriorRing( lp23.clone() ); + p23.addInteriorRing( lp23.clone() ); + QCOMPARE( p23.wkbType(), QgsWkbTypes::PolygonM ); + p23.dropMValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::Polygon ); + QCOMPARE( p23.exteriorRing()->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.exteriorRing() )->pointN( 0 ), QgsPoint( 1, 2 ) ); + QCOMPARE( p23.interiorRing( 0 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + // with zm + lp23.setPoints( QgsPointSequence() << QgsPoint( 1, 2, 3, 4 ) << QgsPoint( 11, 12, 13, 14 ) << QgsPoint( 1, 12, 23, 24 ) << QgsPoint( 1, 2, 3, 4 ) ); + p23.clear(); + p23.setExteriorRing( lp23.clone() ); + p23.addInteriorRing( lp23.clone() ); + QCOMPARE( p23.wkbType(), QgsWkbTypes::PolygonZM ); + p23.dropMValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::PolygonZ ); + QCOMPARE( p23.exteriorRing()->wkbType(), QgsWkbTypes::LineStringZ ); + QCOMPARE( static_cast< const QgsLineString *>( p23.exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) ); + QCOMPARE( p23.interiorRing( 0 )->wkbType(), QgsWkbTypes::LineStringZ ); + QCOMPARE( static_cast< const QgsLineString *>( p23.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) ); + + //vertexAngle + QgsPolygonV2 p24; + ( void )p24.vertexAngle( QgsVertexId() ); //just want no crash + ( void )p24.vertexAngle( QgsVertexId( 0, 0, 0 ) ); //just want no crash + ( void )p24.vertexAngle( QgsVertexId( 0, 1, 0 ) ); //just want no crash + QgsLineString l38; + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0.5, 0 ) << QgsPoint( 1, 0 ) + << QgsPoint( 2, 1 ) << QgsPoint( 1, 2 ) << QgsPoint( 0, 2 ) << QgsPoint( 0, 0 ) ); + p24.setExteriorRing( l38.clone() ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 2.35619, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 1.17809, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 0, 3 ) ), 0.0, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 0, 4 ) ), 5.10509, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 0, 5 ) ), 3.92699, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 0, 6 ) ), 2.35619, 0.00001 ); + p24.addInteriorRing( l38.clone() ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 1, 0 ) ), 2.35619, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 1, 1 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 1, 2 ) ), 1.17809, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 1, 3 ) ), 0.0, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 1, 4 ) ), 5.10509, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 1, 5 ) ), 3.92699, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 1, 6 ) ), 2.35619, 0.00001 ); + + //insert vertex + + //insert vertex in empty polygon + QgsPolygonV2 p25; + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 1, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 1, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( p25.isEmpty() ); + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0.5, 0 ) << QgsPoint( 1, 0 ) + << QgsPoint( 2, 1 ) << QgsPoint( 1, 2 ) << QgsPoint( 0, 2 ) << QgsPoint( 0, 0 ) ); + p25.setExteriorRing( l38.clone() ); + QVERIFY( p25.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 0.3, 0 ) ) ); + QCOMPARE( p25.nCoordinates(), 8 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 0 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 1 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 2 ), QgsPoint( 0.5, 0 ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 0, -1 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 0, 100 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 1, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + // first vertex + QVERIFY( p25.insertVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 0, 0.1 ) ) ); + QCOMPARE( p25.nCoordinates(), 9 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 0 ), QgsPoint( 0, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 1 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 2 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 3 ), QgsPoint( 0.5, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 7 ), QgsPoint( 0, 2 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 8 ), QgsPoint( 0, 0.1 ) ); + // last vertex + QVERIFY( p25.insertVertex( QgsVertexId( 0, 0, 9 ), QgsPoint( 0.1, 0.1 ) ) ); + QCOMPARE( p25.nCoordinates(), 10 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 0 ), QgsPoint( 0.1, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 1 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 2 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 3 ), QgsPoint( 0.5, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 8 ), QgsPoint( 0, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 9 ), QgsPoint( 0.1, 0.1 ) ); + // with interior ring + p25.addInteriorRing( l38.clone() ); + QCOMPARE( p25.nCoordinates(), 17 ); + QVERIFY( p25.insertVertex( QgsVertexId( 0, 1, 1 ), QgsPoint( 0.3, 0 ) ) ); + QCOMPARE( p25.nCoordinates(), 18 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 1 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 2 ), QgsPoint( 0.5, 0 ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 1, -1 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 1, 100 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 2, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + // first vertex in interior ring + QVERIFY( p25.insertVertex( QgsVertexId( 0, 1, 0 ), QgsPoint( 0, 0.1 ) ) ); + QCOMPARE( p25.nCoordinates(), 19 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 0, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 1 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 2 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 3 ), QgsPoint( 0.5, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 7 ), QgsPoint( 0, 2 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 8 ), QgsPoint( 0, 0.1 ) ); + // last vertex in interior ring + QVERIFY( p25.insertVertex( QgsVertexId( 0, 1, 9 ), QgsPoint( 0.1, 0.1 ) ) ); + QCOMPARE( p25.nCoordinates(), 20 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 0.1, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 1 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 2 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 3 ), QgsPoint( 0.5, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 8 ), QgsPoint( 0, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 9 ), QgsPoint( 0.1, 0.1 ) ); + + //move vertex + + //empty polygon + QgsPolygonV2 p26; + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( p26.isEmpty() ); + + //valid polygon + l38.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) << QgsPoint( 1, 2 ) ); + p26.setExteriorRing( l38.clone() ); + QVERIFY( p26.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( p26.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 16.0, 17.0 ) ) ); + QVERIFY( p26.moveVertex( QgsVertexId( 0, 0, 2 ), QgsPoint( 26.0, 27.0 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 0 ), QgsPoint( 6.0, 7.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 1 ), QgsPoint( 16.0, 17.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 2 ), QgsPoint( 26.0, 27.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 3 ), QgsPoint( 6.0, 7.0 ) ); + + //out of range + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 0, -1 ), QgsPoint( 3.0, 4.0 ) ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 0, 10 ), QgsPoint( 3.0, 4.0 ) ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 1, 0 ), QgsPoint( 3.0, 4.0 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 0 ), QgsPoint( 6.0, 7.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 1 ), QgsPoint( 16.0, 17.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 2 ), QgsPoint( 26.0, 27.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 3 ), QgsPoint( 6.0, 7.0 ) ); + + // with interior ring + p26.addInteriorRing( l38.clone() ); + QVERIFY( p26.moveVertex( QgsVertexId( 0, 1, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( p26.moveVertex( QgsVertexId( 0, 1, 1 ), QgsPoint( 16.0, 17.0 ) ) ); + QVERIFY( p26.moveVertex( QgsVertexId( 0, 1, 2 ), QgsPoint( 26.0, 27.0 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 6.0, 7.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.interiorRing( 0 ) )->pointN( 1 ), QgsPoint( 16.0, 17.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.interiorRing( 0 ) )->pointN( 2 ), QgsPoint( 26.0, 27.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.interiorRing( 0 ) )->pointN( 3 ), QgsPoint( 6.0, 7.0 ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 1, -1 ), QgsPoint( 3.0, 4.0 ) ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 1, 10 ), QgsPoint( 3.0, 4.0 ) ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 2, 0 ), QgsPoint( 3.0, 4.0 ) ) ); + + //delete vertex + + //empty polygon + QgsPolygonV2 p27; + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 1, 0 ) ) ); + QVERIFY( p27.isEmpty() ); + + //valid polygon + l38.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 5, 2 ) << QgsPoint( 6, 2 ) << QgsPoint( 7, 2 ) + << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) << QgsPoint( 1, 2 ) ); + + p27.setExteriorRing( l38.clone() ); + //out of range vertices + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 0, -1 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 0, 100 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 1, 1 ) ) ); + + //valid vertices + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 1 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 0 ), QgsPoint( 1.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 1 ), QgsPoint( 6.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 2 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 3 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 5 ), QgsPoint( 1.0, 2.0 ) ); + + // delete first vertex + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 0 ), QgsPoint( 6.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 1 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 2 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 3 ), QgsPoint( 21.0, 22.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 4 ), QgsPoint( 6.0, 2.0 ) ); + + // delete last vertex + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 4 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 0 ), QgsPoint( 21.0, 22.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 1 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 2 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 3 ), QgsPoint( 21.0, 22.0 ) ); + + // delete another vertex - should remove ring + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 1 ) ) ); + QVERIFY( !p27.exteriorRing() ); + + // with interior ring + p27.setExteriorRing( l38.clone() ); + p27.addInteriorRing( l38.clone() ); + + //out of range vertices + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 1, -1 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 1, 100 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 2, 1 ) ) ); + + //valid vertices + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 1, 1 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 1.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 1 ), QgsPoint( 6.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 2 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 3 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 5 ), QgsPoint( 1.0, 2.0 ) ); + + // delete first vertex + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 1, 0 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 6.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 1 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 2 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 3 ), QgsPoint( 21.0, 22.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 4 ), QgsPoint( 6.0, 2.0 ) ); + + // delete last vertex + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 1, 4 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 21.0, 22.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 1 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 2 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 3 ), QgsPoint( 21.0, 22.0 ) ); + + // delete another vertex - should remove ring + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 1, 1 ) ) ); + QCOMPARE( p27.numInteriorRings(), 0 ); + QVERIFY( p27.exteriorRing() ); + + // test that interior ring is "promoted" when exterior is removed + p27.addInteriorRing( l38.clone() ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( p27.numInteriorRings(), 1 ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( p27.numInteriorRings(), 1 ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( p27.numInteriorRings(), 1 ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( p27.numInteriorRings(), 0 ); + QVERIFY( p27.exteriorRing() ); + } void TestQgsGeometry::triangle() @@ -3410,11 +5487,16 @@ void TestQgsGeometry::triangle() QVERIFY( !t1.exteriorRing() ); QVERIFY( !t1.interiorRing( 0 ) ); + // invalid triangles + QgsTriangle invalid( QgsPointXY( 0, 0 ), QgsPointXY( 0, 0 ), QgsPointXY( 10, 10 ) ); + QVERIFY( invalid.isEmpty() ); + invalid = QgsTriangle( QPointF( 0, 0 ), QPointF( 0, 0 ), QPointF( 10, 10 ) ); + QVERIFY( invalid.isEmpty() ); //set exterior ring //try with no ring - QgsLineString *ext = 0; - t1.setExteriorRing( ext ); + std::unique_ptr< QgsLineString > ext; + t1.setExteriorRing( nullptr ); QVERIFY( t1.isEmpty() ); QCOMPARE( t1.numInteriorRings(), 0 ); QCOMPARE( t1.nCoordinates(), 0 ); @@ -3425,11 +5507,11 @@ void TestQgsGeometry::triangle() QCOMPARE( t1.wkbType(), QgsWkbTypes::Triangle ); //valid exterior ring - ext = new QgsLineString(); + ext.reset( new QgsLineString() ); ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) << QgsPoint( 0, 0 ) ); QVERIFY( ext->isClosed() ); - t1.setExteriorRing( ext ); + t1.setExteriorRing( ext->clone() ); QVERIFY( !t1.isEmpty() ); QCOMPARE( t1.numInteriorRings(), 0 ); QCOMPARE( t1.nCoordinates(), 4 ); @@ -3451,11 +5533,11 @@ void TestQgsGeometry::triangle() QCOMPARE( *( static_cast< const QgsLineString * >( t1.exteriorRing() ) ), *ext ); //set new ExteriorRing - ext = new QgsLineString(); + ext.reset( new QgsLineString() ); ext->setPoints( QgsPointSequence() << QgsPoint( 0, 10 ) << QgsPoint( 5, 5 ) << QgsPoint( 10, 10 ) << QgsPoint( 0, 10 ) ); QVERIFY( ext->isClosed() ); - t1.setExteriorRing( ext ); + t1.setExteriorRing( ext->clone() ); QVERIFY( !t1.isEmpty() ); QCOMPARE( t1.numInteriorRings(), 0 ); QCOMPARE( t1.nCoordinates(), 4 ); @@ -3476,32 +5558,42 @@ void TestQgsGeometry::triangle() //test that a non closed exterior ring will be automatically closed QgsTriangle t2; - ext = new QgsLineString(); + ext.reset( new QgsLineString() ); ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) ); QVERIFY( !ext->isClosed() ); - t2.setExteriorRing( ext ); + t2.setExteriorRing( ext.release() ); QVERIFY( !t2.isEmpty() ); QVERIFY( t2.exteriorRing()->isClosed() ); QCOMPARE( t2.nCoordinates(), 4 ); // invalid number of points - ext = new QgsLineString(); + ext.reset( new QgsLineString() ); t2.clear(); ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) ); - t2.setExteriorRing( ext ); + t2.setExteriorRing( ext.release() ); QVERIFY( t2.isEmpty() ); - ext = new QgsLineString(); + ext.reset( new QgsLineString() ); t2.clear(); ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) << QgsPoint( 5, 10 ) << QgsPoint( 8, 10 ) ); - t2.setExteriorRing( ext ); + t2.setExteriorRing( ext.release() ); QVERIFY( t2.isEmpty() ); // invalid exterior ring - ext = new QgsLineString(); + ext.reset( new QgsLineString() ); t2.clear(); ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) << QgsPoint( 5, 10 ) ); - t2.setExteriorRing( ext ); + t2.setExteriorRing( ext.release() ); + QVERIFY( t2.isEmpty() ); + + ext.reset( new QgsLineString() ); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 0, 0 ) ); + t2.setExteriorRing( ext.release() ); + QVERIFY( t2.isEmpty() ); + + ext.reset( new QgsLineString() ); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 0, 0 ) ); + t2.setExteriorRing( ext.release() ); QVERIFY( t2.isEmpty() ); // circular ring @@ -3590,10 +5682,10 @@ void TestQgsGeometry::triangle() // fromWkt QgsTriangle t5; - ext = new QgsLineString(); + ext.reset( new QgsLineString() ); ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 5 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 10, 2, 6 ) << QgsPoint( QgsWkbTypes::PointZM, 10, 10, 3, 7 ) ); - t5.setExteriorRing( ext ); + t5.setExteriorRing( ext.release() ); QString wkt = t5.asWkt(); QVERIFY( !wkt.isEmpty() ); QgsTriangle t6; @@ -3602,10 +5694,10 @@ void TestQgsGeometry::triangle() // conversion QgsPolygonV2 p1; - ext = new QgsLineString(); + ext.reset( new QgsLineString() ); ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 5 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 10, 2, 6 ) << QgsPoint( QgsWkbTypes::PointZM, 10, 10, 3, 7 ) ); - p1.setExteriorRing( ext ); + p1.setExteriorRing( ext.release() ); //toPolygon std::unique_ptr< QgsPolygonV2 > poly( t5.toPolygon() ); QCOMPARE( *poly, p1 ); @@ -3671,6 +5763,7 @@ void TestQgsGeometry::triangle() QVERIFY( !QgsTriangle().isScalene() ); QVERIFY( !QgsTriangle().isEquilateral() ); + // type of triangle QVERIFY( t7.isRight() ); QVERIFY( t7.isIsocele() ); QVERIFY( !t7.isScalene() ); @@ -3787,11 +5880,12 @@ void TestQgsGeometry::triangle() QGSCOMPARENEARPOINT( bis.at( 2 ).pointN( 1 ), QgsPoint( 0, 2.9289 ), 0.0001 ); // "deleted" method + ext.reset( new QgsLineString() ); QgsTriangle t11( QgsPoint( 0, 0 ), QgsPoint( 100, 100 ), QgsPoint( 0, 200 ) ); ext->setPoints( QgsPointSequence() << QgsPoint( 5, 5 ) << QgsPoint( 50, 50 ) << QgsPoint( 0, 25 ) << QgsPoint( 5, 5 ) ); - t11.addInteriorRing( ext ); + t11.addInteriorRing( ext.release() ); QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 100 100, 0 200, 0 0))" ) ); /* QList lc; @@ -3849,6 +5943,32 @@ void TestQgsGeometry::triangle() pt1 = QgsPoint( 0, 0 ); QVERIFY( !t11.moveVertex( id, pt1 ) ); + //toCurveType + QgsTriangle t12( QgsPoint( 7, 4 ), QgsPoint( 13, 3 ), QgsPoint( 9, 6 ) ); + std::unique_ptr< QgsCurvePolygon > curveType( t12.toCurveType() ); + QCOMPARE( curveType->wkbType(), QgsWkbTypes::CurvePolygon ); + QCOMPARE( curveType->exteriorRing()->numPoints(), 4 ); + QCOMPARE( curveType->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( 7, 4 ) ); + QCOMPARE( curveType->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( 13, 3 ) ); + QCOMPARE( curveType->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( 9, 6 ) ); + QCOMPARE( curveType->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 3 ) ), QgsPoint( 7, 4 ) ); + QCOMPARE( curveType->numInteriorRings(), 0 ); + + // boundary + QVERIFY( !QgsTriangle().boundary() ); + std::unique_ptr< QgsCurve > boundary( QgsTriangle( QgsPoint( 7, 4 ), QgsPoint( 13, 3 ), QgsPoint( 9, 6 ) ).boundary() ); + QCOMPARE( boundary->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( boundary->numPoints(), 4 ); + QCOMPARE( boundary->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( 7, 4 ) ); + QCOMPARE( boundary->vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( 13, 3 ) ); + QCOMPARE( boundary->vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( 9, 6 ) ); + QCOMPARE( boundary->vertexAt( QgsVertexId( 0, 0, 3 ) ), QgsPoint( 7, 4 ) ); + + // cast + QgsTriangle pCast; + QVERIFY( QgsPolygonV2().cast( &pCast ) ); + QgsTriangle pCast2( QgsPoint( 7, 4 ), QgsPoint( 13, 3 ), QgsPoint( 9, 6 ) );; + QVERIFY( QgsPolygonV2().cast( &pCast2 ) ); } void TestQgsGeometry::ellipse() @@ -4032,10 +6152,16 @@ void TestQgsGeometry::ellipse() QGSCOMPARENEARPOINT( q.at( 1 ), pts.at( 1 ), 2 ); QGSCOMPARENEARPOINT( q.at( 2 ), pts.at( 2 ), 2 ); QGSCOMPARENEARPOINT( q.at( 3 ), pts.at( 3 ), 2 ); - // linestring - QgsLineString *l = new QgsLineString(); - l = QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 0 ).toLineString( 4 ); + QVERIFY( QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 0 ).points( 2 ).isEmpty() ); // segments too low + + // linestring + std::unique_ptr< QgsLineString > l( new QgsLineString() ); + + l.reset( QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 0 ).toLineString( 2 ) ); + QVERIFY( l->isEmpty() ); // segments too low + + l.reset( QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 0 ).toLineString( 4 ) ); QCOMPARE( l->numPoints(), 5 ); // closed linestring QgsPointSequence pts_l; l->points( pts_l ); @@ -4043,9 +6169,12 @@ void TestQgsGeometry::ellipse() QCOMPARE( pts, pts_l ); // polygon - QgsPolygonV2 *p1 = new QgsPolygonV2(); + std::unique_ptr< QgsPolygonV2 > p1( new QgsPolygonV2() ); - p1 = QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 0 ).toPolygon( 4 ); + p1.reset( QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 0 ).toPolygon( 2 ) ); + QVERIFY( p1->isEmpty() ); // segments too low + + p1.reset( QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 0 ).toPolygon( 4 ) ); q = QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 0 ).quadrant(); QCOMPARE( p1->vertexAt( QgsVertexId( 0, 0, 0 ) ), q.at( 0 ) ); QCOMPARE( p1->vertexAt( QgsVertexId( 0, 0, 1 ) ), q.at( 1 ) ); @@ -4055,7 +6184,7 @@ void TestQgsGeometry::ellipse() QCOMPARE( 0, p1->numInteriorRings() ); QCOMPARE( 5, p1->exteriorRing()->numPoints() ); - p1 = QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 90 ).toPolygon( 4 ); + p1.reset( QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 90 ).toPolygon( 4 ) ); q = QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 90 ).quadrant(); QCOMPARE( p1->vertexAt( QgsVertexId( 0, 0, 0 ) ), q.at( 0 ) ); QCOMPARE( p1->vertexAt( QgsVertexId( 0, 0, 1 ) ), q.at( 1 ) ); @@ -4065,7 +6194,7 @@ void TestQgsGeometry::ellipse() QCOMPARE( 0, p1->numInteriorRings() ); QCOMPARE( 5, p1->exteriorRing()->numPoints() ); - p1 = elpq.toPolygon( 4 ); + p1.reset( elpq.toPolygon( 4 ) ); q = elpq.quadrant(); QCOMPARE( p1->vertexAt( QgsVertexId( 0, 0, 0 ) ), q.at( 0 ) ); QCOMPARE( p1->vertexAt( QgsVertexId( 0, 0, 1 ) ), q.at( 1 ) ); @@ -4076,24 +6205,26 @@ void TestQgsGeometry::ellipse() QCOMPARE( 5, p1->exteriorRing()->numPoints() ); // oriented bounding box - QVERIFY( QgsEllipse().orientedBoundingBox()->isEmpty() ); + std::unique_ptr< QgsPolygonV2 > ombb( QgsEllipse().orientedBoundingBox() ); + QVERIFY( ombb->isEmpty() ); elpq = QgsEllipse( QgsPoint( 0, 0 ), 5, 2 ); - QgsPolygonV2 *ombb = new QgsPolygonV2(); + ombb.reset( new QgsPolygonV2() ); QgsLineString *ext = new QgsLineString(); ext->setPoints( QgsPointSequence() << QgsPoint( 5, 2 ) << QgsPoint( 5, -2 ) << QgsPoint( -5, -2 ) << QgsPoint( -5, 2 ) ); ombb->setExteriorRing( ext ); - QCOMPARE( ombb->asWkt( 2 ), elpq.orientedBoundingBox()->asWkt( 2 ) ); + std::unique_ptr< QgsPolygonV2 >ombb2( elpq.orientedBoundingBox() ); + QCOMPARE( ombb->asWkt( 2 ), ombb2->asWkt( 2 ) ); elpq = QgsEllipse( QgsPoint( 0, 0 ), 5, 2.5, 45 ); - ombb = elpq.orientedBoundingBox(); + ombb.reset( elpq.orientedBoundingBox() ); QGSCOMPARENEARPOINT( ombb->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( 1.7678, 5.3033 ), 0.0001 ); QGSCOMPARENEARPOINT( ombb->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( 5.3033, 1.7678 ), 0.0001 ); QGSCOMPARENEARPOINT( ombb->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( -1.7678, -5.3033 ), 0.0001 ); QGSCOMPARENEARPOINT( ombb->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 3 ) ), QgsPoint( -5.3033, -1.7678 ), 0.0001 ); elpq = QgsEllipse( QgsPoint( 0, 0 ), 5, 2.5, 315 ); - ombb = elpq.orientedBoundingBox(); + ombb.reset( elpq.orientedBoundingBox() ); QGSCOMPARENEARPOINT( ombb->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( -5.3033, 1.7678 ), 0.0001 ); QGSCOMPARENEARPOINT( ombb->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( -1.7678, 5.3033 ), 0.0001 ); QGSCOMPARENEARPOINT( ombb->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( 5.3033, -1.7678 ), 0.0001 ); @@ -4101,7 +6232,8 @@ void TestQgsGeometry::ellipse() // bounding box QCOMPARE( QgsEllipse().boundingBox(), QgsRectangle() ); - QCOMPARE( QgsEllipse( QgsPoint( 0, 0 ), 5, 2 ).boundingBox(), QgsEllipse( QgsPoint( 0, 0 ), 5, 2 ).orientedBoundingBox()->boundingBox() ); + ombb.reset( QgsEllipse( QgsPoint( 0, 0 ), 5, 2 ).orientedBoundingBox() ); + QCOMPARE( QgsEllipse( QgsPoint( 0, 0 ), 5, 2 ).boundingBox(), ombb->boundingBox() ); QCOMPARE( QgsEllipse( QgsPoint( 0, 0 ), 5, 5 ).boundingBox(), QgsRectangle( QgsPointXY( -5, -5 ), QgsPointXY( 5, 5 ) ) ); QCOMPARE( QgsEllipse( QgsPoint( 0, 0 ), 5, 5, 60 ).boundingBox(), QgsRectangle( QgsPointXY( -5, -5 ), QgsPointXY( 5, 5 ) ) ); QCOMPARE( QgsEllipse( QgsPoint( 0, 0 ), 13, 9, 45 ).boundingBox().toString( 4 ).toStdString(), QgsRectangle( QgsPointXY( -11.1803, -11.1803 ), QgsPointXY( 11.1803, 11.1803 ) ).toString( 4 ).toStdString() ); @@ -4122,7 +6254,8 @@ void TestQgsGeometry::ellipse() // area QGSCOMPARENEAR( 31.4159, QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 0 ).area(), 0.0001 ); // perimeter - QGSCOMPARENEAR( QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 45 ).perimeter(), QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 45 ).toPolygon( 10000 )->perimeter(), 0.001 ); + p1.reset( QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 45 ).toPolygon( 10000 ) ); + QGSCOMPARENEAR( QgsEllipse( QgsPoint( 0, 0 ), 5, 2, 45 ).perimeter(), p1->perimeter(), 0.001 ); } @@ -4306,10 +6439,14 @@ void TestQgsGeometry::circle() QVERIFY( ptsPol.at( 4 ) == QgsPoint( -val, val ) ); // circular arc - QCOMPARE( QgsCircle( QgsPoint( 0, 0 ), 5 ).toCircularString()->asWkt( 2 ), QString( "CircularString (0 5, 5 0, 0 -5, -5 0, 0 5)" ) ); - QCOMPARE( QgsCircle( QgsPoint( 0, 0 ), 5 ).toCircularString( true )->asWkt( 2 ), QString( "CircularString (0 5, 5 0, 0 -5, -5 -0, 0 5)" ) ); - QCOMPARE( QgsCircle( QgsPoint( 0, 0 ), 5, 315 ).toCircularString()->asWkt( 2 ), QString( "CircularString (0 5, 5 0, 0 -5, -5 0, 0 5)" ) ); - QCOMPARE( QgsCircle( QgsPoint( 0, 0 ), 5, 315 ).toCircularString( true )->asWkt( 2 ), QString( "CircularString (-3.54 3.54, 3.54 3.54, 3.54 -3.54, -3.54 -3.54, -3.54 3.54)" ) ); + std::unique_ptr< QgsCircularString > cs( QgsCircle( QgsPoint( 0, 0 ), 5 ).toCircularString() ); + QCOMPARE( cs->asWkt( 2 ), QString( "CircularString (0 5, 5 0, 0 -5, -5 0, 0 5)" ) ); + cs.reset( QgsCircle( QgsPoint( 0, 0 ), 5 ).toCircularString( true ) ); + QCOMPARE( cs->asWkt( 2 ), QString( "CircularString (0 5, 5 0, 0 -5, -5 -0, 0 5)" ) ); + cs.reset( QgsCircle( QgsPoint( 0, 0 ), 5, 315 ).toCircularString() ); + QCOMPARE( cs->asWkt( 2 ), QString( "CircularString (0 5, 5 0, 0 -5, -5 0, 0 5)" ) ); + cs.reset( QgsCircle( QgsPoint( 0, 0 ), 5, 315 ).toCircularString( true ) ); + QCOMPARE( cs->asWkt( 2 ), QString( "CircularString (-3.54 3.54, 3.54 3.54, 3.54 -3.54, -3.54 -3.54, -3.54 3.54)" ) ); // bounding box QVERIFY( QgsRectangle( QgsPointXY( -2.5, -2.5 ), QgsPointXY( 2.5, 2.5 ) ) == QgsCircle( QgsPoint( 0, 0 ), 2.5, 0 ).boundingBox() ); @@ -4336,7 +6473,7 @@ void TestQgsGeometry::regularPolygon() QgsRegularPolygon rp1 = QgsRegularPolygon(); QCOMPARE( rp1.center(), QgsPoint() ); QCOMPARE( rp1.firstVertex(), QgsPoint() ); - QCOMPARE( rp1.numberSides(), 0 ); + QCOMPARE( rp1.numberSides(), static_cast< unsigned int >( 0 ) ); QCOMPARE( rp1.radius(), 0.0 ); QVERIFY( rp1.isEmpty() ); @@ -4350,7 +6487,7 @@ void TestQgsGeometry::regularPolygon() QVERIFY( !rp2.isEmpty() ); QCOMPARE( rp2.center(), QgsPoint() ); QCOMPARE( rp2.firstVertex(), QgsPoint( 0, 5 ) ); - QCOMPARE( rp2.numberSides(), 5 ); + QCOMPARE( rp2.numberSides(), static_cast< unsigned int>( 5 ) ); QCOMPARE( rp2.radius(), 5.0 ); QGSCOMPARENEAR( rp2.apothem(), 4.0451, 10E-4 ); QVERIFY( rp2 == QgsRegularPolygon( QgsPoint(), -5, 0, 5, QgsRegularPolygon::InscribedCircle ) ); @@ -4390,16 +6527,16 @@ void TestQgsGeometry::regularPolygon() rp7.setNumberSides( 2 ); QVERIFY( rp7.isEmpty() ); - QCOMPARE( rp7.numberSides(), 0 ); + QCOMPARE( rp7.numberSides(), static_cast< unsigned int >( 0 ) ); rp7.setNumberSides( 5 ); QVERIFY( rp7.isEmpty() ); - QCOMPARE( rp7.numberSides(), 5 ); + QCOMPARE( rp7.numberSides(), static_cast< unsigned int >( 5 ) ); rp7.setNumberSides( 2 ); QVERIFY( rp7.isEmpty() ); - QCOMPARE( rp7.numberSides(), 5 ); + QCOMPARE( rp7.numberSides(), static_cast< unsigned int >( 5 ) ); rp7.setNumberSides( 3 ); QVERIFY( rp7.isEmpty() ); - QCOMPARE( rp7.numberSides(), 3 ); + QCOMPARE( rp7.numberSides(), static_cast< unsigned int >( 3 ) ); rp7.setRadius( -6 ); QVERIFY( !rp7.isEmpty() ); @@ -4443,6 +6580,18 @@ void TestQgsGeometry::regularPolygon() QCOMPARE( rp8.interiorAngle(), 60.0 ); QCOMPARE( rp8.centralAngle(), 120.0 ); + //points + rp8 = QgsRegularPolygon(); // empty + QgsPointSequence points = rp8.points(); + QVERIFY( points.isEmpty() ); + rp8 = QgsRegularPolygon( QgsPoint(), QgsPoint( 0, 5 ), 3, QgsRegularPolygon::InscribedCircle ); + points = rp8.points(); + QCOMPARE( points.count(), 3 ); + QCOMPARE( points.at( 0 ), QgsPoint( 0, 5 ) ); + QGSCOMPARENEAR( points.at( 1 ).x(), 4.33, 0.01 ); + QGSCOMPARENEAR( points.at( 1 ).y(), -2.4999, 0.01 ); + QGSCOMPARENEAR( points.at( 2 ).x(), -4.33, 0.01 ); + QGSCOMPARENEAR( points.at( 2 ).y(), -2.4999, 0.01 ); //test conversions // circle @@ -4458,13 +6607,18 @@ void TestQgsGeometry::regularPolygon() QgsRegularPolygon rp10 = QgsRegularPolygon( QgsPoint( 0, 0 ), QgsPoint( 0, 4 ), 4 ); QList rp10_tri = rp10.triangulate(); - QCOMPARE( rp10_tri.length(), ( int )rp10.numberSides() ); + QCOMPARE( rp10_tri.length(), static_cast< int >( rp10.numberSides() ) ); QVERIFY( rp10_tri.at( 0 ) == QgsTriangle( QgsPoint( 0, 0 ), QgsPoint( 0, 4 ), rp10.center() ) ); QVERIFY( rp10_tri.at( 1 ) == QgsTriangle( QgsPoint( 0, 4 ), QgsPoint( 4, 4 ), rp10.center() ) ); QVERIFY( rp10_tri.at( 2 ) == QgsTriangle( QgsPoint( 4, 4 ), QgsPoint( 4, 0 ), rp10.center() ) ); QVERIFY( rp10_tri.at( 3 ) == QgsTriangle( QgsPoint( 4, 0 ), QgsPoint( 0, 0 ), rp10.center() ) ); + QVERIFY( QgsRegularPolygon().triangulate().isEmpty() ); + // polygon + std::unique_ptr< QgsPolygonV2 > toP( QgsRegularPolygon().toPolygon() ); + QVERIFY( toP->isEmpty() ); + QgsPointSequence ptsPol; std::unique_ptr< QgsPolygonV2 > pol( new QgsPolygonV2() ); pol.reset( rp10.toPolygon() ); @@ -4480,7 +6634,8 @@ void TestQgsGeometry::regularPolygon() QVERIFY( ptsPol.at( 4 ) == QgsPoint( 0, 0 ) ); ptsPol.pop_back(); - std::unique_ptr< QgsLineString > l( new QgsLineString() ); + std::unique_ptr< QgsLineString > l( QgsRegularPolygon( QgsPoint(), QgsPoint( 0, 5 ), 1, QgsRegularPolygon::InscribedCircle ).toLineString() ); + QVERIFY( l->isEmpty() ); l.reset( rp10.toLineString( ) ); QCOMPARE( l->numPoints(), 5 ); QCOMPARE( l->pointN( 0 ), l->pointN( 4 ) ); @@ -4495,19 +6650,3200 @@ void TestQgsGeometry::regularPolygon() } + +void TestQgsGeometry::curvePolygon() +{ + //test constructor + QgsCurvePolygon p1; + QVERIFY( p1.isEmpty() ); + QCOMPARE( p1.numInteriorRings(), 0 ); + QCOMPARE( p1.nCoordinates(), 0 ); + QCOMPARE( p1.ringCount(), 0 ); + QCOMPARE( p1.partCount(), 0 ); + QVERIFY( !p1.is3D() ); + QVERIFY( !p1.isMeasure() ); + QCOMPARE( p1.wkbType(), QgsWkbTypes::CurvePolygon ); + QCOMPARE( p1.wktTypeStr(), QString( "CurvePolygon" ) ); + QCOMPARE( p1.geometryType(), QString( "CurvePolygon" ) ); + QCOMPARE( p1.dimension(), 2 ); + QVERIFY( !p1.hasCurvedSegments() ); + QCOMPARE( p1.area(), 0.0 ); + QCOMPARE( p1.perimeter(), 0.0 ); + QVERIFY( !p1.exteriorRing() ); + QVERIFY( !p1.interiorRing( 0 ) ); + + //set exterior ring + + //try with no ring + QgsCircularString *ext = nullptr; + p1.setExteriorRing( ext ); + QVERIFY( p1.isEmpty() ); + QCOMPARE( p1.numInteriorRings(), 0 ); + QCOMPARE( p1.nCoordinates(), 0 ); + QCOMPARE( p1.ringCount(), 0 ); + QCOMPARE( p1.partCount(), 0 ); + QVERIFY( !p1.exteriorRing() ); + QVERIFY( !p1.interiorRing( 0 ) ); + QCOMPARE( p1.wkbType(), QgsWkbTypes::CurvePolygon ); + + // empty exterior ring + ext = new QgsCircularString(); + p1.setExteriorRing( ext ); + QVERIFY( p1.isEmpty() ); + QCOMPARE( p1.numInteriorRings(), 0 ); + QCOMPARE( p1.nCoordinates(), 0 ); + QCOMPARE( p1.ringCount(), 1 ); + QCOMPARE( p1.partCount(), 1 ); + QVERIFY( p1.exteriorRing() ); + QVERIFY( !p1.interiorRing( 0 ) ); + QCOMPARE( p1.wkbType(), QgsWkbTypes::CurvePolygon ); + + //valid exterior ring + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) + << QgsPoint( 10, 0 ) << QgsPoint( 0, 0 ) ); + p1.setExteriorRing( ext ); + QVERIFY( !p1.isEmpty() ); + QCOMPARE( p1.numInteriorRings(), 0 ); + QCOMPARE( p1.nCoordinates(), 5 ); + QCOMPARE( p1.ringCount(), 1 ); + QCOMPARE( p1.partCount(), 1 ); + QVERIFY( !p1.is3D() ); + QVERIFY( !p1.isMeasure() ); + QCOMPARE( p1.wkbType(), QgsWkbTypes::CurvePolygon ); + QCOMPARE( p1.wktTypeStr(), QString( "CurvePolygon" ) ); + QCOMPARE( p1.geometryType(), QString( "CurvePolygon" ) ); + QCOMPARE( p1.dimension(), 2 ); + QVERIFY( p1.hasCurvedSegments() ); + QGSCOMPARENEAR( p1.area(), 157.08, 0.01 ); + QGSCOMPARENEAR( p1.perimeter(), 44.4288, 0.01 ); + QVERIFY( p1.exteriorRing() ); + QVERIFY( !p1.interiorRing( 0 ) ); + + //retrieve exterior ring and check + QCOMPARE( *( static_cast< const QgsCircularString * >( p1.exteriorRing() ) ), *ext ); + + //initial setting of exterior ring should set z/m type + QgsCurvePolygon p2; + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) + << QgsPoint( QgsWkbTypes::PointZ, 0, 10, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 10, 10, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 10, 0, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) ); + p2.setExteriorRing( ext ); + QVERIFY( p2.is3D() ); + QVERIFY( !p2.isMeasure() ); + QCOMPARE( p2.wkbType(), QgsWkbTypes::CurvePolygonZ ); + QCOMPARE( p2.wktTypeStr(), QString( "CurvePolygonZ" ) ); + QCOMPARE( p2.geometryType(), QString( "CurvePolygon" ) ); + QCOMPARE( *( static_cast< const QgsCircularString * >( p2.exteriorRing() ) ), *ext ); + QgsCurvePolygon p3; + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 0, 0, 0, 1 ) + << QgsPoint( QgsWkbTypes::PointM, 0, 10, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 10, 10, 0, 3 ) + << QgsPoint( QgsWkbTypes::PointM, 10, 0, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 0, 0, 0, 1 ) ); + p3.setExteriorRing( ext ); + QVERIFY( !p3.is3D() ); + QVERIFY( p3.isMeasure() ); + QCOMPARE( p3.wkbType(), QgsWkbTypes::CurvePolygonM ); + QCOMPARE( p3.wktTypeStr(), QString( "CurvePolygonM" ) ); + QCOMPARE( *( static_cast< const QgsCircularString * >( p3.exteriorRing() ) ), *ext ); + QgsCurvePolygon p4; + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 2, 1 ) + << QgsPoint( QgsWkbTypes::PointZM, 0, 10, 3, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 10, 10, 5, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 10, 0, 0, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 2, 1 ) ); + p4.setExteriorRing( ext ); + QVERIFY( p4.is3D() ); + QVERIFY( p4.isMeasure() ); + QCOMPARE( p4.wkbType(), QgsWkbTypes::CurvePolygonZM ); + QCOMPARE( p4.wktTypeStr(), QString( "CurvePolygonZM" ) ); + QCOMPARE( *( static_cast< const QgsCircularString * >( p4.exteriorRing() ) ), *ext ); + + //addInteriorRing + QgsCurvePolygon p6; + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) + << QgsPoint( 10, 0 ) << QgsPoint( 0, 0 ) ); + p6.setExteriorRing( ext ); + //empty ring + QCOMPARE( p6.numInteriorRings(), 0 ); + QVERIFY( !p6.interiorRing( -1 ) ); + QVERIFY( !p6.interiorRing( 0 ) ); + p6.addInteriorRing( 0 ); + QCOMPARE( p6.numInteriorRings(), 0 ); + QgsCircularString *ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( 1, 1 ) << QgsPoint( 1, 9 ) << QgsPoint( 9, 9 ) + << QgsPoint( 9, 1 ) << QgsPoint( 1, 1 ) ); + p6.addInteriorRing( ring ); + QCOMPARE( p6.numInteriorRings(), 1 ); + QCOMPARE( p6.interiorRing( 0 ), ring ); + QVERIFY( !p6.interiorRing( 1 ) ); + + QgsCoordinateSequence seq = p6.coordinateSequence(); + QCOMPARE( seq, QgsCoordinateSequence() << ( QgsRingSequence() << ( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) + << QgsPoint( 10, 0 ) << QgsPoint( 0, 0 ) ) + << ( QgsPointSequence() << QgsPoint( 1, 1 ) << QgsPoint( 1, 9 ) << QgsPoint( 9, 9 ) + << QgsPoint( 9, 1 ) << QgsPoint( 1, 1 ) ) ) ); + QCOMPARE( p6.nCoordinates(), 10 ); + + //try adding an interior ring with z to a 2d polygon, z should be dropped + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 0.1, 0.1, 1 ) + << QgsPoint( QgsWkbTypes::PointZ, 0.1, 0.2, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 0.2, 0.2, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 0.2, 0.1, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 0.1, 0.1, 1 ) ); + p6.addInteriorRing( ring ); + QCOMPARE( p6.numInteriorRings(), 2 ); + QVERIFY( !p6.is3D() ); + QVERIFY( !p6.isMeasure() ); + QCOMPARE( p6.wkbType(), QgsWkbTypes::CurvePolygon ); + QVERIFY( p6.interiorRing( 1 ) ); + QVERIFY( !p6.interiorRing( 1 )->is3D() ); + QVERIFY( !p6.interiorRing( 1 )->isMeasure() ); + QCOMPARE( p6.interiorRing( 1 )->wkbType(), QgsWkbTypes::CircularString ); + + //try adding an interior ring with m to a 2d polygon, m should be dropped + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 0.1, 0.1, 0, 1 ) + << QgsPoint( QgsWkbTypes::PointM, 0.1, 0.2, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 0.2, 0.2, 0, 3 ) + << QgsPoint( QgsWkbTypes::PointM, 0.2, 0.1, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 0.1, 0.1, 0, 1 ) ); + p6.addInteriorRing( ring ); + QCOMPARE( p6.numInteriorRings(), 3 ); + QVERIFY( !p6.is3D() ); + QVERIFY( !p6.isMeasure() ); + QCOMPARE( p6.wkbType(), QgsWkbTypes::CurvePolygon ); + QVERIFY( p6.interiorRing( 2 ) ); + QVERIFY( !p6.interiorRing( 2 )->is3D() ); + QVERIFY( !p6.interiorRing( 2 )->isMeasure() ); + QCOMPARE( p6.interiorRing( 2 )->wkbType(), QgsWkbTypes::CircularString ); + + + //addInteriorRing without z/m to PolygonZM + QgsCurvePolygon p6b; + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1 ) + << QgsPoint( QgsWkbTypes::PointZM, 0, 10, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 10, 10, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1 ) ); + p6b.setExteriorRing( ext ); + QVERIFY( p6b.is3D() ); + QVERIFY( p6b.isMeasure() ); + QCOMPARE( p6b.wkbType(), QgsWkbTypes::CurvePolygonZM ); + //ring has no z + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 1, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 1, 9 ) << QgsPoint( QgsWkbTypes::PointM, 9, 9 ) + << QgsPoint( QgsWkbTypes::PointM, 9, 1 ) << QgsPoint( QgsWkbTypes::PointM, 1, 1 ) ); + p6b.addInteriorRing( ring ); + QVERIFY( p6b.interiorRing( 0 ) ); + QVERIFY( p6b.interiorRing( 0 )->is3D() ); + QVERIFY( p6b.interiorRing( 0 )->isMeasure() ); + QCOMPARE( p6b.interiorRing( 0 )->wkbType(), QgsWkbTypes::CircularStringZM ); + QCOMPARE( p6b.interiorRing( 0 )->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( QgsWkbTypes::PointZM, 1, 1, 0, 2 ) ); + //ring has no m + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 0.1, 0.1, 1 ) + << QgsPoint( QgsWkbTypes::PointZ, 0.1, 0.2, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 0.2, 0.2, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 0.2, 0.1, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 0.1, 0.1, 1 ) ); + p6b.addInteriorRing( ring ); + QVERIFY( p6b.interiorRing( 1 ) ); + QVERIFY( p6b.interiorRing( 1 )->is3D() ); + QVERIFY( p6b.interiorRing( 1 )->isMeasure() ); + QCOMPARE( p6b.interiorRing( 1 )->wkbType(), QgsWkbTypes::CircularStringZM ); + QCOMPARE( p6b.interiorRing( 1 )->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( QgsWkbTypes::PointZM, 0.1, 0.1, 1, 0 ) ); + + //set interior rings + QgsCurvePolygon p7; + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) + << QgsPoint( 10, 0 ) << QgsPoint( 0, 0 ) ); + p7.setExteriorRing( ext ); + //add a list of rings with mixed types + QList< QgsCurve * > rings; + rings << new QgsCircularString(); + static_cast< QgsCircularString *>( rings[0] )->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 0.1, 0.1, 1 ) + << QgsPoint( QgsWkbTypes::PointZ, 0.1, 0.2, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 0.2, 0.2, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 0.2, 0.1, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 0.1, 0.1, 1 ) ); + rings << new QgsCircularString(); + static_cast< QgsCircularString *>( rings[1] )->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 0.3, 0.3, 0, 1 ) + << QgsPoint( QgsWkbTypes::PointM, 0.3, 0.4, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 0.4, 0.4, 0, 3 ) + << QgsPoint( QgsWkbTypes::PointM, 0.4, 0.3, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 0.3, 0.3, 0, 1 ) ); + //throw an empty ring in too + rings << 0; + rings << new QgsCircularString(); + static_cast< QgsCircularString *>( rings[3] )->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) + << QgsPoint( 10, 0 ) << QgsPoint( 0, 0 ) ); + p7.setInteriorRings( rings ); + QCOMPARE( p7.numInteriorRings(), 3 ); + QVERIFY( p7.interiorRing( 0 ) ); + QVERIFY( !p7.interiorRing( 0 )->is3D() ); + QVERIFY( !p7.interiorRing( 0 )->isMeasure() ); + QCOMPARE( p7.interiorRing( 0 )->wkbType(), QgsWkbTypes::CircularString ); + QCOMPARE( p7.interiorRing( 0 )->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( QgsWkbTypes::Point, 0.1, 0.1 ) ); + QVERIFY( p7.interiorRing( 1 ) ); + QVERIFY( !p7.interiorRing( 1 )->is3D() ); + QVERIFY( !p7.interiorRing( 1 )->isMeasure() ); + QCOMPARE( p7.interiorRing( 1 )->wkbType(), QgsWkbTypes::CircularString ); + QCOMPARE( p7.interiorRing( 1 )->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( QgsWkbTypes::Point, 0.3, 0.3 ) ); + QVERIFY( p7.interiorRing( 2 ) ); + QVERIFY( !p7.interiorRing( 2 )->is3D() ); + QVERIFY( !p7.interiorRing( 2 )->isMeasure() ); + QCOMPARE( p7.interiorRing( 2 )->wkbType(), QgsWkbTypes::CircularString ); + + //set rings with existing + rings.clear(); + rings << new QgsCircularString(); + static_cast< QgsCircularString *>( rings[0] )->setPoints( QgsPointSequence() << QgsPoint( 0.8, 0.8 ) + << QgsPoint( 0.8, 0.9 ) << QgsPoint( 0.9, 0.9 ) + << QgsPoint( 0.9, 0.8 ) << QgsPoint( 0.8, 0.8 ) ); + p7.setInteriorRings( rings ); + QCOMPARE( p7.numInteriorRings(), 1 ); + QVERIFY( p7.interiorRing( 0 ) ); + QVERIFY( !p7.interiorRing( 0 )->is3D() ); + QVERIFY( !p7.interiorRing( 0 )->isMeasure() ); + QCOMPARE( p7.interiorRing( 0 )->wkbType(), QgsWkbTypes::CircularString ); + QCOMPARE( p7.interiorRing( 0 )->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( QgsWkbTypes::Point, 0.8, 0.8 ) ); + rings.clear(); + p7.setInteriorRings( rings ); + QCOMPARE( p7.numInteriorRings(), 0 ); + + //change dimensionality of interior rings using setExteriorRing + QgsCurvePolygon p7a; + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) << QgsPoint( QgsWkbTypes::PointZ, 0, 10, 2 ) + << QgsPoint( QgsWkbTypes::PointZ, 10, 10, 1 ) << QgsPoint( QgsWkbTypes::PointZ, 10, 0, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) ); + p7a.setExteriorRing( ext ); + rings.clear(); + rings << new QgsCircularString(); + static_cast< QgsCircularString *>( rings[0] )->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 0.1, 0.1, 1 ) + << QgsPoint( QgsWkbTypes::PointZ, 0.1, 0.2, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 0.2, 0.2, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 0.2, 0.1, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 0.1, 0.1, 1 ) ); + rings << new QgsCircularString(); + static_cast< QgsCircularString *>( rings[1] )->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 0.3, 0.3, 1 ) + << QgsPoint( QgsWkbTypes::PointZ, 0.3, 0.4, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 0.4, 0.4, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 0.4, 0.3, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 0.3, 0.3, 1 ) ); + p7a.setInteriorRings( rings ); + QVERIFY( p7a.is3D() ); + QVERIFY( !p7a.isMeasure() ); + QVERIFY( p7a.interiorRing( 0 )->is3D() ); + QVERIFY( !p7a.interiorRing( 0 )->isMeasure() ); + QVERIFY( p7a.interiorRing( 1 )->is3D() ); + QVERIFY( !p7a.interiorRing( 1 )->isMeasure() ); + //reset exterior ring to 2d + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) + << QgsPoint( 10, 10 ) << QgsPoint( 10, 0 ) << QgsPoint( 0, 0 ) ); + p7a.setExteriorRing( ext ); + QVERIFY( !p7a.is3D() ); + QVERIFY( !p7a.interiorRing( 0 )->is3D() ); //rings should also be made 2D + QVERIFY( !p7a.interiorRing( 1 )->is3D() ); + //reset exterior ring to LineStringM + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 0, 0 ) << QgsPoint( QgsWkbTypes::PointM, 0, 10 ) + << QgsPoint( QgsWkbTypes::PointM, 10, 10 ) << QgsPoint( QgsWkbTypes::PointM, 10, 0 ) << QgsPoint( QgsWkbTypes::PointM, 0, 0 ) ); + p7a.setExteriorRing( ext ); + QVERIFY( p7a.isMeasure() ); + QVERIFY( p7a.interiorRing( 0 )->isMeasure() ); //rings should also gain measure + QVERIFY( p7a.interiorRing( 1 )->isMeasure() ); + + //removeInteriorRing + QgsCurvePolygon p8; + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) + << QgsPoint( 10, 0 ) << QgsPoint( 0, 0 ) ); + p8.setExteriorRing( ext ); + QVERIFY( !p8.removeInteriorRing( -1 ) ); + QVERIFY( !p8.removeInteriorRing( 0 ) ); + rings.clear(); + rings << new QgsCircularString(); + static_cast< QgsCircularString *>( rings[0] )->setPoints( QgsPointSequence() << QgsPoint( 0.1, 0.1 ) + << QgsPoint( 0.1, 0.2 ) << QgsPoint( 0.2, 0.2 ) + << QgsPoint( 0.2, 0.1 ) << QgsPoint( 0.1, 0.1 ) ); + rings << new QgsCircularString(); + static_cast< QgsCircularString *>( rings[1] )->setPoints( QgsPointSequence() << QgsPoint( 0.3, 0.3 ) + << QgsPoint( 0.3, 0.4 ) << QgsPoint( 0.4, 0.4 ) + << QgsPoint( 0.4, 0.3 ) << QgsPoint( 0.3, 0.3 ) ); + rings << new QgsCircularString(); + static_cast< QgsCircularString *>( rings[2] )->setPoints( QgsPointSequence() << QgsPoint( 0.8, 0.8 ) + << QgsPoint( 0.8, 0.9 ) << QgsPoint( 0.9, 0.9 ) + << QgsPoint( 0.9, 0.8 ) << QgsPoint( 0.8, 0.8 ) ); + p8.setInteriorRings( rings ); + QCOMPARE( p8.numInteriorRings(), 3 ); + QVERIFY( p8.removeInteriorRing( 0 ) ); + QCOMPARE( p8.numInteriorRings(), 2 ); + QCOMPARE( p8.interiorRing( 0 )->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( 0.3, 0.3 ) ); + QCOMPARE( p8.interiorRing( 1 )->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( 0.8, 0.8 ) ); + QVERIFY( p8.removeInteriorRing( 1 ) ); + QCOMPARE( p8.numInteriorRings(), 1 ); + QCOMPARE( p8.interiorRing( 0 )->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( 0.3, 0.3 ) ); + QVERIFY( p8.removeInteriorRing( 0 ) ); + QCOMPARE( p8.numInteriorRings(), 0 ); + QVERIFY( !p8.removeInteriorRing( 0 ) ); + + //clear + QgsCurvePolygon p9; + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) + << QgsPoint( QgsWkbTypes::PointZ, 0, 10, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 10, 10, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 10, 0, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) ); + p9.setExteriorRing( ext ); + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 1, 1 ) + << QgsPoint( QgsWkbTypes::PointZ, 1, 9, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 9, 9, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 9, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 1, 1, 1 ) ); + p9.addInteriorRing( ring ); + QCOMPARE( p9.numInteriorRings(), 1 ); + p9.clear(); + QVERIFY( p9.isEmpty() ); + QCOMPARE( p9.numInteriorRings(), 0 ); + QCOMPARE( p9.nCoordinates(), 0 ); + QCOMPARE( p9.ringCount(), 0 ); + QCOMPARE( p9.partCount(), 0 ); + QVERIFY( !p9.is3D() ); + QVERIFY( !p9.isMeasure() ); + QCOMPARE( p9.wkbType(), QgsWkbTypes::CurvePolygon ); + + //equality operator + QgsCurvePolygon p10; + QgsCurvePolygon p10b; + QVERIFY( p10 == p10b ); + QVERIFY( !( p10 != p10b ) ); + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) + << QgsPoint( 10, 0 ) << QgsPoint( 0, 0 ) ); + p10.setExteriorRing( ext ); + QVERIFY( !( p10 == p10b ) ); + QVERIFY( p10 != p10b ); + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) + << QgsPoint( 10, 0 ) << QgsPoint( 0, 0 ) ); + p10b.setExteriorRing( ext ); + QVERIFY( p10 == p10b ); + QVERIFY( !( p10 != p10b ) ); + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 9 ) << QgsPoint( 9, 9 ) + << QgsPoint( 9, 0 ) << QgsPoint( 0, 0 ) ); + p10b.setExteriorRing( ext ); + QVERIFY( !( p10 == p10b ) ); + QVERIFY( p10 != p10b ); + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) << QgsPoint( QgsWkbTypes::PointZ, 0, 10, 2 ) + << QgsPoint( QgsWkbTypes::PointZ, 10, 10, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 10, 0, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) ); + p10b.setExteriorRing( ext ); + QVERIFY( !( p10 == p10b ) ); + QVERIFY( p10 != p10b ); + p10b.setExteriorRing( p10.exteriorRing()->clone() ); + QVERIFY( p10 == p10b ); + QVERIFY( !( p10 != p10b ) ); + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( 1, 1 ) + << QgsPoint( 1, 9 ) << QgsPoint( 9, 9 ) + << QgsPoint( 9, 1 ) << QgsPoint( 1, 1 ) ); + p10.addInteriorRing( ring ); + QVERIFY( !( p10 == p10b ) ); + QVERIFY( p10 != p10b ); + + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( 2, 1 ) + << QgsPoint( 2, 9 ) << QgsPoint( 9, 9 ) + << QgsPoint( 9, 1 ) << QgsPoint( 2, 1 ) ); + p10b.addInteriorRing( ring ); + QVERIFY( !( p10 == p10b ) ); + QVERIFY( p10 != p10b ); + p10b.removeInteriorRing( 0 ); + p10b.addInteriorRing( p10.interiorRing( 0 )->clone() ); + QVERIFY( p10 == p10b ); + QVERIFY( !( p10 != p10b ) ); + + //clone + + QgsCurvePolygon p11; + std::unique_ptr< QgsCurvePolygon >cloned( p11.clone() ); + QCOMPARE( p11, *cloned ); + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 0, 10, 2, 6 ) << QgsPoint( QgsWkbTypes::PointZM, 10, 10, 3, 7 ) + << QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4, 8 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 9 ) ); + p11.setExteriorRing( ext ); + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 9, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZM, 9, 9, 3, 6 ) + << QgsPoint( QgsWkbTypes::PointZM, 9, 1, 4, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 7 ) ); + p11.addInteriorRing( ring ); + cloned.reset( p11.clone() ); + QCOMPARE( p11, *cloned ); + + //copy constructor + QgsCurvePolygon p12; + QgsCurvePolygon p13( p12 ); + QCOMPARE( p12, p13 ); + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 0, 10, 2, 6 ) << QgsPoint( QgsWkbTypes::PointZM, 10, 10, 3, 7 ) + << QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4, 8 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 9 ) ); + p12.setExteriorRing( ext ); + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 9, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZM, 9, 9, 3, 6 ) + << QgsPoint( QgsWkbTypes::PointZM, 9, 1, 4, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 7 ) ); + p12.addInteriorRing( ring ); + QgsCurvePolygon p14( p12 ); + QCOMPARE( p12, p14 ); + + //assignment operator + QgsCurvePolygon p15; + p15 = p13; + QCOMPARE( p13, p15 ); + p15 = p12; + QCOMPARE( p12, p15 ); + + // bounding box + QgsCurvePolygon boundingBoxPoly; + QgsRectangle bBox = boundingBoxPoly.boundingBox(); //no crash! + + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0, 1 ) << QgsPoint( 1, 10, 2 ) << QgsPoint( 0, 18, 3 ) + << QgsPoint( -1, 4, 4 ) << QgsPoint( 0, 0, 1 ) ); + boundingBoxPoly.setExteriorRing( ext ); + bBox = boundingBoxPoly.boundingBox(); + QGSCOMPARENEAR( bBox.xMinimum(), -1.435273, 0.001 ); + QGSCOMPARENEAR( bBox.xMaximum(), 1.012344, 0.001 ); + QGSCOMPARENEAR( bBox.yMinimum(), 0.000000, 0.001 ); + QGSCOMPARENEAR( bBox.yMaximum(), 18, 0.001 ); + + //surfaceToPolygon + QgsCurvePolygon p12a; + std::unique_ptr< QgsPolygonV2 > surface( p12a.surfaceToPolygon() ); + QVERIFY( surface->isEmpty() ); + + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 1, 3 ) << QgsPoint( 2, 4 ) + << QgsPoint( -1, 5 ) << QgsPoint( 0, 6 ) ); + p12a.setExteriorRing( ext ); + surface.reset( p12a.surfaceToPolygon() ); + QCOMPARE( surface->wkbType(), QgsWkbTypes::Polygon ); + QCOMPARE( surface->exteriorRing()->nCoordinates(), 290 ); + QCOMPARE( surface->exteriorRing()->nCoordinates(), 290 ); // nCoordinates is cached, so check twice + QVERIFY( surface->exteriorRing()->isClosed() ); + // too many vertices to actually check the result, let's just make sure the bounding boxes are similar + QgsRectangle r1 = ext->boundingBox(); + QgsRectangle r2 = surface->exteriorRing()->boundingBox(); + QGSCOMPARENEAR( r1.xMinimum(), r2.xMinimum(), 0.0001 ); + QGSCOMPARENEAR( r1.xMaximum(), r2.xMaximum(), 0.0001 ); + QGSCOMPARENEAR( r1.yMinimum(), r2.yMinimum(), 0.0001 ); + QGSCOMPARENEAR( r1.yMaximum(), r2.yMaximum(), 0.0001 ); + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 9, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZM, 9, 9, 3, 6 ) + << QgsPoint( QgsWkbTypes::PointZM, 9, 1, 4, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 7 ) ); + p12a.addInteriorRing( ring ); + surface.reset( p12a.surfaceToPolygon() ); + QCOMPARE( surface->wkbType(), QgsWkbTypes::Polygon ); + QCOMPARE( surface->exteriorRing()->nCoordinates(), 290 ); + QCOMPARE( surface->exteriorRing()->nCoordinates(), 290 ); // nCoordinates is cached, so check twice + QVERIFY( surface->exteriorRing()->isClosed() ); + QCOMPARE( surface->numInteriorRings(), 1 ); + // too many vertices to actually check the result, let's just make sure the bounding boxes are similar + r1 = ring->boundingBox(); + r2 = surface->interiorRing( 0 )->boundingBox(); + QGSCOMPARENEAR( r1.xMinimum(), r2.xMinimum(), 0.0001 ); + QGSCOMPARENEAR( r1.xMaximum(), r2.xMaximum(), 0.0001 ); + QGSCOMPARENEAR( r1.yMinimum(), r2.yMinimum(), 0.0001 ); + QGSCOMPARENEAR( r1.yMaximum(), r2.yMaximum(), 0.0001 ); + + //toPolygon + p12a = QgsCurvePolygon(); + surface.reset( p12a.toPolygon() ); + QVERIFY( surface->isEmpty() ); + + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 1, 10 ) << QgsPoint( 0, 18 ) + << QgsPoint( -1, 4 ) << QgsPoint( 0, 0 ) ); + p12a.setExteriorRing( ext ); + surface.reset( p12a.toPolygon() ); + QCOMPARE( surface->wkbType(), QgsWkbTypes::Polygon ); + QCOMPARE( surface->exteriorRing()->nCoordinates(), 64 ); + QCOMPARE( surface->exteriorRing()->nCoordinates(), 64 ); // ncoordinates is cached, so check twice + QVERIFY( surface->exteriorRing()->isClosed() ); + // too many vertices to actually check the result, let's just make sure the bounding boxes are similar + r1 = ext->boundingBox(); + r2 = surface->exteriorRing()->boundingBox(); + QGSCOMPARENEAR( r1.xMinimum(), r2.xMinimum(), 0.01 ); + QGSCOMPARENEAR( r1.xMaximum(), r2.xMaximum(), 0.01 ); + QGSCOMPARENEAR( r1.yMinimum(), r2.yMinimum(), 0.01 ); + QGSCOMPARENEAR( r1.yMaximum(), r2.yMaximum(), 0.01 ); + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 9, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZM, 9, 9, 3, 6 ) + << QgsPoint( QgsWkbTypes::PointZM, 9, 1, 4, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 7 ) ); + p12a.addInteriorRing( ring ); + surface.reset( p12a.toPolygon() ); + QCOMPARE( surface->wkbType(), QgsWkbTypes::Polygon ); + QCOMPARE( surface->exteriorRing()->nCoordinates(), 64 ); + QCOMPARE( surface->exteriorRing()->nCoordinates(), 64 ); //ncoordinates is cached, so check twice + QVERIFY( surface->exteriorRing()->isClosed() ); + QCOMPARE( surface->numInteriorRings(), 1 ); + // too many vertices to actually check the result, let's just make sure the bounding boxes are similar + r1 = ring->boundingBox(); + r2 = surface->interiorRing( 0 )->boundingBox(); + QGSCOMPARENEAR( r1.xMinimum(), r2.xMinimum(), 0.0001 ); + QGSCOMPARENEAR( r1.xMaximum(), r2.xMaximum(), 0.0001 ); + QGSCOMPARENEAR( r1.yMinimum(), r2.yMinimum(), 0.0001 ); + QGSCOMPARENEAR( r1.yMaximum(), r2.yMaximum(), 0.0001 ); + + //toCurveType - should be identical since it's already a curve + std::unique_ptr< QgsCurvePolygon > curveType( p12a.toCurveType() ); + QCOMPARE( *curveType, p12a ); + + //to/fromWKB + QgsCurvePolygon p16; + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) << QgsPoint( 2, 0 ) + << QgsPoint( 1, 0.5 ) << QgsPoint( 0, 0 ) ); + p16.setExteriorRing( ext ); + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0.1, 0 ) << QgsPoint( 0.2, 0 ) + << QgsPoint( 0.1, 0.05 ) << QgsPoint( 0, 0 ) ); + p16.addInteriorRing( ring ); + QByteArray wkb16 = p16.asWkb(); + QgsCurvePolygon p17; + QgsConstWkbPtr wkb16ptr( wkb16 ); + p17.fromWkb( wkb16ptr ); + QCOMPARE( p16, p17 ); + //CurvePolygonZ + p16.clear(); + p17.clear(); + + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0, 1 ) << QgsPoint( 1, 0, 2 ) << QgsPoint( 2, 0, 3 ) + << QgsPoint( 1, 0.5, 4 ) << QgsPoint( 0, 0, 1 ) ); + p16.setExteriorRing( ext ); + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( 0, 0, 1 ) << QgsPoint( 0.1, 0, 2 ) << QgsPoint( 0.2, 0, 3 ) + << QgsPoint( 0.1, 0.05, 4 ) << QgsPoint( 0, 0, 1 ) ); + p16.addInteriorRing( ring ); + wkb16 = p16.asWkb(); + QgsConstWkbPtr wkb16ptr2( wkb16 ); + p17.fromWkb( wkb16ptr2 ); + QCOMPARE( p16, p17 ); + + // compound curve + QgsCompoundCurve *cCurve = new QgsCompoundCurve(); + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0, 1 ) << QgsPoint( 1, 0, 2 ) << QgsPoint( 2, 0, 3 ) + << QgsPoint( 1, 0.5, 4 ) << QgsPoint( 0, 0, 1 ) ); + cCurve->addCurve( ext ); + p16.addInteriorRing( cCurve ); + wkb16 = p16.asWkb(); + QgsConstWkbPtr wkb16ptr3( wkb16 ); + p17.fromWkb( wkb16ptr3 ); + QCOMPARE( p16, p17 ); + + //CurvePolygonM + p16.clear(); + p17.clear(); + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 0, 0, 0, 1 ) << QgsPoint( QgsWkbTypes::PointM, 1, 0, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 2, 0, 0, 3 ) + << QgsPoint( QgsWkbTypes::PointM, 1, 0.5, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 0, 0, 0, 1 ) ); + p16.setExteriorRing( ext ); + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 0, 0, 0, 1 ) << QgsPoint( QgsWkbTypes::PointM, 0.1, 0, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 0.2, 0, 0, 3 ) + << QgsPoint( QgsWkbTypes::PointM, 0.1, 0.05, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 0, 0, 0, 1 ) ); + p16.addInteriorRing( ring ); + wkb16 = p16.asWkb(); + QgsConstWkbPtr wkb16ptr8( wkb16 ); + p17.fromWkb( wkb16ptr8 ); + QCOMPARE( p16, p17 ); + + //CurvePolygonZM + p16.clear(); + p17.clear(); + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 1, 0, 11, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 2, 0, 12, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 0.5, 13, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) ); + p16.setExteriorRing( ext ); + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 0.1, 0, 11, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 0.2, 0, 12, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 0.1, 0.05, 13, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) ); + p16.addInteriorRing( ring ); + wkb16 = p16.asWkb(); + QgsConstWkbPtr wkb16ptr4( wkb16 ); + p17.fromWkb( wkb16ptr4 ); + QCOMPARE( p16, p17 ); + + //bad WKB - check for no crash + p17.clear(); + QgsConstWkbPtr nullPtr( nullptr, 0 ); + QVERIFY( !p17.fromWkb( nullPtr ) ); + QCOMPARE( p17.wkbType(), QgsWkbTypes::CurvePolygon ); + QgsPoint point( 1, 2 ); + QByteArray wkbPoint = point.asWkb(); + QgsConstWkbPtr wkbPointPtr( wkbPoint ); + QVERIFY( !p17.fromWkb( wkbPointPtr ) ); + QCOMPARE( p17.wkbType(), QgsWkbTypes::CurvePolygon ); + + //to/from WKT + QgsCurvePolygon p18; + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 1, 0, 11, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 2, 0, 12, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 0.5, 13, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) ); + p18.setExteriorRing( ext ); + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 0.1, 0, 11, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 0.2, 0, 12, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 0.1, 0.05, 13, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) ); + p18.addInteriorRing( ring ); + + QString wkt = p18.asWkt(); + QVERIFY( !wkt.isEmpty() ); + QgsCurvePolygon p19; + QVERIFY( p19.fromWkt( wkt ) ); + QCOMPARE( p18, p19 ); + + //bad WKT + QVERIFY( !p19.fromWkt( "Point()" ) ); + QVERIFY( p19.isEmpty() ); + QVERIFY( !p19.exteriorRing() ); + QCOMPARE( p19.numInteriorRings(), 0 ); + QVERIFY( !p19.is3D() ); + QVERIFY( !p19.isMeasure() ); + QCOMPARE( p19.wkbType(), QgsWkbTypes::CurvePolygon ); + + + //as JSON + QgsCurvePolygon exportPolygon; + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 1, 0, 11, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 2, 0, 12, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 0.5, 13, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) ); + exportPolygon.setExteriorRing( ext ); + + + // GML document for compare + QDomDocument doc( QStringLiteral( "gml" ) ); + + // as GML2 + QString expectedSimpleGML2( QStringLiteral( "0,0 1,0 2,0 2,0 2,0 2,0.1 1.9,0.1 1.9,0.1 1.9,0.1 1.9,0.1 1.9,0.1 1.9,0.1 1.9,0.2 1.8,0.2 1.8,0.2 1.8,0.2 1.8,0.2 1.8,0.2 1.8,0.2 1.7,0.3 1.7,0.3 1.7,0.3 1.7,0.3 1.7,0.3 1.6,0.3 1.6,0.3 1.6,0.3 1.6,0.4 1.6,0.4 1.6,0.4 1.5,0.4 1.5,0.4 1.5,0.4 1.5,0.4 1.5,0.4 1.4,0.4 1.4,0.4 1.4,0.4 1.4,0.4 1.4,0.4 1.3,0.5 1.3,0.5 1.3,0.5 1.3,0.5 1.2,0.5 1.2,0.5 1.2,0.5 1.2,0.5 1.2,0.5 1.1,0.5 1.1,0.5 1.1,0.5 1.1,0.5 1.1,0.5 1,0.5 1,0.5 1,0.5 1,0.5 0.9,0.5 0.9,0.5 0.9,0.5 0.9,0.5 0.9,0.5 0.8,0.5 0.8,0.5 0.8,0.5 0.8,0.5 0.8,0.5 0.7,0.5 0.7,0.5 0.7,0.5 0.7,0.5 0.6,0.4 0.6,0.4 0.6,0.4 0.6,0.4 0.6,0.4 0.5,0.4 0.5,0.4 0.5,0.4 0.5,0.4 0.5,0.4 0.4,0.4 0.4,0.4 0.4,0.4 0.4,0.3 0.4,0.3 0.4,0.3 0.3,0.3 0.3,0.3 0.3,0.3 0.3,0.3 0.3,0.3 0.2,0.2 0.2,0.2 0.2,0.2 0.2,0.2 0.2,0.2 0.2,0.2 0.1,0.2 0.1,0.1 0.1,0.1 0.1,0.1 0.1,0.1 0.1,0.1 0.1,0.1 0,0.1 0,0 0,0 0,0" ) ); + QString res = elemToString( exportPolygon.asGML2( doc, 1 ) ); + QGSCOMPAREGML( res, expectedSimpleGML2 ); + + //as GML3 + QString expectedSimpleGML3( QStringLiteral( "0 0 10 1 0 11 2 0 12 1 0.5 13 0 0 10" ) ); + res = elemToString( exportPolygon.asGML3( doc, 2 ) ); + QCOMPARE( elemToString( exportPolygon.asGML3( doc ) ), expectedSimpleGML3 ); + + // as JSON + QString expectedSimpleJson( QStringLiteral( "{\"type\": \"Polygon\", \"coordinates\": [[ [0, 0], [1, 0], [2, 0], [2, 0], [2, 0], [2, 0.1], [1.9, 0.1], [1.9, 0.1], [1.9, 0.1], [1.9, 0.1], [1.9, 0.1], [1.9, 0.1], [1.9, 0.2], [1.8, 0.2], [1.8, 0.2], [1.8, 0.2], [1.8, 0.2], [1.8, 0.2], [1.8, 0.2], [1.7, 0.3], [1.7, 0.3], [1.7, 0.3], [1.7, 0.3], [1.7, 0.3], [1.6, 0.3], [1.6, 0.3], [1.6, 0.3], [1.6, 0.4], [1.6, 0.4], [1.6, 0.4], [1.5, 0.4], [1.5, 0.4], [1.5, 0.4], [1.5, 0.4], [1.5, 0.4], [1.4, 0.4], [1.4, 0.4], [1.4, 0.4], [1.4, 0.4], [1.4, 0.4], [1.3, 0.5], [1.3, 0.5], [1.3, 0.5], [1.3, 0.5], [1.2, 0.5], [1.2, 0.5], [1.2, 0.5], [1.2, 0.5], [1.2, 0.5], [1.1, 0.5], [1.1, 0.5], [1.1, 0.5], [1.1, 0.5], [1.1, 0.5], [1, 0.5], [1, 0.5], [1, 0.5], [1, 0.5], [0.9, 0.5], [0.9, 0.5], [0.9, 0.5], [0.9, 0.5], [0.9, 0.5], [0.8, 0.5], [0.8, 0.5], [0.8, 0.5], [0.8, 0.5], [0.8, 0.5], [0.7, 0.5], [0.7, 0.5], [0.7, 0.5], [0.7, 0.5], [0.6, 0.4], [0.6, 0.4], [0.6, 0.4], [0.6, 0.4], [0.6, 0.4], [0.5, 0.4], [0.5, 0.4], [0.5, 0.4], [0.5, 0.4], [0.5, 0.4], [0.4, 0.4], [0.4, 0.4], [0.4, 0.4], [0.4, 0.3], [0.4, 0.3], [0.4, 0.3], [0.3, 0.3], [0.3, 0.3], [0.3, 0.3], [0.3, 0.3], [0.3, 0.3], [0.2, 0.2], [0.2, 0.2], [0.2, 0.2], [0.2, 0.2], [0.2, 0.2], [0.2, 0.2], [0.1, 0.2], [0.1, 0.1], [0.1, 0.1], [0.1, 0.1], [0.1, 0.1], [0.1, 0.1], [0.1, 0.1], [0, 0.1], [0, 0], [0, 0], [0, 0]]] }" ) ); + res = exportPolygon.asJSON( 1 ); + QCOMPARE( res, expectedSimpleJson ); + + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 0.1, 0, 11, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 0.2, 0, 12, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 0.1, 0.05, 13, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) ); + exportPolygon.addInteriorRing( ring ); + + QString expectedJson( QStringLiteral( "{\"type\": \"Polygon\", \"coordinates\": [[ [0, 0], [1, 0], [2, 0], [2, 0], [2, 0], [2, 0.1], [1.9, 0.1], [1.9, 0.1], [1.9, 0.1], [1.9, 0.1], [1.9, 0.1], [1.9, 0.1], [1.9, 0.2], [1.8, 0.2], [1.8, 0.2], [1.8, 0.2], [1.8, 0.2], [1.8, 0.2], [1.8, 0.2], [1.7, 0.3], [1.7, 0.3], [1.7, 0.3], [1.7, 0.3], [1.7, 0.3], [1.6, 0.3], [1.6, 0.3], [1.6, 0.3], [1.6, 0.4], [1.6, 0.4], [1.6, 0.4], [1.5, 0.4], [1.5, 0.4], [1.5, 0.4], [1.5, 0.4], [1.5, 0.4], [1.4, 0.4], [1.4, 0.4], [1.4, 0.4], [1.4, 0.4], [1.4, 0.4], [1.3, 0.5], [1.3, 0.5], [1.3, 0.5], [1.3, 0.5], [1.2, 0.5], [1.2, 0.5], [1.2, 0.5], [1.2, 0.5], [1.2, 0.5], [1.1, 0.5], [1.1, 0.5], [1.1, 0.5], [1.1, 0.5], [1.1, 0.5], [1, 0.5], [1, 0.5], [1, 0.5], [1, 0.5], [0.9, 0.5], [0.9, 0.5], [0.9, 0.5], [0.9, 0.5], [0.9, 0.5], [0.8, 0.5], [0.8, 0.5], [0.8, 0.5], [0.8, 0.5], [0.8, 0.5], [0.7, 0.5], [0.7, 0.5], [0.7, 0.5], [0.7, 0.5], [0.6, 0.4], [0.6, 0.4], [0.6, 0.4], [0.6, 0.4], [0.6, 0.4], [0.5, 0.4], [0.5, 0.4], [0.5, 0.4], [0.5, 0.4], [0.5, 0.4], [0.4, 0.4], [0.4, 0.4], [0.4, 0.4], [0.4, 0.3], [0.4, 0.3], [0.4, 0.3], [0.3, 0.3], [0.3, 0.3], [0.3, 0.3], [0.3, 0.3], [0.3, 0.3], [0.2, 0.2], [0.2, 0.2], [0.2, 0.2], [0.2, 0.2], [0.2, 0.2], [0.2, 0.2], [0.1, 0.2], [0.1, 0.1], [0.1, 0.1], [0.1, 0.1], [0.1, 0.1], [0.1, 0.1], [0.1, 0.1], [0, 0.1], [0, 0], [0, 0], [0, 0]], [ [0, 0], [0.1, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.2, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0.1, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]] }" ) ); + res = exportPolygon.asJSON( 1 ); + QCOMPARE( res, expectedJson ); + + + QgsCurvePolygon exportPolygonFloat; + ext = new QgsCircularString(); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 1 / 3.0, 0, 11, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 2 / 3.0, 0, 12, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 1 / 3.0, 0.5, 13, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) ); + exportPolygonFloat.setExteriorRing( ext ); + ring = new QgsCircularString(); + ring->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 0.1 / 3.0, 0, 11, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 0.2 / 3.0, 0, 12, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 0.1 / 3.0, 0.05 / 3.0, 13, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 10, 1 ) ); + exportPolygonFloat.addInteriorRing( ring ); + + QString expectedJsonPrec3( QStringLiteral( "{\"type\": \"Polygon\", \"coordinates\": [[ [0, 0], [0.333, 0], [0.667, 0], [0.669, 0.006], [0.671, 0.012], [0.673, 0.018], [0.676, 0.024], [0.677, 0.029], [0.679, 0.035], [0.681, 0.042], [0.683, 0.048], [0.684, 0.054], [0.686, 0.06], [0.687, 0.066], [0.688, 0.072], [0.689, 0.078], [0.69, 0.084], [0.691, 0.091], [0.692, 0.097], [0.693, 0.103], [0.693, 0.109], [0.694, 0.116], [0.694, 0.122], [0.694, 0.128], [0.694, 0.135], [0.694, 0.141], [0.694, 0.147], [0.694, 0.153], [0.694, 0.16], [0.693, 0.166], [0.693, 0.172], [0.692, 0.178], [0.692, 0.185], [0.691, 0.191], [0.69, 0.197], [0.689, 0.203], [0.687, 0.209], [0.686, 0.216], [0.685, 0.222], [0.683, 0.228], [0.682, 0.234], [0.68, 0.24], [0.678, 0.246], [0.676, 0.252], [0.674, 0.258], [0.672, 0.264], [0.67, 0.27], [0.668, 0.275], [0.665, 0.281], [0.663, 0.287], [0.66, 0.293], [0.657, 0.298], [0.654, 0.304], [0.652, 0.31], [0.649, 0.315], [0.645, 0.321], [0.642, 0.326], [0.639, 0.331], [0.636, 0.337], [0.632, 0.342], [0.628, 0.347], [0.625, 0.352], [0.621, 0.357], [0.617, 0.362], [0.613, 0.367], [0.609, 0.372], [0.605, 0.377], [0.601, 0.381], [0.597, 0.386], [0.592, 0.39], [0.588, 0.395], [0.584, 0.399], [0.579, 0.404], [0.574, 0.408], [0.57, 0.412], [0.565, 0.416], [0.56, 0.42], [0.555, 0.424], [0.55, 0.428], [0.545, 0.431], [0.54, 0.435], [0.535, 0.439], [0.529, 0.442], [0.524, 0.445], [0.519, 0.449], [0.513, 0.452], [0.508, 0.455], [0.502, 0.458], [0.497, 0.461], [0.491, 0.464], [0.485, 0.466], [0.48, 0.469], [0.474, 0.471], [0.468, 0.474], [0.462, 0.476], [0.456, 0.478], [0.451, 0.48], [0.445, 0.482], [0.439, 0.484], [0.433, 0.486], [0.426, 0.488], [0.42, 0.489], [0.414, 0.491], [0.408, 0.492], [0.402, 0.493], [0.396, 0.495], [0.39, 0.496], [0.383, 0.497], [0.377, 0.497], [0.371, 0.498], [0.365, 0.499], [0.358, 0.499], [0.352, 0.5], [0.346, 0.5], [0.34, 0.5], [0.333, 0.5], [0.327, 0.5], [0.321, 0.5], [0.314, 0.5], [0.308, 0.499], [0.302, 0.499], [0.296, 0.498], [0.289, 0.497], [0.283, 0.497], [0.277, 0.496], [0.271, 0.495], [0.265, 0.493], [0.259, 0.492], [0.252, 0.491], [0.246, 0.489], [0.24, 0.488], [0.234, 0.486], [0.228, 0.484], [0.222, 0.482], [0.216, 0.48], [0.21, 0.478], [0.204, 0.476], [0.198, 0.474], [0.193, 0.471], [0.187, 0.469], [0.181, 0.466], [0.176, 0.464], [0.17, 0.461], [0.164, 0.458], [0.159, 0.455], [0.153, 0.452], [0.148, 0.449], [0.143, 0.445], [0.137, 0.442], [0.132, 0.439], [0.127, 0.435], [0.122, 0.431], [0.117, 0.428], [0.112, 0.424], [0.107, 0.42], [0.102, 0.416], [0.097, 0.412], [0.092, 0.408], [0.088, 0.404], [0.083, 0.399], [0.079, 0.395], [0.074, 0.39], [0.07, 0.386], [0.066, 0.381], [0.061, 0.377], [0.057, 0.372], [0.053, 0.367], [0.049, 0.362], [0.046, 0.357], [0.042, 0.352], [0.038, 0.347], [0.035, 0.342], [0.031, 0.337], [0.028, 0.331], [0.024, 0.326], [0.021, 0.321], [0.018, 0.315], [0.015, 0.31], [0.012, 0.304], [0.009, 0.298], [0.007, 0.293], [0.004, 0.287], [0.001, 0.281], [-0.001, 0.275], [-0.003, 0.27], [-0.005, 0.264], [-0.008, 0.258], [-0.01, 0.252], [-0.012, 0.246], [-0.013, 0.24], [-0.015, 0.234], [-0.017, 0.228], [-0.018, 0.222], [-0.02, 0.216], [-0.021, 0.209], [-0.022, 0.203], [-0.023, 0.197], [-0.024, 0.191], [-0.025, 0.185], [-0.026, 0.178], [-0.026, 0.172], [-0.027, 0.166], [-0.027, 0.16], [-0.027, 0.153], [-0.028, 0.147], [-0.028, 0.141], [-0.028, 0.135], [-0.028, 0.128], [-0.027, 0.122], [-0.027, 0.116], [-0.027, 0.109], [-0.026, 0.103], [-0.025, 0.097], [-0.025, 0.091], [-0.024, 0.084], [-0.023, 0.078], [-0.022, 0.072], [-0.02, 0.066], [-0.019, 0.06], [-0.018, 0.054], [-0.016, 0.048], [-0.014, 0.042], [-0.013, 0.035], [-0.011, 0.029], [-0.009, 0.024], [-0.007, 0.018], [-0.005, 0.012], [-0.002, 0.006], [0, 0]], [ [0, 0], [0.033, 0], [0.067, 0], [0.066, 0.001], [0.066, 0.001], [0.065, 0.002], [0.065, 0.002], [0.064, 0.003], [0.064, 0.003], [0.063, 0.004], [0.063, 0.004], [0.062, 0.005], [0.062, 0.005], [0.061, 0.006], [0.061, 0.006], [0.06, 0.007], [0.06, 0.007], [0.059, 0.008], [0.059, 0.008], [0.058, 0.009], [0.057, 0.009], [0.057, 0.009], [0.056, 0.01], [0.056, 0.01], [0.055, 0.011], [0.054, 0.011], [0.054, 0.011], [0.053, 0.012], [0.052, 0.012], [0.052, 0.012], [0.051, 0.013], [0.051, 0.013], [0.05, 0.013], [0.049, 0.014], [0.049, 0.014], [0.048, 0.014], [0.047, 0.014], [0.046, 0.015], [0.046, 0.015], [0.045, 0.015], [0.044, 0.015], [0.044, 0.015], [0.043, 0.016], [0.042, 0.016], [0.042, 0.016], [0.041, 0.016], [0.04, 0.016], [0.039, 0.016], [0.039, 0.016], [0.038, 0.016], [0.037, 0.016], [0.037, 0.017], [0.036, 0.017], [0.035, 0.017], [0.034, 0.017], [0.034, 0.017], [0.033, 0.017], [0.032, 0.017], [0.032, 0.017], [0.031, 0.017], [0.03, 0.017], [0.029, 0.016], [0.029, 0.016], [0.028, 0.016], [0.027, 0.016], [0.027, 0.016], [0.026, 0.016], [0.025, 0.016], [0.024, 0.016], [0.024, 0.016], [0.023, 0.015], [0.022, 0.015], [0.022, 0.015], [0.021, 0.015], [0.02, 0.015], [0.02, 0.014], [0.019, 0.014], [0.018, 0.014], [0.017, 0.014], [0.017, 0.013], [0.016, 0.013], [0.016, 0.013], [0.015, 0.012], [0.014, 0.012], [0.014, 0.012], [0.013, 0.011], [0.012, 0.011], [0.012, 0.011], [0.011, 0.01], [0.01, 0.01], [0.01, 0.009], [0.009, 0.009], [0.009, 0.009], [0.008, 0.008], [0.008, 0.008], [0.007, 0.007], [0.006, 0.007], [0.006, 0.006], [0.005, 0.006], [0.005, 0.005], [0.004, 0.005], [0.004, 0.004], [0.003, 0.004], [0.003, 0.003], [0.002, 0.003], [0.002, 0.002], [0.001, 0.002], [0.001, 0.001], [0, 0.001], [0, 0]]] }" ) ); + res = exportPolygonFloat.asJSON( 3 ); + QCOMPARE( exportPolygonFloat.asJSON( 3 ), expectedJsonPrec3 ); + + // as GML2 + QString expectedGML2( QStringLiteral( "0,0 1,0 2,0 1.98685,0.01722 1.97341,0.03421 1.95967,0.05096 1.94564,0.06747 1.93133,0.08374 1.91674,0.09976 1.90188,0.11552 1.88674,0.13102 1.87134,0.14625 1.85567,0.16122 1.83975,0.17592 1.82358,0.19033 1.80716,0.20446 1.79049,0.21831 1.77359,0.23186 1.75646,0.24512 1.7391,0.25809 1.72151,0.27074 1.70371,0.2831 1.6857,0.29514 1.66748,0.30687 1.64907,0.31828 1.63045,0.32936 1.61165,0.34013 1.59267,0.35057 1.5735,0.36067 1.55417,0.37045 1.53466,0.37988 1.515,0.38898 1.49518,0.39773 1.47522,0.40614 1.45511,0.41421 1.43486,0.42192 1.41448,0.42928 1.39398,0.43629 1.37336,0.44294 1.35263,0.44923 1.33179,0.45516 1.31086,0.46073 1.28983,0.46594 1.26871,0.47078 1.24751,0.47525 1.22624,0.47936 1.2049,0.48309 1.18349,0.48646 1.16204,0.48945 1.14053,0.49208 1.11898,0.49432 1.0974,0.4962 1.07578,0.4977 1.05415,0.49883 1.0325,0.49958 1.01083,0.49995 0.98917,0.49995 0.9675,0.49958 0.94585,0.49883 0.92422,0.4977 0.9026,0.4962 0.88102,0.49432 0.85947,0.49208 0.83796,0.48945 0.81651,0.48646 0.7951,0.48309 0.77376,0.47936 0.75249,0.47525 0.73129,0.47078 0.71017,0.46594 0.68914,0.46073 0.66821,0.45516 0.64737,0.44923 0.62664,0.44294 0.60602,0.43629 0.58552,0.42928 0.56514,0.42192 0.54489,0.41421 0.52478,0.40614 0.50482,0.39773 0.485,0.38898 0.46534,0.37988 0.44583,0.37045 0.4265,0.36067 0.40733,0.35057 0.38835,0.34013 0.36955,0.32936 0.35093,0.31828 0.33252,0.30687 0.3143,0.29514 0.29629,0.2831 0.27849,0.27074 0.2609,0.25809 0.24354,0.24512 0.22641,0.23186 0.20951,0.21831 0.19284,0.20446 0.17642,0.19033 0.16025,0.17592 0.14433,0.16122 0.12866,0.14625 0.11326,0.13102 0.09812,0.11552 0.08326,0.09976 0.06867,0.08374 0.05436,0.06747 0.04033,0.05096 0.02659,0.03421 0.01315,0.01722 0,00,0 0.1,0 0.2,0 0.19869,0.00172 0.19734,0.00342 0.19597,0.0051 0.19456,0.00675 0.19313,0.00837 0.19167,0.00998 0.19019,0.01155 0.18867,0.0131 0.18713,0.01463 0.18557,0.01612 0.18398,0.01759 0.18236,0.01903 0.18072,0.02045 0.17905,0.02183 0.17736,0.02319 0.17565,0.02451 0.17391,0.02581 0.17215,0.02707 0.17037,0.02831 0.16857,0.02951 0.16675,0.03069 0.16491,0.03183 0.16305,0.03294 0.16117,0.03401 0.15927,0.03506 0.15735,0.03607 0.15542,0.03704 0.15347,0.03799 0.1515,0.0389 0.14952,0.03977 0.14752,0.04061 0.14551,0.04142 0.14349,0.04219 0.14145,0.04293 0.1394,0.04363 0.13734,0.04429 0.13526,0.04492 0.13318,0.04552 0.13109,0.04607 0.12898,0.04659 0.12687,0.04708 0.12475,0.04753 0.12262,0.04794 0.12049,0.04831 0.11835,0.04865 0.1162,0.04895 0.11405,0.04921 0.1119,0.04943 0.10974,0.04962 0.10758,0.04977 0.10541,0.04988 0.10325,0.04996 0.10108,0.05 0.09892,0.05 0.09675,0.04996 0.09459,0.04988 0.09242,0.04977 0.09026,0.04962 0.0881,0.04943 0.08595,0.04921 0.0838,0.04895 0.08165,0.04865 0.07951,0.04831 0.07738,0.04794 0.07525,0.04753 0.07313,0.04708 0.07102,0.04659 0.06891,0.04607 0.06682,0.04552 0.06474,0.04492 0.06266,0.04429 0.0606,0.04363 0.05855,0.04293 0.05651,0.04219 0.05449,0.04142 0.05248,0.04061 0.05048,0.03977 0.0485,0.0389 0.04653,0.03799 0.04458,0.03704 0.04265,0.03607 0.04073,0.03506 0.03883,0.03401 0.03695,0.03294 0.03509,0.03183 0.03325,0.03069 0.03143,0.02951 0.02963,0.02831 0.02785,0.02707 0.02609,0.02581 0.02435,0.02451 0.02264,0.02319 0.02095,0.02183 0.01928,0.02045 0.01764,0.01903 0.01602,0.01759 0.01443,0.01612 0.01287,0.01463 0.01133,0.0131 0.00981,0.01155 0.00833,0.00998 0.00687,0.00837 0.00544,0.00675 0.00403,0.0051 0.00266,0.00342 0.00131,0.00172 0,0" ) ); + res = elemToString( exportPolygon.asGML2( doc, 5 ) ); + QGSCOMPAREGML( res, expectedGML2 ); + + QString expectedGML2prec2( QStringLiteral( "0,0 1,0 2,0 1.99,0.02 1.97,0.03 1.96,0.05 1.95,0.07 1.93,0.08 1.92,0.1 1.9,0.12 1.89,0.13 1.87,0.15 1.86,0.16 1.84,0.18 1.82,0.19 1.81,0.2 1.79,0.22 1.77,0.23 1.76,0.25 1.74,0.26 1.72,0.27 1.7,0.28 1.69,0.3 1.67,0.31 1.65,0.32 1.63,0.33 1.61,0.34 1.59,0.35 1.57,0.36 1.55,0.37 1.53,0.38 1.52,0.39 1.5,0.4 1.48,0.41 1.46,0.41 1.43,0.42 1.41,0.43 1.39,0.44 1.37,0.44 1.35,0.45 1.33,0.46 1.31,0.46 1.29,0.47 1.27,0.47 1.25,0.48 1.23,0.48 1.2,0.48 1.18,0.49 1.16,0.49 1.14,0.49 1.12,0.49 1.1,0.5 1.08,0.5 1.05,0.5 1.03,0.5 1.01,0.5 0.99,0.5 0.97,0.5 0.95,0.5 0.92,0.5 0.9,0.5 0.88,0.49 0.86,0.49 0.84,0.49 0.82,0.49 0.8,0.48 0.77,0.48 0.75,0.48 0.73,0.47 0.71,0.47 0.69,0.46 0.67,0.46 0.65,0.45 0.63,0.44 0.61,0.44 0.59,0.43 0.57,0.42 0.54,0.41 0.52,0.41 0.5,0.4 0.48,0.39 0.47,0.38 0.45,0.37 0.43,0.36 0.41,0.35 0.39,0.34 0.37,0.33 0.35,0.32 0.33,0.31 0.31,0.3 0.3,0.28 0.28,0.27 0.26,0.26 0.24,0.25 0.23,0.23 0.21,0.22 0.19,0.2 0.18,0.19 0.16,0.18 0.14,0.16 0.13,0.15 0.11,0.13 0.1,0.12 0.08,0.1 0.07,0.08 0.05,0.07 0.04,0.05 0.03,0.03 0.01,0.02 0,00,0 0.1,0 0.2,0 0.2,0 0.2,0 0.2,0.01 0.19,0.01 0.19,0.01 0.19,0.01 0.19,0.01 0.19,0.01 0.19,0.01 0.19,0.02 0.18,0.02 0.18,0.02 0.18,0.02 0.18,0.02 0.18,0.02 0.18,0.02 0.17,0.03 0.17,0.03 0.17,0.03 0.17,0.03 0.17,0.03 0.16,0.03 0.16,0.03 0.16,0.03 0.16,0.04 0.16,0.04 0.16,0.04 0.15,0.04 0.15,0.04 0.15,0.04 0.15,0.04 0.15,0.04 0.14,0.04 0.14,0.04 0.14,0.04 0.14,0.04 0.14,0.04 0.13,0.05 0.13,0.05 0.13,0.05 0.13,0.05 0.12,0.05 0.12,0.05 0.12,0.05 0.12,0.05 0.12,0.05 0.11,0.05 0.11,0.05 0.11,0.05 0.11,0.05 0.11,0.05 0.1,0.05 0.1,0.05 0.1,0.05 0.1,0.05 0.09,0.05 0.09,0.05 0.09,0.05 0.09,0.05 0.09,0.05 0.08,0.05 0.08,0.05 0.08,0.05 0.08,0.05 0.08,0.05 0.07,0.05 0.07,0.05 0.07,0.05 0.07,0.05 0.06,0.04 0.06,0.04 0.06,0.04 0.06,0.04 0.06,0.04 0.05,0.04 0.05,0.04 0.05,0.04 0.05,0.04 0.05,0.04 0.04,0.04 0.04,0.04 0.04,0.04 0.04,0.03 0.04,0.03 0.04,0.03 0.03,0.03 0.03,0.03 0.03,0.03 0.03,0.03 0.03,0.03 0.02,0.02 0.02,0.02 0.02,0.02 0.02,0.02 0.02,0.02 0.02,0.02 0.01,0.02 0.01,0.01 0.01,0.01 0.01,0.01 0.01,0.01 0.01,0.01 0.01,0.01 0,0.01 0,0 0,0 0,0" ) ); + res = elemToString( exportPolygon.asGML2( doc, 2 ) ); + QGSCOMPAREGML( res, expectedGML2prec2 ); + + //as GML3 + QString expectedGML3( QStringLiteral( "0 0 10 1 0 11 2 0 12 1 0.5 13 0 0 100 0 10 0.10000000000000001 0 11 0.20000000000000001 0 12 0.10000000000000001 0.05 13 0 0 10" ) ); + res = elemToString( exportPolygon.asGML3( doc ) ); + QCOMPARE( res, expectedGML3 ); + + QString expectedGML3prec3( QStringLiteral( "0 0 10 1 0 11 2 0 12 1 0.5 13 0 0 100 0 10 0.1 0 11 0.2 0 12 0.1 0.05 13 0 0 10" ) ); + res = elemToString( exportPolygon.asGML3( doc, 3 ) ); + QCOMPARE( res, expectedGML3prec3 ); + + //removing the fourth to last vertex removes the whole ring + QgsCurvePolygon p20; + QgsCircularString *p20ExteriorRing = new QgsCircularString(); + p20ExteriorRing->setPoints( QList() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) << QgsPoint( 1, 1 ) << QgsPoint( 0, 0 ) ); + p20.setExteriorRing( p20ExteriorRing ); + QVERIFY( p20.exteriorRing() ); + p20.deleteVertex( QgsVertexId( 0, 0, 2 ) ); + QVERIFY( !p20.exteriorRing() ); + + //boundary + QgsCircularString boundary1; + boundary1.setPoints( QgsPointSequence() << QgsPoint( 0, 0, 1 ) << QgsPoint( 1, 0, 2 ) << QgsPoint( 2, 0, 3 ) + << QgsPoint( 1, 0.5, 4 ) << QgsPoint( 0, 0, 1 ) ); + QgsCurvePolygon boundaryPolygon; + QVERIFY( !boundaryPolygon.boundary() ); + + boundaryPolygon.setExteriorRing( boundary1.clone() ); + QgsAbstractGeometry *boundary = boundaryPolygon.boundary(); + QgsCircularString *lineBoundary = dynamic_cast< QgsCircularString * >( boundary ); + QVERIFY( lineBoundary ); + QCOMPARE( lineBoundary->numPoints(), 5 ); + QCOMPARE( lineBoundary->xAt( 0 ), 0.0 ); + QCOMPARE( lineBoundary->xAt( 1 ), 1.0 ); + QCOMPARE( lineBoundary->xAt( 2 ), 2.0 ); + QCOMPARE( lineBoundary->xAt( 3 ), 1.0 ); + QCOMPARE( lineBoundary->xAt( 4 ), 0.0 ); + QCOMPARE( lineBoundary->yAt( 0 ), 0.0 ); + QCOMPARE( lineBoundary->yAt( 1 ), 0.0 ); + QCOMPARE( lineBoundary->yAt( 2 ), 0.0 ); + QCOMPARE( lineBoundary->yAt( 3 ), 0.5 ); + QCOMPARE( lineBoundary->yAt( 4 ), 0.0 ); + delete boundary; + + // add interior rings + QgsCircularString boundaryRing1; + boundaryRing1.setPoints( QList() << QgsPoint( 0.1, 0.1 ) << QgsPoint( 0.2, 0.1 ) << QgsPoint( 0.2, 0.2 ) ); + QgsCircularString boundaryRing2; + boundaryRing2.setPoints( QList() << QgsPoint( 0.8, 0.8 ) << QgsPoint( 0.9, 0.8 ) << QgsPoint( 0.9, 0.9 ) ); + boundaryPolygon.setInteriorRings( QList< QgsCurve * >() << boundaryRing1.clone() << boundaryRing2.clone() ); + boundary = boundaryPolygon.boundary(); + QgsMultiCurve *multiLineBoundary = dynamic_cast< QgsMultiCurve * >( boundary ); + QVERIFY( multiLineBoundary ); + QCOMPARE( multiLineBoundary->numGeometries(), 3 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 0 ) )->numPoints(), 5 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 0 ) )->xAt( 0 ), 0.0 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 0 ) )->xAt( 1 ), 1.0 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 0 ) )->xAt( 2 ), 2.0 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 0 ) )->xAt( 3 ), 1.0 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 0 ) )->xAt( 4 ), 0.0 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 0 ) )->yAt( 0 ), 0.0 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 0 ) )->yAt( 1 ), 0.0 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 0 ) )->yAt( 2 ), 0.0 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 0 ) )->yAt( 3 ), 0.5 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 0 ) )->yAt( 4 ), 0.0 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 1 ) )->numPoints(), 3 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 1 ) )->xAt( 0 ), 0.1 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 1 ) )->xAt( 1 ), 0.2 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 1 ) )->xAt( 2 ), 0.2 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 1 ) )->yAt( 0 ), 0.1 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 1 ) )->yAt( 1 ), 0.1 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 1 ) )->yAt( 2 ), 0.2 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 2 ) )->numPoints(), 3 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 2 ) )->xAt( 0 ), 0.8 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 2 ) )->xAt( 1 ), 0.9 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 2 ) )->xAt( 2 ), 0.9 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 2 ) )->yAt( 0 ), 0.8 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 2 ) )->yAt( 1 ), 0.8 ); + QCOMPARE( dynamic_cast< QgsCircularString * >( multiLineBoundary->geometryN( 2 ) )->yAt( 2 ), 0.9 ); + boundaryPolygon.setInteriorRings( QList< QgsCurve * >() ); + delete boundary; + + //test boundary with z + boundary1.setPoints( QList() << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 10 ) << QgsPoint( QgsWkbTypes::PointZ, 1, 0, 15 ) + << QgsPoint( QgsWkbTypes::PointZ, 1, 1, 20 ) ); + boundaryPolygon.setExteriorRing( boundary1.clone() ); + boundary = boundaryPolygon.boundary(); + lineBoundary = dynamic_cast< QgsCircularString * >( boundary ); + QVERIFY( lineBoundary ); + QCOMPARE( lineBoundary->numPoints(), 3 ); + QCOMPARE( lineBoundary->wkbType(), QgsWkbTypes::CircularStringZ ); + QCOMPARE( lineBoundary->pointN( 0 ).z(), 10.0 ); + QCOMPARE( lineBoundary->pointN( 1 ).z(), 15.0 ); + QCOMPARE( lineBoundary->pointN( 2 ).z(), 20.0 ); + delete boundary; + + // remove interior rings + QgsCircularString removeRingsExt; + removeRingsExt.setPoints( QList() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) << QgsPoint( 1, 1 ) << QgsPoint( 0, 0 ) ); + QgsCurvePolygon removeRings1; + removeRings1.removeInteriorRings(); + + removeRings1.setExteriorRing( boundary1.clone() ); + removeRings1.removeInteriorRings(); + QCOMPARE( removeRings1.numInteriorRings(), 0 ); + + // add interior rings + QgsCircularString removeRingsRing1; + removeRingsRing1.setPoints( QgsPointSequence() << QgsPoint( 0, 0, 1 ) << QgsPoint( 0.1, 1, 2 ) << QgsPoint( 0, 2, 3 ) + << QgsPoint( -0.1, 1.2, 4 ) << QgsPoint( 0, 0, 1 ) ); + QgsCircularString removeRingsRing2; + removeRingsRing2.setPoints( QgsPointSequence() << QgsPoint( 0, 0, 1 ) << QgsPoint( 0.01, 0.1, 2 ) << QgsPoint( 0, 0.2, 3 ) + << QgsPoint( -0.01, 0.12, 4 ) << QgsPoint( 0, 0, 1 ) ); + removeRings1.setInteriorRings( QList< QgsCurve * >() << removeRingsRing1.clone() << removeRingsRing2.clone() ); + + // remove ring with size filter + removeRings1.removeInteriorRings( 0.05 ); + QCOMPARE( removeRings1.numInteriorRings(), 1 ); + + // remove ring with no size filter + removeRings1.removeInteriorRings(); + QCOMPARE( removeRings1.numInteriorRings(), 0 ); + + // cast + QVERIFY( !QgsCurvePolygon().cast( nullptr ) ); + QgsCurvePolygon pCast; + QVERIFY( QgsCurvePolygon().cast( &pCast ) ); + QgsCurvePolygon pCast2; + pCast2.fromWkt( QStringLiteral( "CurvePolygonZ((0 0 0, 0 1 1, 1 0 2, 0 0 0))" ) ); + QVERIFY( QgsCurvePolygon().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "CurvePolygonM((0 0 1, 0 1 2, 1 0 3, 0 0 1))" ) ); + QVERIFY( QgsCurvePolygon().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "CurvePolygonZM((0 0 0 1, 0 1 1 2, 1 0 2 3, 0 0 0 1))" ) ); + QVERIFY( QgsCurvePolygon().cast( &pCast2 ) ); + + // draw - most tests are in test_qgsgeometry.py + QgsCurvePolygon empty; + QPainter p; + empty.draw( p ); //no crash! + + + // closestSegment + QgsPoint pt; + QgsVertexId v; + bool leftOf = false; + ( void )empty.closestSegment( QgsPoint( 1, 2 ), pt, v ); // empty curve, just want no crash + + QgsCurvePolygon cp12; + QgsLineString cp12ls; + cp12ls.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 7, 12 ) << QgsPoint( 5, 15 ) << QgsPoint( 5, 10 ) ); + cp12.setExteriorRing( cp12ls.clone() ); + QGSCOMPARENEAR( cp12.closestSegment( QgsPoint( 4, 11 ), pt, v, &leftOf ), 1.0, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5, 0.01 ); + QGSCOMPARENEAR( pt.y(), 11, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 3 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( cp12.closestSegment( QgsPoint( 8, 11 ), pt, v, &leftOf ), 2.0, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 7, 0.01 ); + QGSCOMPARENEAR( pt.y(), 12, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( cp12.closestSegment( QgsPoint( 6, 11.5 ), pt, v, &leftOf ), 0.125000, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 6.25, 0.01 ); + QGSCOMPARENEAR( pt.y(), 11.25, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, true ); + QGSCOMPARENEAR( cp12.closestSegment( QgsPoint( 7, 16 ), pt, v, &leftOf ), 4.923077, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5.153846, 0.01 ); + QGSCOMPARENEAR( pt.y(), 14.769231, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( cp12.closestSegment( QgsPoint( 5.5, 13.5 ), pt, v, &leftOf ), 0.173077, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5.846154, 0.01 ); + QGSCOMPARENEAR( pt.y(), 13.730769, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, true ); + // point directly on segment + QCOMPARE( cp12.closestSegment( QgsPoint( 5, 15 ), pt, v, &leftOf ), 0.0 ); + QCOMPARE( pt, QgsPoint( 5, 15 ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + + // with interior ring + cp12ls.setPoints( QgsPointSequence() << QgsPoint( 6, 11.5 ) << QgsPoint( 6.5, 12 ) << QgsPoint( 6, 13 ) << QgsPoint( 6, 11.5 ) ); + cp12.addInteriorRing( cp12ls.clone() ); + QGSCOMPARENEAR( cp12.closestSegment( QgsPoint( 4, 11 ), pt, v, &leftOf ), 1.0, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5, 0.01 ); + QGSCOMPARENEAR( pt.y(), 11, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 3 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( cp12.closestSegment( QgsPoint( 8, 11 ), pt, v, &leftOf ), 2.0, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 7, 0.01 ); + QGSCOMPARENEAR( pt.y(), 12, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( cp12.closestSegment( QgsPoint( 6, 11.4 ), pt, v, &leftOf ), 0.01, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 6.0, 0.01 ); + QGSCOMPARENEAR( pt.y(), 11.5, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 1, 1 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( cp12.closestSegment( QgsPoint( 7, 16 ), pt, v, &leftOf ), 4.923077, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5.153846, 0.01 ); + QGSCOMPARENEAR( pt.y(), 14.769231, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( cp12.closestSegment( QgsPoint( 5.5, 13.5 ), pt, v, &leftOf ), 0.173077, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5.846154, 0.01 ); + QGSCOMPARENEAR( pt.y(), 13.730769, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, true ); + // point directly on segment + QCOMPARE( cp12.closestSegment( QgsPoint( 6, 13 ), pt, v, &leftOf ), 0.0 ); + QCOMPARE( pt, QgsPoint( 6, 13 ) ); + QCOMPARE( v, QgsVertexId( 0, 1, 2 ) ); + + //nextVertex + QgsCurvePolygon cp13; + QVERIFY( !cp13.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 0, -2 ); + QVERIFY( !cp13.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 0, 10 ); + QVERIFY( !cp13.nextVertex( v, pt ) ); + QgsLineString lp22; + lp22.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 12 ) << QgsPoint( 1, 2 ) ); + cp13.setExteriorRing( lp22.clone() ); + v = QgsVertexId( 0, 0, 4 ); //out of range + QVERIFY( !cp13.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 0, -5 ); + QVERIFY( cp13.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 0, -1 ); + QVERIFY( cp13.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 0 ) ); + QCOMPARE( pt, QgsPoint( 1, 2 ) ); + QVERIFY( cp13.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( pt, QgsPoint( 11, 12 ) ); + QVERIFY( cp13.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( pt, QgsPoint( 1, 12 ) ); + QVERIFY( cp13.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 3 ) ); + QCOMPARE( pt, QgsPoint( 1, 2 ) ); + v = QgsVertexId( 0, 1, 0 ); + QVERIFY( !cp13.nextVertex( v, pt ) ); + v = QgsVertexId( 1, 0, 0 ); + QVERIFY( cp13.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 1, 0, 1 ) ); //test that part number is maintained + QCOMPARE( pt, QgsPoint( 11, 12 ) ); + // add interior ring + lp22.setPoints( QgsPointSequence() << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) << QgsPoint( 11, 22 ) << QgsPoint( 11, 12 ) ); + cp13.addInteriorRing( lp22.clone() ); + v = QgsVertexId( 0, 1, 4 ); //out of range + QVERIFY( !cp13.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 1, -5 ); + QVERIFY( cp13.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 1, -1 ); + QVERIFY( cp13.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 1, 0 ) ); + QCOMPARE( pt, QgsPoint( 11, 12 ) ); + QVERIFY( cp13.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 1, 1 ) ); + QCOMPARE( pt, QgsPoint( 21, 22 ) ); + QVERIFY( cp13.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 1, 2 ) ); + QCOMPARE( pt, QgsPoint( 11, 22 ) ); + QVERIFY( cp13.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 1, 3 ) ); + QCOMPARE( pt, QgsPoint( 11, 12 ) ); + v = QgsVertexId( 0, 2, 0 ); + QVERIFY( !cp13.nextVertex( v, pt ) ); + v = QgsVertexId( 1, 1, 0 ); + QVERIFY( cp13.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 1, 1, 1 ) ); //test that part number is maintained + QCOMPARE( pt, QgsPoint( 21, 22 ) ); + + // dropZValue + QgsCurvePolygon p23; + p23.dropZValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::CurvePolygon ); + QgsLineString lp23; + lp23.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 12 ) << QgsPoint( 1, 2 ) ); + p23.setExteriorRing( lp23.clone() ); + p23.addInteriorRing( lp23.clone() ); + QCOMPARE( p23.wkbType(), QgsWkbTypes::CurvePolygon ); + p23.dropZValue(); // not z + QCOMPARE( p23.wkbType(), QgsWkbTypes::CurvePolygon ); + QCOMPARE( p23.exteriorRing()->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.exteriorRing() )->pointN( 0 ), QgsPoint( 1, 2 ) ); + QCOMPARE( p23.interiorRing( 0 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + // with z + lp23.setPoints( QgsPointSequence() << QgsPoint( 1, 2, 3 ) << QgsPoint( 11, 12, 13 ) << QgsPoint( 1, 12, 23 ) << QgsPoint( 1, 2, 3 ) ); + p23.clear(); + p23.setExteriorRing( lp23.clone() ); + p23.addInteriorRing( lp23.clone() ); + QCOMPARE( p23.wkbType(), QgsWkbTypes::CurvePolygonZ ); + p23.dropZValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::CurvePolygon ); + QCOMPARE( p23.exteriorRing()->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.exteriorRing() )->pointN( 0 ), QgsPoint( 1, 2 ) ); + QCOMPARE( p23.interiorRing( 0 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + // with zm + lp23.setPoints( QgsPointSequence() << QgsPoint( 1, 2, 3, 4 ) << QgsPoint( 11, 12, 13, 14 ) << QgsPoint( 1, 12, 23, 24 ) << QgsPoint( 1, 2, 3, 4 ) ); + p23.clear(); + p23.setExteriorRing( lp23.clone() ); + p23.addInteriorRing( lp23.clone() ); + QCOMPARE( p23.wkbType(), QgsWkbTypes::CurvePolygonZM ); + p23.dropZValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::CurvePolygonM ); + QCOMPARE( p23.exteriorRing()->wkbType(), QgsWkbTypes::LineStringM ); + QCOMPARE( static_cast< const QgsLineString *>( p23.exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) ); + QCOMPARE( p23.interiorRing( 0 )->wkbType(), QgsWkbTypes::LineStringM ); + QCOMPARE( static_cast< const QgsLineString *>( p23.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) ); + + // dropMValue + p23.clear(); + p23.dropMValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::CurvePolygon ); + lp23.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 12 ) << QgsPoint( 1, 2 ) ); + p23.setExteriorRing( lp23.clone() ); + p23.addInteriorRing( lp23.clone() ); + QCOMPARE( p23.wkbType(), QgsWkbTypes::CurvePolygon ); + p23.dropMValue(); // not zm + QCOMPARE( p23.wkbType(), QgsWkbTypes::CurvePolygon ); + QCOMPARE( p23.exteriorRing()->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.exteriorRing() )->pointN( 0 ), QgsPoint( 1, 2 ) ); + QCOMPARE( p23.interiorRing( 0 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + // with m + lp23.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) << QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 13 ) << QgsPoint( QgsWkbTypes::PointM, 1, 12, 0, 23 ) << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) ); + p23.clear(); + p23.setExteriorRing( lp23.clone() ); + p23.addInteriorRing( lp23.clone() ); + QCOMPARE( p23.wkbType(), QgsWkbTypes::CurvePolygonM ); + p23.dropMValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::CurvePolygon ); + QCOMPARE( p23.exteriorRing()->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.exteriorRing() )->pointN( 0 ), QgsPoint( 1, 2 ) ); + QCOMPARE( p23.interiorRing( 0 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + // with zm + lp23.setPoints( QgsPointSequence() << QgsPoint( 1, 2, 3, 4 ) << QgsPoint( 11, 12, 13, 14 ) << QgsPoint( 1, 12, 23, 24 ) << QgsPoint( 1, 2, 3, 4 ) ); + p23.clear(); + p23.setExteriorRing( lp23.clone() ); + p23.addInteriorRing( lp23.clone() ); + QCOMPARE( p23.wkbType(), QgsWkbTypes::CurvePolygonZM ); + p23.dropMValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::CurvePolygonZ ); + QCOMPARE( p23.exteriorRing()->wkbType(), QgsWkbTypes::LineStringZ ); + QCOMPARE( static_cast< const QgsLineString *>( p23.exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) ); + QCOMPARE( p23.interiorRing( 0 )->wkbType(), QgsWkbTypes::LineStringZ ); + QCOMPARE( static_cast< const QgsLineString *>( p23.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) ); + + + // hasCurvedSegments + QgsCurvePolygon p24; + QVERIFY( !p24.hasCurvedSegments() ); + QgsLineString lp24; + lp24.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 12 ) << QgsPoint( 1, 2 ) ); + p24.setExteriorRing( lp23.clone() ); + QVERIFY( !p24.hasCurvedSegments() ); + QgsCircularString cs24; + cs24.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 12 ) << QgsPoint( 1, 2 ) ); + p24.addInteriorRing( cs24.clone() ); + QVERIFY( p24.hasCurvedSegments() ); + + //vertexAngle + QgsCurvePolygon p25; + ( void )p25.vertexAngle( QgsVertexId() ); //just want no crash + ( void )p25.vertexAngle( QgsVertexId( 0, 0, 0 ) ); //just want no crash + ( void )p25.vertexAngle( QgsVertexId( 0, 1, 0 ) ); //just want no crash + QgsLineString l38; + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0.5, 0 ) << QgsPoint( 1, 0 ) + << QgsPoint( 2, 1 ) << QgsPoint( 1, 2 ) << QgsPoint( 0, 2 ) << QgsPoint( 0, 0 ) ); + p25.setExteriorRing( l38.clone() ); + QGSCOMPARENEAR( p25.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 2.35619, 0.00001 ); + QGSCOMPARENEAR( p25.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( p25.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 1.17809, 0.00001 ); + QGSCOMPARENEAR( p25.vertexAngle( QgsVertexId( 0, 0, 3 ) ), 0.0, 0.00001 ); + QGSCOMPARENEAR( p25.vertexAngle( QgsVertexId( 0, 0, 4 ) ), 5.10509, 0.00001 ); + QGSCOMPARENEAR( p25.vertexAngle( QgsVertexId( 0, 0, 5 ) ), 3.92699, 0.00001 ); + QGSCOMPARENEAR( p25.vertexAngle( QgsVertexId( 0, 0, 6 ) ), 2.35619, 0.00001 ); + p25.addInteriorRing( l38.clone() ); + QGSCOMPARENEAR( p25.vertexAngle( QgsVertexId( 0, 1, 0 ) ), 2.35619, 0.00001 ); + QGSCOMPARENEAR( p25.vertexAngle( QgsVertexId( 0, 1, 1 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( p25.vertexAngle( QgsVertexId( 0, 1, 2 ) ), 1.17809, 0.00001 ); + QGSCOMPARENEAR( p25.vertexAngle( QgsVertexId( 0, 1, 3 ) ), 0.0, 0.00001 ); + QGSCOMPARENEAR( p25.vertexAngle( QgsVertexId( 0, 1, 4 ) ), 5.10509, 0.00001 ); + QGSCOMPARENEAR( p25.vertexAngle( QgsVertexId( 0, 1, 5 ) ), 3.92699, 0.00001 ); + QGSCOMPARENEAR( p25.vertexAngle( QgsVertexId( 0, 1, 6 ) ), 2.35619, 0.00001 ); + + + //insert vertex + + //insert vertex in empty polygon + p25.clear(); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 1, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 1, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( p25.isEmpty() ); + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0.5, 0 ) << QgsPoint( 1, 0 ) + << QgsPoint( 2, 1 ) << QgsPoint( 1, 2 ) << QgsPoint( 0, 2 ) << QgsPoint( 0, 0 ) ); + p25.setExteriorRing( l38.clone() ); + QVERIFY( p25.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 0.3, 0 ) ) ); + QCOMPARE( p25.nCoordinates(), 8 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 0 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 1 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 2 ), QgsPoint( 0.5, 0 ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 0, -1 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 0, 100 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 1, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + // first vertex + QVERIFY( p25.insertVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 0, 0.1 ) ) ); + QCOMPARE( p25.nCoordinates(), 9 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 0 ), QgsPoint( 0, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 1 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 2 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 3 ), QgsPoint( 0.5, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 7 ), QgsPoint( 0, 2 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 8 ), QgsPoint( 0, 0.1 ) ); + // last vertex + QVERIFY( p25.insertVertex( QgsVertexId( 0, 0, 9 ), QgsPoint( 0.1, 0.1 ) ) ); + QCOMPARE( p25.nCoordinates(), 10 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 0 ), QgsPoint( 0.1, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 1 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 2 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 3 ), QgsPoint( 0.5, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 8 ), QgsPoint( 0, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.exteriorRing() )->pointN( 9 ), QgsPoint( 0.1, 0.1 ) ); + // with interior ring + p25.addInteriorRing( l38.clone() ); + QCOMPARE( p25.nCoordinates(), 17 ); + QVERIFY( p25.insertVertex( QgsVertexId( 0, 1, 1 ), QgsPoint( 0.3, 0 ) ) ); + QCOMPARE( p25.nCoordinates(), 18 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 1 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 2 ), QgsPoint( 0.5, 0 ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 1, -1 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 1, 100 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 2, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + // first vertex in interior ring + QVERIFY( p25.insertVertex( QgsVertexId( 0, 1, 0 ), QgsPoint( 0, 0.1 ) ) ); + QCOMPARE( p25.nCoordinates(), 19 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 0, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 1 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 2 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 3 ), QgsPoint( 0.5, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 7 ), QgsPoint( 0, 2 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 8 ), QgsPoint( 0, 0.1 ) ); + // last vertex in interior ring + QVERIFY( p25.insertVertex( QgsVertexId( 0, 1, 9 ), QgsPoint( 0.1, 0.1 ) ) ); + QCOMPARE( p25.nCoordinates(), 20 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 0.1, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 1 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 2 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 3 ), QgsPoint( 0.5, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 8 ), QgsPoint( 0, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.interiorRing( 0 ) )->pointN( 9 ), QgsPoint( 0.1, 0.1 ) ); + + //move vertex + + //empty polygon + QgsCurvePolygon p26; + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( p26.isEmpty() ); + + //valid polygon + l38.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) << QgsPoint( 1, 2 ) ); + p26.setExteriorRing( l38.clone() ); + QVERIFY( p26.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( p26.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 16.0, 17.0 ) ) ); + QVERIFY( p26.moveVertex( QgsVertexId( 0, 0, 2 ), QgsPoint( 26.0, 27.0 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 0 ), QgsPoint( 6.0, 7.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 1 ), QgsPoint( 16.0, 17.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 2 ), QgsPoint( 26.0, 27.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 3 ), QgsPoint( 6.0, 7.0 ) ); + + //out of range + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 0, -1 ), QgsPoint( 3.0, 4.0 ) ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 0, 10 ), QgsPoint( 3.0, 4.0 ) ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 1, 0 ), QgsPoint( 3.0, 4.0 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 0 ), QgsPoint( 6.0, 7.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 1 ), QgsPoint( 16.0, 17.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 2 ), QgsPoint( 26.0, 27.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.exteriorRing() )->pointN( 3 ), QgsPoint( 6.0, 7.0 ) ); + + // with interior ring + p26.addInteriorRing( l38.clone() ); + QVERIFY( p26.moveVertex( QgsVertexId( 0, 1, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( p26.moveVertex( QgsVertexId( 0, 1, 1 ), QgsPoint( 16.0, 17.0 ) ) ); + QVERIFY( p26.moveVertex( QgsVertexId( 0, 1, 2 ), QgsPoint( 26.0, 27.0 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 6.0, 7.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.interiorRing( 0 ) )->pointN( 1 ), QgsPoint( 16.0, 17.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.interiorRing( 0 ) )->pointN( 2 ), QgsPoint( 26.0, 27.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.interiorRing( 0 ) )->pointN( 3 ), QgsPoint( 6.0, 7.0 ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 1, -1 ), QgsPoint( 3.0, 4.0 ) ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 1, 10 ), QgsPoint( 3.0, 4.0 ) ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 2, 0 ), QgsPoint( 3.0, 4.0 ) ) ); + + //delete vertex + + //empty polygon + QgsCurvePolygon p27; + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 1, 0 ) ) ); + QVERIFY( p27.isEmpty() ); + + //valid polygon + l38.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 5, 2 ) << QgsPoint( 6, 2 ) << QgsPoint( 7, 2 ) + << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) << QgsPoint( 1, 2 ) ); + + p27.setExteriorRing( l38.clone() ); + //out of range vertices + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 0, -1 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 0, 100 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 1, 1 ) ) ); + + //valid vertices + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 1 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 0 ), QgsPoint( 1.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 1 ), QgsPoint( 6.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 2 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 3 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 5 ), QgsPoint( 1.0, 2.0 ) ); + + // delete first vertex + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 0 ), QgsPoint( 6.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 1 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 2 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 3 ), QgsPoint( 21.0, 22.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 4 ), QgsPoint( 6.0, 2.0 ) ); + + // delete last vertex + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 4 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 0 ), QgsPoint( 21.0, 22.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 1 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 2 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.exteriorRing() )->pointN( 3 ), QgsPoint( 21.0, 22.0 ) ); + + // delete another vertex - should remove ring + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 1 ) ) ); + QVERIFY( !p27.exteriorRing() ); + + // with interior ring + p27.setExteriorRing( l38.clone() ); + p27.addInteriorRing( l38.clone() ); + + //out of range vertices + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 1, -1 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 1, 100 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 2, 1 ) ) ); + + //valid vertices + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 1, 1 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 1.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 1 ), QgsPoint( 6.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 2 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 3 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 5 ), QgsPoint( 1.0, 2.0 ) ); + + // delete first vertex + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 1, 0 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 6.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 1 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 2 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 3 ), QgsPoint( 21.0, 22.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 4 ), QgsPoint( 6.0, 2.0 ) ); + + // delete last vertex + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 1, 4 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 0 ), QgsPoint( 21.0, 22.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 1 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 2 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.interiorRing( 0 ) )->pointN( 3 ), QgsPoint( 21.0, 22.0 ) ); + + // delete another vertex - should remove ring + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 1, 1 ) ) ); + QCOMPARE( p27.numInteriorRings(), 0 ); + QVERIFY( p27.exteriorRing() ); + + // test that interior ring is "promoted" when exterior is removed + p27.addInteriorRing( l38.clone() ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( p27.numInteriorRings(), 1 ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( p27.numInteriorRings(), 1 ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( p27.numInteriorRings(), 1 ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( p27.numInteriorRings(), 0 ); + QVERIFY( p27.exteriorRing() ); +} + void TestQgsGeometry::compoundCurve() { + //test constructors + QgsCompoundCurve l1; + QVERIFY( l1.isEmpty() ); + QCOMPARE( l1.numPoints(), 0 ); + QCOMPARE( l1.vertexCount(), 0 ); + QCOMPARE( l1.nCoordinates(), 0 ); + QCOMPARE( l1.ringCount(), 0 ); + QCOMPARE( l1.partCount(), 0 ); + QVERIFY( !l1.is3D() ); + QVERIFY( !l1.isMeasure() ); + QCOMPARE( l1.wkbType(), QgsWkbTypes::CompoundCurve ); + QCOMPARE( l1.wktTypeStr(), QString( "CompoundCurve" ) ); + QCOMPARE( l1.geometryType(), QString( "CompoundCurve" ) ); + QCOMPARE( l1.dimension(), 1 ); + QVERIFY( !l1.hasCurvedSegments() ); + QCOMPARE( l1.area(), 0.0 ); + QCOMPARE( l1.perimeter(), 0.0 ); + QgsPointSequence pts; + l1.points( pts ); + QVERIFY( pts.empty() ); + + // empty, test some methods to make sure they don't crash + QCOMPARE( l1.nCurves(), 0 ); + QVERIFY( !l1.curveAt( -1 ) ); + QVERIFY( !l1.curveAt( 0 ) ); + QVERIFY( !l1.curveAt( 100 ) ); + l1.removeCurve( -1 ); + l1.removeCurve( 0 ); + l1.removeCurve( 100 ); + + //addCurve + QgsCompoundCurve c1; + //try to add null curve + c1.addCurve( nullptr ); + QCOMPARE( c1.nCurves(), 0 ); + QVERIFY( !c1.curveAt( 0 ) ); + + QgsCircularString l2; + l2.setPoints( QgsPointSequence() << QgsPoint( 1.0, 2.0 ) ); + c1.addCurve( l2.clone() ); + QVERIFY( !c1.isEmpty() ); + QCOMPARE( c1.numPoints(), 1 ); + QCOMPARE( c1.vertexCount(), 1 ); + QCOMPARE( c1.nCoordinates(), 1 ); + QCOMPARE( c1.ringCount(), 1 ); + QCOMPARE( c1.partCount(), 1 ); + QVERIFY( !c1.is3D() ); + QVERIFY( !c1.isMeasure() ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::CompoundCurve ); + QVERIFY( c1.hasCurvedSegments() ); + QCOMPARE( c1.area(), 0.0 ); + QCOMPARE( c1.perimeter(), 0.0 ); + c1.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( 1.0, 2.0 ) ); + + //adding first curve should set linestring z/m type + QgsCircularString l3; + l3.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1.0, 2.0, 3.0 ) ); + QgsCompoundCurve c2; + c2.addCurve( l3.clone() ); + QVERIFY( !c2.isEmpty() ); + QVERIFY( c2.is3D() ); + QVERIFY( !c2.isMeasure() ); + QCOMPARE( c2.wkbType(), QgsWkbTypes::CompoundCurveZ ); + QCOMPARE( c2.wktTypeStr(), QString( "CompoundCurveZ" ) ); + c2.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1.0, 2.0, 3.0 ) ); + + QgsCircularString l4; + l4.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1.0, 2.0, 0.0, 3.0 ) ); + QgsCompoundCurve c4; + c4.addCurve( l4.clone() ); + QVERIFY( !c4.isEmpty() ); + QVERIFY( !c4.is3D() ); + QVERIFY( c4.isMeasure() ); + QCOMPARE( c4.wkbType(), QgsWkbTypes::CompoundCurveM ); + QCOMPARE( c4.wktTypeStr(), QString( "CompoundCurveM" ) ); + c4.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1.0, 2.0, 0.0, 3.0 ) ); + + QgsCircularString l5; + l5.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1.0, 2.0, 3.0, 4.0 ) ); + QgsCompoundCurve c5; + c5.addCurve( l5.clone() ); + QVERIFY( !c5.isEmpty() ); + QVERIFY( c5.is3D() ); + QVERIFY( c5.isMeasure() ); + QCOMPARE( c5.wkbType(), QgsWkbTypes::CompoundCurveZM ); + QCOMPARE( c5.wktTypeStr(), QString( "CompoundCurveZM" ) ); + c5.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1.0, 2.0, 3.0, 4.0 ) ); + + //clear + c5.clear(); + QVERIFY( c5.isEmpty() ); + QCOMPARE( c5.nCurves(), 0 ); + QCOMPARE( c5.numPoints(), 0 ); + QCOMPARE( c5.vertexCount(), 0 ); + QCOMPARE( c5.nCoordinates(), 0 ); + QCOMPARE( c5.ringCount(), 0 ); + QCOMPARE( c5.partCount(), 0 ); + QVERIFY( !c5.is3D() ); + QVERIFY( !c5.isMeasure() ); + QCOMPARE( c5.wkbType(), QgsWkbTypes::CompoundCurve ); + + //addCurve + QgsCircularString l8; + QgsCompoundCurve c8; + l8.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 2, 3 ) << QgsPoint( 3, 4 ) ); + c8.addCurve( l8.clone() ); + QVERIFY( !c8.isEmpty() ); + QCOMPARE( c8.numPoints(), 3 ); + QCOMPARE( c8.vertexCount(), 3 ); + QCOMPARE( c8.nCoordinates(), 3 ); + QCOMPARE( c8.ringCount(), 1 ); + QCOMPARE( c8.partCount(), 1 ); + QCOMPARE( c8.nCurves(), 1 ); + QVERIFY( !c8.is3D() ); + QVERIFY( !c8.isMeasure() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurve ); + QVERIFY( c8.hasCurvedSegments() ); + c8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 2, 3 ) << QgsPoint( 3, 4 ) ); + QCOMPARE( *dynamic_cast< const QgsCircularString *>( c8.curveAt( 0 ) ), l8 ); + QVERIFY( ! c8.curveAt( -1 ) ); + QVERIFY( ! c8.curveAt( 1 ) ); + + QgsCircularString l8a; + l8a.setPoints( QgsPointSequence() << QgsPoint( 3, 4 ) << QgsPoint( 4, 5 ) << QgsPoint( 3, 6 ) ); + c8.addCurve( l8a.clone() ); + QCOMPARE( c8.numPoints(), 5 ); + QCOMPARE( c8.vertexCount(), 5 ); + QCOMPARE( c8.nCoordinates(), 5 ); + QCOMPARE( c8.ringCount(), 1 ); + QCOMPARE( c8.partCount(), 1 ); + QCOMPARE( c8.nCurves(), 2 ); + pts.clear(); + c8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 2, 3 ) << QgsPoint( 3, 4 ) + << QgsPoint( 4, 5 ) << QgsPoint( 3, 6 ) ); + QCOMPARE( *dynamic_cast< const QgsCircularString *>( c8.curveAt( 0 ) ), l8 ); + QCOMPARE( *dynamic_cast< const QgsCircularString *>( c8.curveAt( 1 ) ), l8a ); + QVERIFY( ! c8.curveAt( -1 ) ); + QVERIFY( ! c8.curveAt( 2 ) ); + + QgsLineString l8b; + l8b.setPoints( QgsPointSequence() << QgsPoint( 3, 6 ) << QgsPoint( 4, 6 ) ); + c8.addCurve( l8b.clone() ); + QCOMPARE( c8.numPoints(), 6 ); + QCOMPARE( c8.vertexCount(), 6 ); + QCOMPARE( c8.nCoordinates(), 6 ); + QCOMPARE( c8.ringCount(), 1 ); + QCOMPARE( c8.partCount(), 1 ); + QCOMPARE( c8.nCurves(), 3 ); + pts.clear(); + c8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 2, 3 ) << QgsPoint( 3, 4 ) + << QgsPoint( 4, 5 ) << QgsPoint( 3, 6 ) + << QgsPoint( 4, 6 ) ); + QCOMPARE( *dynamic_cast< const QgsCircularString *>( c8.curveAt( 0 ) ), l8 ); + QCOMPARE( *dynamic_cast< const QgsCircularString *>( c8.curveAt( 1 ) ), l8a ); + QCOMPARE( *dynamic_cast< const QgsLineString *>( c8.curveAt( 2 ) ), l8b ); + QVERIFY( ! c8.curveAt( -1 ) ); + QVERIFY( ! c8.curveAt( 3 ) ); + + //removeCurve + c8.removeCurve( -1 ); + c8.removeCurve( 3 ); + QCOMPARE( c8.nCurves(), 3 ); + QCOMPARE( *dynamic_cast< const QgsCircularString *>( c8.curveAt( 0 ) ), l8 ); + QCOMPARE( *dynamic_cast< const QgsCircularString *>( c8.curveAt( 1 ) ), l8a ); + QCOMPARE( *dynamic_cast< const QgsLineString *>( c8.curveAt( 2 ) ), l8b ); + c8.removeCurve( 1 ); + QCOMPARE( c8.nCurves(), 2 ); + QCOMPARE( *dynamic_cast< const QgsCircularString *>( c8.curveAt( 0 ) ), l8 ); + QCOMPARE( *dynamic_cast< const QgsLineString *>( c8.curveAt( 1 ) ), l8b ); + c8.removeCurve( 0 ); + QCOMPARE( c8.nCurves(), 1 ); + QCOMPARE( *dynamic_cast< const QgsLineString *>( c8.curveAt( 0 ) ), l8b ); + c8.removeCurve( 0 ); + QCOMPARE( c8.nCurves(), 0 ); + QVERIFY( c8.isEmpty() ); + + //addCurve with z + c8.clear(); + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 2, 3, 4 ) ); + c8.addCurve( l8.clone() ); + QCOMPARE( c8.numPoints(), 2 ); + QVERIFY( c8.is3D() ); + QVERIFY( !c8.isMeasure() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurveZ ); + pts.clear(); + c8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 2, 3, 4 ) ); + + //addCurve with m + c8.clear(); + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) << QgsPoint( QgsWkbTypes::PointM, 2, 3, 0, 4 ) ); + c8.addCurve( l8.clone() ); + QCOMPARE( c8.numPoints(), 2 ); + QVERIFY( !c8.is3D() ); + QVERIFY( c8.isMeasure() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurveM ); + c8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) << QgsPoint( QgsWkbTypes::PointM, 2, 3, 0, 4 ) ); + + //addCurve with zm + c8.clear(); + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 4, 5 ) << QgsPoint( QgsWkbTypes::PointZM, 2, 3, 4, 5 ) ); + c8.addCurve( l8.clone() ); + QCOMPARE( c8.numPoints(), 2 ); + QVERIFY( c8.is3D() ); + QVERIFY( c8.isMeasure() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurveZM ); + c8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 4, 5 ) << QgsPoint( QgsWkbTypes::PointZM, 2, 3, 4, 5 ) ); + + //addCurve with z to non z compound curve + c8.clear(); + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 1, 2 ) << QgsPoint( QgsWkbTypes::Point, 2, 3 ) ); + c8.addCurve( l8.clone() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurve ); + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 2, 3, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 3, 3, 5 ) ); + c8.addCurve( l8.clone() ); + QVERIFY( !c8.is3D() ); + QVERIFY( !c8.isMeasure() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurve ); + c8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 1, 2 ) << QgsPoint( QgsWkbTypes::Point, 2, 3 ) + << QgsPoint( QgsWkbTypes::Point, 3, 3 ) ); + c8.removeCurve( 1 ); + + //addCurve with m to non m compound curve + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 2, 3, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 3, 3, 0, 5 ) ); + c8.addCurve( l8.clone() ); + QVERIFY( !c8.is3D() ); + QVERIFY( !c8.isMeasure() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurve ); + c8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 1, 2 ) << QgsPoint( QgsWkbTypes::Point, 2, 3 ) + << QgsPoint( QgsWkbTypes::Point, 3, 3 ) ); + c8.removeCurve( 1 ); + + //addCurve with zm to non m compound curve + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 2, 3, 6, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 3, 3, 1, 5 ) ); + c8.addCurve( l8.clone() ); + QVERIFY( !c8.is3D() ); + QVERIFY( !c8.isMeasure() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurve ); + c8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 1, 2 ) << QgsPoint( QgsWkbTypes::Point, 2, 3 ) + << QgsPoint( QgsWkbTypes::Point, 3, 3 ) ); + c8.removeCurve( 1 ); + + //addCurve with no z to z compound curve + c8.clear(); + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 2, 3, 5 ) ); + c8.addCurve( l8.clone() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurveZ ); + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 2, 3 ) << QgsPoint( QgsWkbTypes::Point, 3, 4 ) ); + c8.addCurve( l8.clone() ); + QVERIFY( c8.is3D() ); + QVERIFY( !c8.isMeasure() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurveZ ); + c8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 2, 3, 5 ) + << QgsPoint( QgsWkbTypes::PointZ, 3, 4, 0 ) ); + c8.removeCurve( 1 ); + + //add curve with m, no z to z compound curve + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 2, 3, 0, 8 ) << QgsPoint( QgsWkbTypes::PointM, 3, 4, 0, 9 ) ); + c8.addCurve( l8.clone() ); + QVERIFY( c8.is3D() ); + QVERIFY( !c8.isMeasure() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurveZ ); + c8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 2, 3, 5 ) + << QgsPoint( QgsWkbTypes::PointZ, 3, 4, 0 ) ); + c8.removeCurve( 1 ); + + //add curve with zm to z compound curve + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 2, 3, 6, 8 ) << QgsPoint( QgsWkbTypes::PointZM, 3, 4, 7, 9 ) ); + c8.addCurve( l8.clone() ); + QVERIFY( c8.is3D() ); + QVERIFY( !c8.isMeasure() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurveZ ); + c8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 2, 3, 5 ) + << QgsPoint( QgsWkbTypes::PointZ, 3, 4, 7 ) ); + c8.removeCurve( 1 ); + + //addCurve with no m to m compound curve + c8.clear(); + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 2, 3, 0, 5 ) ); + c8.addCurve( l8.clone() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurveM ); + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 2, 3 ) << QgsPoint( QgsWkbTypes::Point, 3, 4 ) ); + c8.addCurve( l8.clone() ); + QVERIFY( !c8.is3D() ); + QVERIFY( c8.isMeasure() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurveM ); + c8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 2, 3, 0, 5 ) + << QgsPoint( QgsWkbTypes::PointM, 3, 4, 0, 0 ) ); + c8.removeCurve( 1 ); + + //add curve with z, no m to m compound curve + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 2, 3, 8 ) << QgsPoint( QgsWkbTypes::PointZ, 3, 4, 9 ) ); + c8.addCurve( l8.clone() ); + QVERIFY( !c8.is3D() ); + QVERIFY( c8.isMeasure() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurveM ); + c8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 2, 3, 0, 5 ) + << QgsPoint( QgsWkbTypes::PointM, 3, 4, 0, 0 ) ); + c8.removeCurve( 1 ); + + //add curve with zm to m compound curve + l8.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 2, 3, 6, 8 ) << QgsPoint( QgsWkbTypes::PointZM, 3, 4, 7, 9 ) ); + c8.addCurve( l8.clone() ); + QVERIFY( !c8.is3D() ); + QVERIFY( c8.isMeasure() ); + QCOMPARE( c8.wkbType(), QgsWkbTypes::CompoundCurveM ); + c8.points( pts ); + QCOMPARE( pts, QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 2, 3, 0, 5 ) + << QgsPoint( QgsWkbTypes::PointM, 3, 4, 0, 9 ) ); + c8.removeCurve( 1 ); + + //test getters/setters + QgsCompoundCurve c9; + + // no crash! + ( void )c9.xAt( -1 ); + ( void )c9.xAt( 1 ); + ( void )c9.yAt( -1 ); + ( void )c9.yAt( 1 ); + + QgsCircularString l9; + l9.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) + << QgsPoint( QgsWkbTypes::PointZM, 21, 22, 23, 24 ) ); + c9.addCurve( l9.clone() ); + QCOMPARE( c9.xAt( 0 ), 1.0 ); + QCOMPARE( c9.xAt( 1 ), 11.0 ); + QCOMPARE( c9.xAt( 2 ), 21.0 ); + ( void ) c9.xAt( -1 ); //out of range + ( void ) c9.xAt( 11 ); //out of range + QCOMPARE( c9.yAt( 0 ), 2.0 ); + QCOMPARE( c9.yAt( 1 ), 12.0 ); + QCOMPARE( c9.yAt( 2 ), 22.0 ); + ( void ) c9.yAt( -1 ); //out of range + ( void ) c9.yAt( 11 ); //out of range + + QgsLineString l9a; + l9a.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 21, 22, 23, 24 ) + << QgsPoint( QgsWkbTypes::PointZM, 31, 22, 13, 14 ) ); + c9.addCurve( l9a.clone() ); + QCOMPARE( c9.xAt( 0 ), 1.0 ); + QCOMPARE( c9.xAt( 1 ), 11.0 ); + QCOMPARE( c9.xAt( 2 ), 21.0 ); + QCOMPARE( c9.xAt( 3 ), 31.0 ); + QCOMPARE( c9.xAt( 4 ), 0.0 ); + ( void ) c9.xAt( -1 ); //out of range + ( void ) c9.xAt( 11 ); //out of range + QCOMPARE( c9.yAt( 0 ), 2.0 ); + QCOMPARE( c9.yAt( 1 ), 12.0 ); + QCOMPARE( c9.yAt( 2 ), 22.0 ); + QCOMPARE( c9.yAt( 3 ), 22.0 ); + QCOMPARE( c9.yAt( 4 ), 0.0 ); + ( void ) c9.yAt( -1 ); //out of range + ( void ) c9.yAt( 11 ); //out of range + + c9.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 51.0, 52.0 ) ); + QCOMPARE( c9.xAt( 0 ), 51.0 ); + QCOMPARE( c9.yAt( 0 ), 52.0 ); + c9.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 61.0, 62 ) ); + QCOMPARE( c9.xAt( 1 ), 61.0 ); + QCOMPARE( c9.yAt( 1 ), 62.0 ); + c9.moveVertex( QgsVertexId( 0, 0, -1 ), QgsPoint( 71.0, 2 ) ); //out of range + c9.moveVertex( QgsVertexId( 0, 0, 11 ), QgsPoint( 71.0, 2 ) ); //out of range + + QgsPoint p; + QgsVertexId::VertexType type; + QVERIFY( !c9.pointAt( -1, p, type ) ); + QVERIFY( !c9.pointAt( 11, p, type ) ); + QVERIFY( c9.pointAt( 0, p, type ) ); + QCOMPARE( p.z(), 3.0 ); + QCOMPARE( p.m(), 4.0 ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + QVERIFY( c9.pointAt( 1, p, type ) ); + QCOMPARE( p.z(), 13.0 ); + QCOMPARE( p.m(), 14.0 ); + QCOMPARE( type, QgsVertexId::CurveVertex ); + QVERIFY( c9.pointAt( 2, p, type ) ); + QCOMPARE( p.z(), 23.0 ); + QCOMPARE( p.m(), 24.0 ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + QVERIFY( c9.pointAt( 3, p, type ) ); + QCOMPARE( p.z(), 13.0 ); + QCOMPARE( p.m(), 14.0 ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + + //equality + QgsCompoundCurve e1; + QgsCompoundCurve e2; + QVERIFY( e1 == e2 ); + QVERIFY( !( e1 != e2 ) ); + QgsLineString le1; + QgsLineString le2; + le1.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) ); + e1.addCurve( le1.clone() ); + QVERIFY( !( e1 == e2 ) ); //different number of curves + QVERIFY( e1 != e2 ); + e2.addCurve( le1.clone() ); + QVERIFY( e1 == e2 ); + QVERIFY( !( e1 != e2 ) ); + le1.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 1 / 3.0, 4 / 3.0 ) ); + e1.addCurve( le1.clone() ); + QVERIFY( !( e1 == e2 ) ); //different number of curves + QVERIFY( e1 != e2 ); + le2.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 2 / 6.0, 8 / 6.0 ) ); + e2.addCurve( le2.clone() ); + QVERIFY( e1 == e2 ); //check non-integer equality + QVERIFY( !( e1 != e2 ) ); + le1.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 1 / 3.0, 4 / 3.0 ) << QgsPoint( 7, 8 ) ); + e1.addCurve( le1.clone() ); + le2.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 2 / 6.0, 8 / 6.0 ) << QgsPoint( 6, 9 ) ); + e2.addCurve( le2.clone() ); + QVERIFY( !( e1 == e2 ) ); //different coordinates + QVERIFY( e1 != e2 ); + + // different dimensions + QgsCompoundCurve e3; + e1.clear(); + e1.addCurve( le1.clone() ); + QgsLineString le3; + le3.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 0 ) + << QgsPoint( QgsWkbTypes::PointZ, 1 / 3.0, 4 / 3.0, 0 ) + << QgsPoint( QgsWkbTypes::PointZ, 7, 8, 0 ) ); + e3.addCurve( le3.clone() ); + QVERIFY( !( e1 == e3 ) ); //different dimension + QVERIFY( e1 != e3 ); + QgsCompoundCurve e4; + QgsLineString le4; + le4.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 2 ) + << QgsPoint( QgsWkbTypes::PointZ, 1 / 3.0, 4 / 3.0, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 7, 8, 4 ) ); + e4.addCurve( le4.clone() ); + QVERIFY( !( e3 == e4 ) ); //different z coordinates + QVERIFY( e3 != e4 ); + QgsCompoundCurve e5; + QgsLineString le5; + le5.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 1 ) + << QgsPoint( QgsWkbTypes::PointM, 1 / 3.0, 4 / 3.0, 0, 2 ) + << QgsPoint( QgsWkbTypes::PointM, 7, 8, 0, 3 ) ); + e5.addCurve( le5.clone() ); + QgsCompoundCurve e6; + QgsLineString le6; + le6.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 11 ) + << QgsPoint( QgsWkbTypes::PointM, 1 / 3.0, 4 / 3.0, 0, 12 ) + << QgsPoint( QgsWkbTypes::PointM, 7, 8, 0, 13 ) ); + e6.addCurve( le6.clone() ); + QVERIFY( !( e5 == e6 ) ); //different m values + QVERIFY( e5 != e6 ); + + QVERIFY( e6 != QgsLineString() ); + + // assignment operator + e5.addCurve( le5.clone() ); + QVERIFY( e5 != e6 ); + e6 = e5; + QCOMPARE( e5, e6 ); + + //isClosed + QgsCompoundCurve c11; + QgsCircularString l11; + QVERIFY( !c11.isClosed() ); + l11.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 2 ) + << QgsPoint( 11, 22 ) + << QgsPoint( 1, 22 ) ); + c11.addCurve( l11.clone() ); + QVERIFY( !c11.isClosed() ); + QgsLineString ls11; + ls11.setPoints( QgsPointSequence() << QgsPoint( 1, 22 ) + << QgsPoint( 1, 2 ) ); + c11.addCurve( ls11.clone() ); + QVERIFY( c11.isClosed() ); + + //test that m values aren't considered when testing for closedness + c11.clear(); + l11.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) + << QgsPoint( QgsWkbTypes::PointM, 11, 2, 0, 4 ) + << QgsPoint( QgsWkbTypes::PointM, 11, 22, 0, 5 ) + << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 6 ) ); + c11.addCurve( l11.clone() ); + QVERIFY( c11.isClosed() ); + + //polygonf + QgsCircularString lc13; + QgsCompoundCurve c13; + lc13.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 2, 11, 14 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 22, 21, 24 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 22, 31, 34 ) ); + c13.addCurve( lc13.clone() ); + QgsLineString ls13; + ls13.setPoints( QgsPointSequence() << QgsPoint( 1, 22 ) << QgsPoint( 23, 22 ) ); + c13.addCurve( ls13.clone() ); + QPolygonF poly = c13.asQPolygonF(); + QCOMPARE( poly.count(), 5 ); + QCOMPARE( poly.at( 0 ).x(), 1.0 ); + QCOMPARE( poly.at( 0 ).y(), 2.0 ); + QCOMPARE( poly.at( 1 ).x(), 11.0 ); + QCOMPARE( poly.at( 1 ).y(), 2.0 ); + QCOMPARE( poly.at( 2 ).x(), 11.0 ); + QCOMPARE( poly.at( 2 ).y(), 22.0 ); + QCOMPARE( poly.at( 3 ).x(), 1.0 ); + QCOMPARE( poly.at( 3 ).y(), 22.0 ); + QCOMPARE( poly.at( 4 ).x(), 23.0 ); + QCOMPARE( poly.at( 4 ).y(), 22.0 ); + + // clone tests + QgsCircularString lc14; + lc14.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 2 ) + << QgsPoint( 11, 22 ) + << QgsPoint( 1, 22 ) ); + QgsCompoundCurve c14; + c14.addCurve( lc14.clone() ); + QgsLineString ls14; + ls14.setPoints( QgsPointSequence() << QgsPoint( 1, 22 ) << QgsPoint( 23, 22 ) ); + c14.addCurve( ls14.clone() ); + std::unique_ptr cloned( c14.clone() ); + QCOMPARE( *cloned, c14 ); + + //clone with Z/M + lc14.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 2, 11, 14 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 22, 21, 24 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 22, 31, 34 ) ); + ls14.setPoints( QgsPointSequence() << QgsPoint( 1, 22, 31, 34 ) << QgsPoint( 23, 22, 42, 43 ) ); + c14.clear(); + c14.addCurve( lc14.clone() ); + c14.addCurve( ls14.clone() ); + cloned.reset( c14.clone() ); + QCOMPARE( *cloned, c14 ); + + //clone an empty line + c14.clear(); + cloned.reset( c14.clone() ); + QVERIFY( cloned->isEmpty() ); + QCOMPARE( cloned->numPoints(), 0 ); + QVERIFY( !cloned->is3D() ); + QVERIFY( !cloned->isMeasure() ); + QCOMPARE( cloned->wkbType(), QgsWkbTypes::CompoundCurve ); + + //segmentize tests + QgsCompoundCurve toSegment; + QgsCircularString lcSegment; + lcSegment.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 10 ) << QgsPoint( 21, 2 ) ); + toSegment.addCurve( lcSegment.clone() ); + std::unique_ptr segmentized( static_cast< QgsLineString * >( toSegment.segmentize() ) ); + QCOMPARE( segmentized->numPoints(), 156 ); + QCOMPARE( segmentized->vertexCount(), 156 ); + QCOMPARE( segmentized->ringCount(), 1 ); + QCOMPARE( segmentized->partCount(), 1 ); + QCOMPARE( segmentized->wkbType(), QgsWkbTypes::LineString ); + QVERIFY( !segmentized->is3D() ); + QVERIFY( !segmentized->isMeasure() ); + + QCOMPARE( segmentized->pointN( 0 ), lcSegment.pointN( 0 ) ); + QCOMPARE( segmentized->pointN( segmentized->numPoints() - 1 ), lcSegment.pointN( toSegment.numPoints() - 1 ) ); + + //segmentize with Z/M + lcSegment.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 10, 11, 14 ) + << QgsPoint( QgsWkbTypes::PointZM, 21, 2, 21, 24 ) ); + toSegment.clear(); + toSegment.addCurve( lcSegment.clone() ); + segmentized.reset( static_cast< QgsLineString * >( toSegment.segmentize() ) ); + QCOMPARE( segmentized->numPoints(), 156 ); + QCOMPARE( segmentized->vertexCount(), 156 ); + QCOMPARE( segmentized->ringCount(), 1 ); + QCOMPARE( segmentized->partCount(), 1 ); + QCOMPARE( segmentized->wkbType(), QgsWkbTypes::LineStringZM ); + QVERIFY( segmentized->is3D() ); + QVERIFY( segmentized->isMeasure() ); + QCOMPARE( segmentized->pointN( 0 ), lcSegment.pointN( 0 ) ); + QCOMPARE( segmentized->pointN( segmentized->numPoints() - 1 ), lcSegment.pointN( toSegment.numPoints() - 1 ) ); + + //segmentize an empty line + toSegment.clear(); + segmentized.reset( static_cast< QgsLineString * >( toSegment.segmentize() ) ); + QVERIFY( segmentized->isEmpty() ); + QCOMPARE( segmentized->numPoints(), 0 ); + QVERIFY( !segmentized->is3D() ); + QVERIFY( !segmentized->isMeasure() ); + QCOMPARE( segmentized->wkbType(), QgsWkbTypes::LineString ); + + //to/from WKB + QgsCompoundCurve c15; + QgsCircularString l15; + l15.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 2, 11, 14 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 22, 21, 24 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 22, 31, 34 ) ); + c15.addCurve( l15.clone() ); + QByteArray wkb15 = c15.asWkb(); + QgsCompoundCurve c16; + QgsConstWkbPtr wkb15ptr( wkb15 ); + c16.fromWkb( wkb15ptr ); + QCOMPARE( c16.numPoints(), 4 ); + QCOMPARE( c16.vertexCount(), 4 ); + QCOMPARE( c16.nCoordinates(), 4 ); + QCOMPARE( c16.ringCount(), 1 ); + QCOMPARE( c16.partCount(), 1 ); + QCOMPARE( c16.wkbType(), QgsWkbTypes::CompoundCurveZM ); + QVERIFY( c16.is3D() ); + QVERIFY( c16.isMeasure() ); + QCOMPARE( c16.nCurves(), 1 ); + QCOMPARE( dynamic_cast< const QgsCircularString *>( c16.curveAt( 0 ) )->pointN( 0 ), l15.pointN( 0 ) ); + QCOMPARE( dynamic_cast< const QgsCircularString *>( c16.curveAt( 0 ) )->pointN( 1 ), l15.pointN( 1 ) ); + QCOMPARE( dynamic_cast< const QgsCircularString *>( c16.curveAt( 0 ) )->pointN( 2 ), l15.pointN( 2 ) ); + QCOMPARE( dynamic_cast< const QgsCircularString *>( c16.curveAt( 0 ) )->pointN( 3 ), l15.pointN( 3 ) ); + + //bad WKB - check for no crash + c16.clear(); + QgsConstWkbPtr nullPtr( nullptr, 0 ); + QVERIFY( !c16.fromWkb( nullPtr ) ); + QCOMPARE( c16.wkbType(), QgsWkbTypes::CompoundCurve ); + QgsPoint point( 1, 2 ); + QByteArray wkb16 = point.asWkb(); + QgsConstWkbPtr wkb16ptr( wkb16 ); + QVERIFY( !c16.fromWkb( wkb16ptr ) ); + QCOMPARE( c16.wkbType(), QgsWkbTypes::CompoundCurve ); + + //to/from WKT + QgsCompoundCurve c17; + QgsCircularString l17; + l17.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 2, 11, 14 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 22, 21, 24 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 22, 31, 34 ) ); + c17.addCurve( l17.clone() ); + + QString wkt = c17.asWkt(); + QVERIFY( !wkt.isEmpty() ); + QgsCompoundCurve c18; + QVERIFY( c18.fromWkt( wkt ) ); + QCOMPARE( c18.numPoints(), 4 ); + QCOMPARE( c18.wkbType(), QgsWkbTypes::CompoundCurveZM ); + QVERIFY( c18.is3D() ); + QVERIFY( c18.isMeasure() ); + QCOMPARE( dynamic_cast< const QgsCircularString *>( c18.curveAt( 0 ) )->pointN( 0 ), l17.pointN( 0 ) ); + QCOMPARE( dynamic_cast< const QgsCircularString *>( c18.curveAt( 0 ) )->pointN( 1 ), l17.pointN( 1 ) ); + QCOMPARE( dynamic_cast< const QgsCircularString *>( c18.curveAt( 0 ) )->pointN( 2 ), l17.pointN( 2 ) ); + QCOMPARE( dynamic_cast< const QgsCircularString *>( c18.curveAt( 0 ) )->pointN( 3 ), l17.pointN( 3 ) ); + + //bad WKT + QVERIFY( !c18.fromWkt( "Polygon()" ) ); + QVERIFY( c18.isEmpty() ); + QCOMPARE( c18.numPoints(), 0 ); + QVERIFY( !c18.is3D() ); + QVERIFY( !c18.isMeasure() ); + QCOMPARE( c18.wkbType(), QgsWkbTypes::CompoundCurve ); + QVERIFY( !c18.fromWkt( "CompoundCurve(LineString(0 0, 1 1),Point( 2 2 ))" ) ); + + //asGML2 + QgsCompoundCurve exportCurve; + QgsCircularString exportLine; + exportLine.setPoints( QgsPointSequence() << QgsPoint( 31, 32 ) + << QgsPoint( 41, 42 ) + << QgsPoint( 51, 52 ) ); + exportCurve.addCurve( exportLine.clone() ); + QgsLineString exportLineString; + exportLineString.setPoints( QgsPointSequence() << QgsPoint( 51, 52 ) + << QgsPoint( 61, 62 ) ); + exportCurve.addCurve( exportLineString.clone() ); + + QgsCircularString exportLineFloat; + exportLineFloat.setPoints( QgsPointSequence() << QgsPoint( 1 / 3.0, 2 / 3.0 ) + << QgsPoint( 1 + 1 / 3.0, 1 + 2 / 3.0 ) + << QgsPoint( 2 + 1 / 3.0, 2 + 2 / 3.0 ) ); + QgsCompoundCurve exportCurveFloat; + exportCurveFloat.addCurve( exportLineFloat.clone() ); + QgsLineString exportLineStringFloat; + exportLineStringFloat.setPoints( QgsPointSequence() << QgsPoint( 2 + 1 / 3.0, 2 + 2 / 3.0 ) + << QgsPoint( 3 + 1 / 3.0, 3 + 2 / 3.0 ) ); + exportCurveFloat.addCurve( exportLineStringFloat.clone() ); + + QDomDocument doc( QStringLiteral( "gml" ) ); + QString expectedGML2( QStringLiteral( "31,32 41,42 51,52 61,62" ) ); + QString result = elemToString( exportCurve.asGML2( doc ) ); + QGSCOMPAREGML( result, expectedGML2 ); + QString expectedGML2prec3( QStringLiteral( "0.333,0.667 1.333,1.667 2.333,2.667 3.333,3.667" ) ); + result = elemToString( exportCurveFloat.asGML2( doc, 3 ) ); + QGSCOMPAREGML( result, expectedGML2prec3 ); + + + //asGML3 + QString expectedGML3( QStringLiteral( "31 32 41 42 51 5251 52 61 62" ) ); + result = elemToString( exportCurve.asGML3( doc ) ); + QCOMPARE( result, expectedGML3 ); + QString expectedGML3prec3( QStringLiteral( "0.333 0.667 1.333 1.667 2.333 2.6672.333 2.667 3.333 3.667" ) ); + result = elemToString( exportCurveFloat.asGML3( doc, 3 ) ); + QCOMPARE( result, expectedGML3prec3 ); + + //asJSON + QString expectedJson( QStringLiteral( "{\"type\": \"LineString\", \"coordinates\": [ [31, 32], [41, 42], [51, 52], [61, 62]]}" ) ); + result = exportCurve.asJSON(); + QCOMPARE( result, expectedJson ); + QString expectedJsonPrec3( QStringLiteral( "{\"type\": \"LineString\", \"coordinates\": [ [0.333, 0.667], [1.333, 1.667], [2.333, 2.667], [3.333, 3.667]]}" ) ); + result = exportCurveFloat.asJSON( 3 ); + QCOMPARE( result, expectedJsonPrec3 ); + + //length + QgsCompoundCurve c19; + QCOMPARE( c19.length(), 0.0 ); + QgsCircularString l19; + l19.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 10, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) ); + c19.addCurve( l19.clone() ); + QgsLineString l19a; + l19a.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) + << QgsPoint( QgsWkbTypes::PointZM, 25, 10, 6, 7 ) ); + c19.addCurve( l19a.clone() ); + QGSCOMPARENEAR( c19.length(), 36.1433, 0.001 ); + + //startPoint + QCOMPARE( c19.startPoint(), QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 ) ); + + //endPoint + QCOMPARE( c19.endPoint(), QgsPoint( QgsWkbTypes::PointZM, 25, 10, 6, 7 ) ); + + //bad start/end points. Test that this doesn't crash. + c19.clear(); + QCOMPARE( c19.startPoint(), QgsPoint() ); + QCOMPARE( c19.endPoint(), QgsPoint() ); + + //curveToLine + c19.clear(); + l19.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 10, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) ); + c19.addCurve( l19.clone() ); + l19a.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) + << QgsPoint( QgsWkbTypes::PointZM, 25, 10, 6, 7 ) ); + c19.addCurve( l19a.clone() ); + segmentized.reset( c19.curveToLine() ); + QCOMPARE( segmentized->numPoints(), 182 ); + QCOMPARE( segmentized->wkbType(), QgsWkbTypes::LineStringZM ); + QVERIFY( segmentized->is3D() ); + QVERIFY( segmentized->isMeasure() ); + QCOMPARE( segmentized->pointN( 0 ), l19.pointN( 0 ) ); + QCOMPARE( segmentized->pointN( segmentized->numPoints() - 1 ), l19a.pointN( l19a.numPoints() - 1 ) ); + + // points + QgsCompoundCurve c20; + QgsCircularString l20; + QgsPointSequence points; + c20.points( points ); + QVERIFY( points.isEmpty() ); + l20.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 10, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) ); + c20.addCurve( l20.clone() ); + QgsLineString ls20; + ls20.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) + << QgsPoint( QgsWkbTypes::PointZM, 25, 10, 6, 7 ) ); + c20.addCurve( ls20.clone() ); + c20.points( points ); + QCOMPARE( points.count(), 4 ); + QCOMPARE( points.at( 0 ), QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 ) ); + QCOMPARE( points.at( 1 ), QgsPoint( QgsWkbTypes::PointZM, 1, 10, 4, 5 ) ); + QCOMPARE( points.at( 2 ), QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) ); + QCOMPARE( points.at( 3 ), QgsPoint( QgsWkbTypes::PointZM, 25, 10, 6, 7 ) ); + + //CRS transform + QgsCoordinateReferenceSystem sourceSrs; + sourceSrs.createFromSrid( 3994 ); + QgsCoordinateReferenceSystem destSrs; + destSrs.createFromSrid( 4202 ); // want a transform with ellipsoid change + QgsCoordinateTransform tr( sourceSrs, destSrs ); + + // 2d CRS transform + QgsCompoundCurve c21; + QgsCircularString l21; + l21.setPoints( QgsPointSequence() << QgsPoint( 6374985, -3626584 ) + << QgsPoint( 6474985, -3526584 ) ); + c21.addCurve( l21.clone() ); + QgsLineString ls21; + ls21.setPoints( QgsPointSequence() << QgsPoint( 6474985, -3526584 ) + << QgsPoint( 6504985, -3526584 ) ); + c21.addCurve( ls21.clone() ); + c21.transform( tr, QgsCoordinateTransform::ForwardTransform ); + QGSCOMPARENEAR( c21.xAt( 0 ), 175.771, 0.001 ); + QGSCOMPARENEAR( c21.yAt( 0 ), -39.724, 0.001 ); + QGSCOMPARENEAR( c21.xAt( 1 ), 176.959, 0.001 ); + QGSCOMPARENEAR( c21.yAt( 1 ), -38.7999, 0.001 ); + QGSCOMPARENEAR( c21.xAt( 2 ), 177.315211, 0.001 ); + QGSCOMPARENEAR( c21.yAt( 2 ), -38.799974, 0.001 ); + QGSCOMPARENEAR( c21.boundingBox().xMinimum(), 175.770033, 0.001 ); + QGSCOMPARENEAR( c21.boundingBox().yMinimum(), -39.724, 0.001 ); + QGSCOMPARENEAR( c21.boundingBox().xMaximum(), 177.315211, 0.001 ); + QGSCOMPARENEAR( c21.boundingBox().yMaximum(), -38.7999, 0.001 ); + + //3d CRS transform + QgsCompoundCurve c22; + l21.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 6374985, -3626584, 1, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 6474985, -3526584, 3, 4 ) ); + c22.addCurve( l21.clone() ); + ls21.setPoints( QgsPointSequence() << QgsPoint( 6474985, -3526584, 3, 4 ) + << QgsPoint( 6504985, -3526584, 5, 6 ) ); + c22.addCurve( ls21.clone() ); + c22.transform( tr, QgsCoordinateTransform::ForwardTransform ); + QgsPoint pt; + QgsVertexId::VertexType v; + c22.pointAt( 0, pt, v ); + QGSCOMPARENEAR( pt.x(), 175.771, 0.001 ); + QGSCOMPARENEAR( pt.y(), -39.724, 0.001 ); + QGSCOMPARENEAR( pt.z(), 1.0, 0.001 ); + QCOMPARE( pt.m(), 2.0 ); + c22.pointAt( 1, pt, v ); + QGSCOMPARENEAR( pt.x(), 176.959, 0.001 ); + QGSCOMPARENEAR( pt.y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( pt.z(), 3.0, 0.001 ); + QCOMPARE( pt.m(), 4.0 ); + c22.pointAt( 2, pt, v ); + QGSCOMPARENEAR( pt.x(), 177.315211, 0.001 ); + QGSCOMPARENEAR( pt.y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( pt.z(), 5.0, 0.001 ); + QCOMPARE( pt.m(), 6.0 ); + + //reverse transform + c22.transform( tr, QgsCoordinateTransform::ReverseTransform ); + c22.pointAt( 0, pt, v ); + QGSCOMPARENEAR( pt.x(), 6374985, 100 ); + QGSCOMPARENEAR( pt.y(), -3626584, 100 ); + QGSCOMPARENEAR( pt.z(), 1.0, 0.001 ); + QCOMPARE( pt.m(), 2.0 ); + c22.pointAt( 1, pt, v ); + QGSCOMPARENEAR( pt.x(), 6474985, 100 ); + QGSCOMPARENEAR( pt.y(), -3526584, 100 ); + QGSCOMPARENEAR( pt.z(), 3.0, 0.001 ); + QCOMPARE( pt.m(), 4.0 ); + c22.pointAt( 2, pt, v ); + QGSCOMPARENEAR( pt.x(), 6504985, 100 ); + QGSCOMPARENEAR( pt.y(), -3526584, 100 ); + QGSCOMPARENEAR( pt.z(), 5.0, 0.001 ); + QCOMPARE( pt.m(), 6.0 ); + + //z value transform + c22.transform( tr, QgsCoordinateTransform::ForwardTransform, true ); + c22.pointAt( 0, pt, v ); + QGSCOMPARENEAR( pt.z(), -19.249066, 0.001 ); + c22.pointAt( 1, pt, v ); + QGSCOMPARENEAR( pt.z(), -21.092128, 0.001 ); + c22.pointAt( 2, pt, v ); + QGSCOMPARENEAR( pt.z(), -19.370485, 0.001 ); + + c22.transform( tr, QgsCoordinateTransform::ReverseTransform, true ); + c22.pointAt( 0, pt, v ); + QGSCOMPARENEAR( pt.z(), 1, 0.001 ); + c22.pointAt( 1, pt, v ); + QGSCOMPARENEAR( pt.z(), 3, 0.001 ); + c22.pointAt( 2, pt, v ); + QGSCOMPARENEAR( pt.z(), 5, 0.001 ); + + //QTransform transform + QTransform qtr = QTransform::fromScale( 2, 3 ); + QgsCompoundCurve c23; + QgsCircularString l23; + l23.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) ); + c23.addCurve( l23.clone() ); + QgsLineString ls23; + ls23.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) << QgsPoint( QgsWkbTypes::PointZM, 21, 13, 13, 14 ) ); + c23.addCurve( ls23.clone() ); + c23.transform( qtr ); + c23.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 2, 6, 3, 4 ) ); + c23.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 22, 36, 13, 14 ) ); + c23.pointAt( 2, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 42, 39, 13, 14 ) ); + QCOMPARE( c23.boundingBox(), QgsRectangle( 2, 6, 42, 39 ) ); + + //insert vertex + //cannot insert vertex in empty line + QgsCompoundCurve c24; + QVERIFY( !c24.insertVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QCOMPARE( c24.numPoints(), 0 ); + + //2d line + QgsCircularString l24; + l24.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 12 ) << QgsPoint( 1, 22 ) ); + c24.addCurve( l24.clone() ); + QVERIFY( c24.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 4.0, 7.0 ) ) ); + QCOMPARE( c24.numPoints(), 5 ); + QVERIFY( !c24.is3D() ); + QVERIFY( !c24.isMeasure() ); + QCOMPARE( c24.wkbType(), QgsWkbTypes::CompoundCurve ); + c24.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( 1.0, 2.0 ) ); + c24.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( 4.0, 7.0 ) ); + c24.pointAt( 2, pt, v ); + QGSCOMPARENEAR( pt.x(), 7.192236, 0.01 ); + QGSCOMPARENEAR( pt.y(), 9.930870, 0.01 ); + c24.pointAt( 3, pt, v ); + QCOMPARE( pt, QgsPoint( 11.0, 12.0 ) ); + c24.pointAt( 4, pt, v ); + QCOMPARE( pt, QgsPoint( 1.0, 22.0 ) ); + + QVERIFY( c24.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 8.0, 9.0 ) ) ); + QVERIFY( c24.insertVertex( QgsVertexId( 0, 0, 2 ), QgsPoint( 18.0, 16.0 ) ) ); + QCOMPARE( c24.numPoints(), 9 ); + c24.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( 1.0, 2.0 ) ); + c24.pointAt( 1, pt, v ); + QGSCOMPARENEAR( pt.x(), 4.363083, 0.01 ); + QGSCOMPARENEAR( pt.y(), 5.636917, 0.01 ); + c24.pointAt( 2, pt, v ); + QCOMPARE( pt, QgsPoint( 8.0, 9.0 ) ); + c24.pointAt( 3, pt, v ); + QCOMPARE( pt, QgsPoint( 18.0, 16.0 ) ); + c24.pointAt( 4, pt, v ); + QGSCOMPARENEAR( pt.x(), 5.876894, 0.01 ); + QGSCOMPARENEAR( pt.y(), 8.246211, 0.01 ); + c24.pointAt( 5, pt, v ); + QCOMPARE( pt, QgsPoint( 4.0, 7.0 ) ); + c24.pointAt( 6, pt, v ); + QGSCOMPARENEAR( pt.x(), 7.192236, 0.01 ); + QGSCOMPARENEAR( pt.y(), 9.930870, 0.01 ); + c24.pointAt( 7, pt, v ); + QCOMPARE( pt, QgsPoint( 11.0, 12.0 ) ); + c24.pointAt( 8, pt, v ); + QCOMPARE( pt, QgsPoint( 1.0, 22.0 ) ); + + //insert vertex at end + QVERIFY( !c24.insertVertex( QgsVertexId( 0, 0, 9 ), QgsPoint( 31.0, 32.0 ) ) ); + + //insert vertex past end + QVERIFY( !c24.insertVertex( QgsVertexId( 0, 0, 10 ), QgsPoint( 41.0, 42.0 ) ) ); + QCOMPARE( c24.numPoints(), 9 ); + + //insert vertex before start + QVERIFY( !c24.insertVertex( QgsVertexId( 0, 0, -18 ), QgsPoint( 41.0, 42.0 ) ) ); + QCOMPARE( c24.numPoints(), 9 ); + + //insert 4d vertex in 4d line + c24.clear(); + l24.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 10, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) ); + c24.addCurve( l24.clone( ) ); + QVERIFY( c24.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) ) ); + QCOMPARE( c24.numPoints(), 5 ); + c24.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) ); + + //insert 2d vertex in 4d line + QVERIFY( c24.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 101, 102 ) ) ); + QCOMPARE( c24.numPoints(), 7 ); + QCOMPARE( c24.wkbType(), QgsWkbTypes::CompoundCurveZM ); + c24.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 101, 102 ) ); + + //insert 4d vertex in 2d line + c24.clear(); + l24.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 12 ) << QgsPoint( 1, 22 ) ); + c24.addCurve( l24.clone() ); + QVERIFY( c24.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( QgsWkbTypes::PointZM, 2, 4, 103, 104 ) ) ); + QCOMPARE( c24.numPoints(), 5 ); + QCOMPARE( c24.wkbType(), QgsWkbTypes::CompoundCurve ); + c24.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::Point, 2, 4 ) ); + + // invalid + QVERIFY( !c24.insertVertex( QgsVertexId( 0, 1, 0 ), QgsPoint( 1, 2 ) ) ); + + //move vertex + + //empty line + QgsCompoundCurve c25; + QgsCircularString l25; + QVERIFY( !c25.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( c25.isEmpty() ); + + //valid line + l25.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) ); + c25.addCurve( l25.clone() ); + QVERIFY( c25.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( c25.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 16.0, 17.0 ) ) ); + QVERIFY( c25.moveVertex( QgsVertexId( 0, 0, 2 ), QgsPoint( 26.0, 27.0 ) ) ); + c25.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( 6.0, 7.0 ) ); + c25.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( 16.0, 17.0 ) ); + c25.pointAt( 2, pt, v ); + QCOMPARE( pt, QgsPoint( 26.0, 27.0 ) ); + + //out of range + QVERIFY( !c25.moveVertex( QgsVertexId( 0, 0, -1 ), QgsPoint( 3.0, 4.0 ) ) ); + QVERIFY( !c25.moveVertex( QgsVertexId( 0, 0, 10 ), QgsPoint( 3.0, 4.0 ) ) ); + QVERIFY( !c25.moveVertex( QgsVertexId( 0, 1, 10 ), QgsPoint( 3.0, 4.0 ) ) ); + c25.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( 6.0, 7.0 ) ); + c25.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( 16.0, 17.0 ) ); + c25.pointAt( 2, pt, v ); + QCOMPARE( pt, QgsPoint( 26.0, 27.0 ) ); + + //move 4d point in 4d line + l25.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 10, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 15, 10, 6, 7 ) ); + c25.clear(); + c25.addCurve( l25.clone() ); + QVERIFY( c25.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( QgsWkbTypes::PointZM, 6, 7, 12, 13 ) ) ); + c25.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 6, 7, 12, 13 ) ); + + //move 2d point in 4d line, existing z/m should be maintained + QVERIFY( c25.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 34, 35 ) ) ); + c25.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 34, 35, 12, 13 ) ); + + //move 4d point in 2d line + l25.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) ); + c25.clear(); + c25.addCurve( l25.clone() ); + QVERIFY( c25.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( QgsWkbTypes::PointZM, 3, 4, 2, 3 ) ) ); + c25.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( 3, 4 ) ); + + //delete vertex + + //empty line + QgsCompoundCurve c26; + QgsCircularString l26; + QVERIFY( !c26.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QVERIFY( c26.isEmpty() ); + + //valid line + l26.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 21, 22, 6, 7 ) + << QgsPoint( QgsWkbTypes::PointZM, 31, 32, 6, 7 ) ); + c26.addCurve( l26.clone() ); + //out of range vertices + QVERIFY( !c26.deleteVertex( QgsVertexId( 0, 0, -1 ) ) ); + QVERIFY( !c26.deleteVertex( QgsVertexId( 0, 0, 100 ) ) ); + + //valid vertices + QVERIFY( c26.deleteVertex( QgsVertexId( 0, 0, 1 ) ) ); + QCOMPARE( c26.numPoints(), 2 ); + c26.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) ); + c26.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 31, 32, 6, 7 ) ); + + //removing the next vertex removes all remaining vertices + QVERIFY( c26.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( c26.numPoints(), 0 ); + QVERIFY( c26.isEmpty() ); + + // two lines + QgsLineString ls26; + c26.clear(); + ls26.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 4, 5 ) ); + c26.addCurve( ls26.clone() ); + ls26.setPoints( QgsPointSequence() + << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 21, 32, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 31, 42, 4, 5 ) ); + c26.addCurve( ls26.clone() ); + QVERIFY( c26.deleteVertex( QgsVertexId( 0, 0, 1 ) ) ); + QCOMPARE( c26.nCurves(), 1 ); + const QgsLineString *ls26r = dynamic_cast< const QgsLineString * >( c26.curveAt( 0 ) ); + QCOMPARE( ls26r->numPoints(), 2 ); + QCOMPARE( ls26r->startPoint(), QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) ); + QCOMPARE( ls26r->endPoint(), QgsPoint( QgsWkbTypes::PointZM, 31, 42, 4, 5 ) ); + + c26.clear(); + ls26.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 21, 32, 4, 5 ) ); + c26.addCurve( ls26.clone() ); + ls26.setPoints( QgsPointSequence() + << QgsPoint( QgsWkbTypes::PointZM, 21, 32, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 31, 42, 4, 5 ) ); + c26.addCurve( ls26.clone() ); + QVERIFY( c26.deleteVertex( QgsVertexId( 0, 0, 2 ) ) ); + QCOMPARE( c26.nCurves(), 1 ); + ls26r = dynamic_cast< const QgsLineString * >( c26.curveAt( 0 ) ); + QCOMPARE( ls26r->numPoints(), 2 ); + QCOMPARE( ls26r->startPoint(), QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) ); + QCOMPARE( ls26r->endPoint(), QgsPoint( QgsWkbTypes::PointZM, 31, 42, 4, 5 ) ); + + //reversed + QgsCompoundCurve c27; + QgsCircularString l27; + std::unique_ptr< QgsCompoundCurve > reversed( c27.reversed() ); + QVERIFY( reversed->isEmpty() ); + l27.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 21, 22, 6, 7 ) ); + c27.addCurve( l27.clone() ); + QgsLineString ls27; + ls27.setPoints( QgsPointSequence() + << QgsPoint( QgsWkbTypes::PointZM, 21, 22, 6, 7 ) + << QgsPoint( QgsWkbTypes::PointZM, 23, 32, 7, 8 ) ); + c27.addCurve( ls27.clone() ); + + reversed.reset( c27.reversed() ); + QCOMPARE( reversed->numPoints(), 4 ); + QVERIFY( dynamic_cast< const QgsLineString * >( reversed->curveAt( 0 ) ) ); + QVERIFY( dynamic_cast< const QgsCircularString * >( reversed->curveAt( 1 ) ) ); + QCOMPARE( reversed->wkbType(), QgsWkbTypes::CompoundCurveZM ); + QVERIFY( reversed->is3D() ); + QVERIFY( reversed->isMeasure() ); + reversed->pointAt( 0, pt, v ); + reversed->pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 21, 22, 6, 7 ) ); + reversed->pointAt( 2, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 11, 12, 4, 5 ) ); + reversed->pointAt( 3, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) ); + + //addZValue + QgsCompoundCurve c28; + QgsCircularString l28; + QCOMPARE( c28.wkbType(), QgsWkbTypes::CompoundCurve ); + QVERIFY( c28.addZValue() ); + QCOMPARE( c28.wkbType(), QgsWkbTypes::CompoundCurveZ ); + c28.clear(); + //2d line + l28.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) ); + c28.addCurve( l28.clone() ); + l28.setPoints( QgsPointSequence() << QgsPoint( 11, 12 ) << QgsPoint( 3, 4 ) ); + c28.addCurve( l28.clone() ); + QVERIFY( c28.addZValue( 2 ) ); + QVERIFY( c28.is3D() ); + QCOMPARE( c28.wkbType(), QgsWkbTypes::CompoundCurveZ ); + c28.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZ, 1, 2, 2 ) ); + c28.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZ, 11, 12, 2 ) ); + c28.pointAt( 2, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZ, 3, 4, 2 ) ); + + QVERIFY( !c28.addZValue( 4 ) ); //already has z value, test that existing z is unchanged + c28.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZ, 1, 2, 2 ) ); + c28.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZ, 11, 12, 2 ) ); + c28.pointAt( 2, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZ, 3, 4, 2 ) ); + + //linestring with m + l28.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) << QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 4 ) ); + c28.clear(); + c28.addCurve( l28.clone() ); + l28.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 21, 32, 0, 4 ) ); + c28.addCurve( l28.clone() ); + QVERIFY( c28.addZValue( 5 ) ); + QVERIFY( c28.is3D() ); + QVERIFY( c28.isMeasure() ); + QCOMPARE( c28.wkbType(), QgsWkbTypes::CompoundCurveZM ); + c28.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 1, 2, 5, 3 ) ); + c28.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 11, 12, 5, 4 ) ); + c28.pointAt( 2, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 21, 32, 5, 4 ) ); + + //addMValue + c28.clear(); + //2d line + l28.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) ); + c28.addCurve( l28.clone() ); + l28.setPoints( QgsPointSequence() << QgsPoint( 11, 12 ) << QgsPoint( 3, 4 ) ); + c28.addCurve( l28.clone() ); + QVERIFY( c28.addMValue( 2 ) ); + QVERIFY( c28.isMeasure() ); + QCOMPARE( c28.wkbType(), QgsWkbTypes::CompoundCurveM ); + c28.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 2 ) ); + c28.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 2 ) ); + c28.pointAt( 2, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointM, 3, 4, 0, 2 ) ); + + QVERIFY( !c28.addMValue( 4 ) ); //already has z value, test that existing z is unchanged + c28.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 2 ) ); + c28.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 2 ) ); + c28.pointAt( 2, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointM, 3, 4, 0, 2 ) ); + + //linestring with z + l28.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 11, 12, 4 ) ); + c28.clear(); + c28.addCurve( l28.clone() ); + l28.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 11, 12, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 21, 32, 4 ) ); + c28.addCurve( l28.clone() ); + QVERIFY( c28.addMValue( 5 ) ); + QVERIFY( c28.is3D() ); + QVERIFY( c28.isMeasure() ); + QCOMPARE( c28.wkbType(), QgsWkbTypes::CompoundCurveZM ); + c28.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 5 ) ); + c28.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 11, 12, 4, 5 ) ); + c28.pointAt( 2, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 21, 32, 4, 5 ) ); + + //dropZValue + QgsCompoundCurve c28d; + QgsCircularString l28d; + QVERIFY( !c28d.dropZValue() ); + l28d.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) ); + c28d.addCurve( l28d.clone() ); + l28d.setPoints( QgsPointSequence() << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) ); + c28d.addCurve( l28d.clone() ); + QVERIFY( !c28d.dropZValue() ); + c28d.addZValue( 1.0 ); + QCOMPARE( c28d.wkbType(), QgsWkbTypes::CompoundCurveZ ); + QVERIFY( c28d.is3D() ); + QVERIFY( c28d.dropZValue() ); + QVERIFY( !c28d.is3D() ); + QCOMPARE( c28d.wkbType(), QgsWkbTypes::CompoundCurve ); + c28d.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::Point, 1, 2 ) ); + c28d.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::Point, 11, 12 ) ); + c28d.pointAt( 2, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::Point, 21, 22 ) ); + + QVERIFY( !c28d.dropZValue() ); //already dropped + //linestring with m + l28d.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 3, 4 ) ); + c28d.clear(); + c28d.addCurve( l28d.clone() ); + l28d.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 3, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 21, 22, 3, 4 ) ); + c28d.addCurve( l28d.clone() ); + QVERIFY( c28d.dropZValue() ); + QVERIFY( !c28d.is3D() ); + QVERIFY( c28d.isMeasure() ); + QCOMPARE( c28d.wkbType(), QgsWkbTypes::CompoundCurveM ); + c28d.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) ); + c28d.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 4 ) ); + c28d.pointAt( 2, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointM, 21, 22, 0, 4 ) ); + + //dropMValue + c28d.clear(); + QVERIFY( !c28d.dropMValue() ); + l28d.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) ); + c28d.addCurve( l28d.clone() ); + l28d.setPoints( QgsPointSequence() << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) ); + c28d.addCurve( l28d.clone() ); + QVERIFY( !c28d.dropMValue() ); + c28d.addMValue( 1.0 ); + QCOMPARE( c28d.wkbType(), QgsWkbTypes::CompoundCurveM ); + QVERIFY( c28d.isMeasure() ); + QVERIFY( c28d.dropMValue() ); + QVERIFY( !c28d.isMeasure() ); + QCOMPARE( c28d.wkbType(), QgsWkbTypes::CompoundCurve ); + c28d.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::Point, 1, 2 ) ); + c28d.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::Point, 11, 12 ) ); + c28d.pointAt( 2, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::Point, 21, 22 ) ); + + QVERIFY( !c28d.dropMValue() ); //already dropped + //linestring with z + l28d.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 3, 4 ) ); + c28d.clear(); + c28d.addCurve( l28d.clone() ); + l28d.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 3, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 21, 22, 3, 4 ) ); + c28d.addCurve( l28d.clone() ); + QVERIFY( c28d.dropMValue() ); + QVERIFY( !c28d.isMeasure() ); + QVERIFY( c28d.is3D() ); + QCOMPARE( c28d.wkbType(), QgsWkbTypes::CompoundCurveZ ); + c28d.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) ); + c28d.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZ, 11, 12, 3 ) ); + c28d.pointAt( 2, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZ, 21, 22, 3 ) ); + + //convertTo + l28d.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) ); + c28d.clear(); + c28d.addCurve( l28d.clone() ); + QVERIFY( c28d.convertTo( QgsWkbTypes::CompoundCurve ) ); + QCOMPARE( c28d.wkbType(), QgsWkbTypes::CompoundCurve ); + QVERIFY( c28d.convertTo( QgsWkbTypes::CompoundCurveZ ) ); + QCOMPARE( c28d.wkbType(), QgsWkbTypes::CompoundCurveZ ); + c28d.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZ, 1, 2 ) ); + + QVERIFY( c28d.convertTo( QgsWkbTypes::CompoundCurveZM ) ); + QCOMPARE( c28d.wkbType(), QgsWkbTypes::CompoundCurveZM ); + c28d.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 1, 2 ) ); + c28d.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 1, 2, 5 ) ); + c28d.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZM, 1, 2, 5.0 ) ); + QVERIFY( c28d.convertTo( QgsWkbTypes::CompoundCurveM ) ); + QCOMPARE( c28d.wkbType(), QgsWkbTypes::CompoundCurveM ); + c28d.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointM, 1, 2 ) ); + c28d.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 1, 2, 0, 6 ) ); + c28d.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointM, 1, 2, 0.0, 6.0 ) ); + QVERIFY( c28d.convertTo( QgsWkbTypes::CompoundCurve ) ); + QCOMPARE( c28d.wkbType(), QgsWkbTypes::CompoundCurve ); + c28d.pointAt( 0, pt, v ); + QCOMPARE( pt, QgsPoint( 1, 2 ) ); + QVERIFY( !c28d.convertTo( QgsWkbTypes::Polygon ) ); + + //isRing + QgsCircularString l30; + QgsCompoundCurve c30; + QVERIFY( !c30.isRing() ); + l30.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 2 ) ); + c30.addCurve( l30.clone() ); + QVERIFY( !c30.isRing() ); //<4 points + l30.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) << QgsPoint( 31, 32 ) ); + c30.clear(); + c30.addCurve( l30.clone() ); + QVERIFY( !c30.isRing() ); //not closed + l30.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) << QgsPoint( 1, 2 ) ); + c30.clear(); + c30.addCurve( l30.clone() ); + QVERIFY( c30.isRing() ); + l30.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) ); + c30.clear(); + c30.addCurve( l30.clone() ); + QVERIFY( !c30.isRing() ); + l30.setPoints( QgsPointSequence() << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) ); + c30.addCurve( l30.clone() ); + QVERIFY( !c30.isRing() ); + l30.setPoints( QgsPointSequence() << QgsPoint( 21, 22 ) << QgsPoint( 1, 2 ) ); + c30.addCurve( l30.clone() ); + QVERIFY( c30.isRing() ); + + //coordinateSequence + QgsCompoundCurve c31; + QgsCircularString l31; + QgsCoordinateSequence coords = c31.coordinateSequence(); + QCOMPARE( coords.count(), 1 ); + QCOMPARE( coords.at( 0 ).count(), 1 ); + QVERIFY( coords.at( 0 ).at( 0 ).isEmpty() ); + l31.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 4, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 21, 22, 6, 7 ) ); + c31.addCurve( l31.clone() ); + QgsLineString ls31; + ls31.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 21, 22, 6, 7 ) << + QgsPoint( QgsWkbTypes::PointZM, 31, 32, 16, 17 ) ); + c31.addCurve( ls31.clone() ); + coords = c31.coordinateSequence(); + QCOMPARE( coords.count(), 1 ); + QCOMPARE( coords.at( 0 ).count(), 1 ); + QCOMPARE( coords.at( 0 ).at( 0 ).count(), 4 ); + QCOMPARE( coords.at( 0 ).at( 0 ).at( 0 ), QgsPoint( QgsWkbTypes::PointZM, 1, 2, 2, 3 ) ); + QCOMPARE( coords.at( 0 ).at( 0 ).at( 1 ), QgsPoint( QgsWkbTypes::PointZM, 11, 12, 4, 5 ) ); + QCOMPARE( coords.at( 0 ).at( 0 ).at( 2 ), QgsPoint( QgsWkbTypes::PointZM, 21, 22, 6, 7 ) ); + QCOMPARE( coords.at( 0 ).at( 0 ).at( 3 ), QgsPoint( QgsWkbTypes::PointZM, 31, 32, 16, 17 ) ); + + //nextVertex + QgsCompoundCurve c32; + QgsCircularString l32; + QgsVertexId vId; + QVERIFY( !c32.nextVertex( vId, p ) ); + vId = QgsVertexId( 0, 0, -2 ); + QVERIFY( !c32.nextVertex( vId, p ) ); + vId = QgsVertexId( 0, 0, 10 ); + QVERIFY( !c32.nextVertex( vId, p ) ); + //CircularString + l32.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) ); + c32.addCurve( l32.clone() ); + vId = QgsVertexId( 0, 0, 2 ); //out of range + QVERIFY( !c32.nextVertex( vId, p ) ); + vId = QgsVertexId( 0, 0, -5 ); + QVERIFY( c32.nextVertex( vId, p ) ); + vId = QgsVertexId( 0, 0, -1 ); + QVERIFY( c32.nextVertex( vId, p ) ); + QCOMPARE( vId, QgsVertexId( 0, 0, 0 ) ); + QCOMPARE( p, QgsPoint( 1, 2 ) ); + QVERIFY( c32.nextVertex( vId, p ) ); + QCOMPARE( vId, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( p, QgsPoint( 11, 12 ) ); + QVERIFY( !c32.nextVertex( vId, p ) ); + vId = QgsVertexId( 0, 1, 0 ); + QVERIFY( c32.nextVertex( vId, p ) ); + QCOMPARE( vId, QgsVertexId( 0, 1, 1 ) ); //test that ring number is maintained + QCOMPARE( p, QgsPoint( 11, 12 ) ); + vId = QgsVertexId( 1, 0, 0 ); + QVERIFY( c32.nextVertex( vId, p ) ); + QCOMPARE( vId, QgsVertexId( 1, 0, 1 ) ); //test that part number is maintained + QCOMPARE( p, QgsPoint( 11, 12 ) ); + QgsLineString ls32; + ls32.setPoints( QgsPointSequence() << QgsPoint( 11, 12 ) << QgsPoint( 13, 14 ) ); + c32.addCurve( ls32.clone() ); + vId = QgsVertexId( 0, 0, 1 ); + QVERIFY( c32.nextVertex( vId, p ) ); + QCOMPARE( vId, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( p, QgsPoint( 13, 14 ) ); + QVERIFY( !c32.nextVertex( vId, p ) ); + + //CircularStringZ + l32.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) ); + c32.clear(); + c32.addCurve( l32.clone() ); + vId = QgsVertexId( 0, 0, -1 ); + QVERIFY( c32.nextVertex( vId, p ) ); + QCOMPARE( vId, QgsVertexId( 0, 0, 0 ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) ); + QVERIFY( c32.nextVertex( vId, p ) ); + QCOMPARE( vId, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) ); + QVERIFY( !c32.nextVertex( vId, p ) ); + //CircularStringM + l32.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 14 ) ); + c32.clear(); + c32.addCurve( l32.clone() ); + vId = QgsVertexId( 0, 0, -1 ); + QVERIFY( c32.nextVertex( vId, p ) ); + QCOMPARE( vId, QgsVertexId( 0, 0, 0 ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) ); + QVERIFY( c32.nextVertex( vId, p ) ); + QCOMPARE( vId, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 14 ) ); + QVERIFY( !c32.nextVertex( vId, p ) ); + //CircularStringZM + l32.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) ); + c32.clear(); + c32.addCurve( l32.clone() ); + vId = QgsVertexId( 0, 0, -1 ); + QVERIFY( c32.nextVertex( vId, p ) ); + QCOMPARE( vId, QgsVertexId( 0, 0, 0 ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) ); + QVERIFY( c32.nextVertex( vId, p ) ); + QCOMPARE( vId, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) ); + QVERIFY( !c32.nextVertex( vId, p ) ); + + //vertexAt and pointAt + QgsCompoundCurve c33; + QgsCircularString l33; + c33.vertexAt( QgsVertexId( 0, 0, -10 ) ); //out of bounds, check for no crash + c33.vertexAt( QgsVertexId( 0, 0, 10 ) ); //out of bounds, check for no crash + QVERIFY( !c33.pointAt( -10, p, type ) ); + QVERIFY( !c33.pointAt( 10, p, type ) ); + //CircularString + l33.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 22 ) ); + c33.addCurve( l33.clone() ); + c33.vertexAt( QgsVertexId( 0, 0, -10 ) ); + c33.vertexAt( QgsVertexId( 0, 0, 10 ) ); //out of bounds, check for no crash + QCOMPARE( c33.vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( 1, 2 ) ); + QCOMPARE( c33.vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( 11, 12 ) ); + QCOMPARE( c33.vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( 1, 22 ) ); + QVERIFY( !c33.pointAt( -10, p, type ) ); + QVERIFY( !c33.pointAt( 10, p, type ) ); + QVERIFY( c33.pointAt( 0, p, type ) ); + QCOMPARE( p, QgsPoint( 1, 2 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + QVERIFY( c33.pointAt( 1, p, type ) ); + QCOMPARE( p, QgsPoint( 11, 12 ) ); + QCOMPARE( type, QgsVertexId::CurveVertex ); + QVERIFY( c33.pointAt( 2, p, type ) ); + QCOMPARE( p, QgsPoint( 1, 22 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + QgsLineString ls33; + ls33.setPoints( QgsPointSequence() << QgsPoint( 1, 22 ) << QgsPoint( 3, 34 ) ); + c33.addCurve( ls33.clone() ); + QVERIFY( c33.pointAt( 3, p, type ) ); + QCOMPARE( p, QgsPoint( 3, 34 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + + c33.clear(); + //CircularStringZ + l33.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) << QgsPoint( QgsWkbTypes::PointZ, 1, 22, 23 ) ); + c33.addCurve( l33.clone() ); + QCOMPARE( c33.vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) ); + QCOMPARE( c33.vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) ); + QCOMPARE( c33.vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( QgsWkbTypes::PointZ, 1, 22, 23 ) ); + QVERIFY( c33.pointAt( 0, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + QVERIFY( c33.pointAt( 1, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) ); + QCOMPARE( type, QgsVertexId::CurveVertex ); + QVERIFY( c33.pointAt( 2, p, type ) ); + QCOMPARE( p, QgsPoint( 1, 22, 23 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + + //CircularStringM + c33.clear(); + l33.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 14 ) << QgsPoint( QgsWkbTypes::PointM, 1, 22, 0, 24 ) ); + c33.addCurve( l33.clone() ); + QCOMPARE( c33.vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) ); + QCOMPARE( c33.vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 14 ) ); + QCOMPARE( c33.vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( QgsWkbTypes::PointM, 1, 22, 0, 24 ) ); + QVERIFY( c33.pointAt( 0, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + QVERIFY( c33.pointAt( 1, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 14 ) ); + QCOMPARE( type, QgsVertexId::CurveVertex ); + QVERIFY( c33.pointAt( 2, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointM, 1, 22, 0, 24 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + //CircularStringZM + l33.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) << QgsPoint( QgsWkbTypes::PointZM, 1, 22, 23, 24 ) ); + c33.clear(); + c33.addCurve( l33.clone() ); + QCOMPARE( c33.vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) ); + QCOMPARE( c33.vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) ); + QCOMPARE( c33.vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( QgsWkbTypes::PointZM, 1, 22, 23, 24 ) ); + QVERIFY( c33.pointAt( 0, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + QVERIFY( c33.pointAt( 1, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) ); + QCOMPARE( type, QgsVertexId::CurveVertex ); + QVERIFY( c33.pointAt( 2, p, type ) ); + QCOMPARE( p, QgsPoint( QgsWkbTypes::PointZM, 1, 22, 23, 24 ) ); + QCOMPARE( type, QgsVertexId::SegmentVertex ); + + //centroid + QgsCircularString l34; + QgsCompoundCurve c34; + QCOMPARE( c34.centroid(), QgsPoint() ); + l34.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) ); + c34.addCurve( l34.clone() ); + QCOMPARE( c34.centroid(), QgsPoint( 5, 10 ) ); + l34.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 20, 10 ) << QgsPoint( 2, 9 ) ); + c34.clear(); + c34.addCurve( l34.clone() ); + QgsPoint centroid = c34.centroid(); + QGSCOMPARENEAR( centroid.x(), 7.333, 0.001 ); + QGSCOMPARENEAR( centroid.y(), 6.333, 0.001 ); + l34.setPoints( QgsPointSequence() << QgsPoint( 2, 9 ) << QgsPoint( 12, 9 ) << QgsPoint( 15, 19 ) ); + c34.addCurve( l34.clone() ); + centroid = c34.centroid(); + QGSCOMPARENEAR( centroid.x(), 9.756646, 0.001 ); + QGSCOMPARENEAR( centroid.y(), 8.229039, 0.001 ); + + //closest segment + QgsCompoundCurve c35; + QgsCircularString l35; + bool leftOf = false; + p = QgsPoint(); // reset all coords to zero + ( void )c35.closestSegment( QgsPoint( 1, 2 ), p, vId ); //empty line, just want no crash + l35.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) ); + c35.addCurve( l35.clone() ); + QVERIFY( c35.closestSegment( QgsPoint( 5, 10 ), p, vId ) < 0 ); + l35.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 7, 12 ) << QgsPoint( 5, 15 ) ); + c35.clear(); + c35.addCurve( l35.clone() ); + QGSCOMPARENEAR( c35.closestSegment( QgsPoint( 4, 11 ), p, vId, &leftOf ), 2.0, 0.0001 ); + QCOMPARE( p, QgsPoint( 5, 10 ) ); + QCOMPARE( vId, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, true ); + QGSCOMPARENEAR( c35.closestSegment( QgsPoint( 8, 11 ), p, vId, &leftOf ), 1.583512, 0.0001 ); + QGSCOMPARENEAR( p.x(), 6.84, 0.01 ); + QGSCOMPARENEAR( p.y(), 11.49, 0.01 ); + QCOMPARE( vId, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( c35.closestSegment( QgsPoint( 5.5, 11.5 ), p, vId, &leftOf ), 1.288897, 0.0001 ); + QGSCOMPARENEAR( p.x(), 6.302776, 0.01 ); + QGSCOMPARENEAR( p.y(), 10.7, 0.01 ); + QCOMPARE( vId, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, true ); + QGSCOMPARENEAR( c35.closestSegment( QgsPoint( 7, 16 ), p, vId, &leftOf ), 3.068288, 0.0001 ); + QGSCOMPARENEAR( p.x(), 5.981872, 0.01 ); + QGSCOMPARENEAR( p.y(), 14.574621, 0.01 ); + QCOMPARE( vId, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( c35.closestSegment( QgsPoint( 5.5, 13.5 ), p, vId, &leftOf ), 1.288897, 0.0001 ); + QGSCOMPARENEAR( p.x(), 6.302776, 0.01 ); + QGSCOMPARENEAR( p.y(), 14.3, 0.01 ); + QCOMPARE( vId, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, true ); + // point directly on segment + QCOMPARE( c35.closestSegment( QgsPoint( 5, 15 ), p, vId, &leftOf ), 0.0 ); + QCOMPARE( p, QgsPoint( 5, 15 ) ); + QCOMPARE( vId, QgsVertexId( 0, 0, 2 ) ); + + QgsLineString ls35; + ls35.setPoints( QgsPointSequence() << QgsPoint( 5, 15 ) << QgsPoint( 5, 20 ) ); + c35.addCurve( ls35.clone() ); + QGSCOMPARENEAR( c35.closestSegment( QgsPoint( 5.5, 16.5 ), p, vId, &leftOf ), 0.25, 0.0001 ); + QGSCOMPARENEAR( p.x(), 5.0, 0.01 ); + QGSCOMPARENEAR( p.y(), 16.5, 0.01 ); + QCOMPARE( vId, QgsVertexId( 0, 0, 3 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( c35.closestSegment( QgsPoint( 4.5, 16.5 ), p, vId, &leftOf ), 0.25, 0.0001 ); + QGSCOMPARENEAR( p.x(), 5.0, 0.01 ); + QGSCOMPARENEAR( p.y(), 16.5, 0.01 ); + QCOMPARE( vId, QgsVertexId( 0, 0, 3 ) ); + QCOMPARE( leftOf, true ); + QGSCOMPARENEAR( c35.closestSegment( QgsPoint( 4.5, 21.5 ), p, vId, &leftOf ), 2.500000, 0.0001 ); + QGSCOMPARENEAR( p.x(), 5.0, 0.01 ); + QGSCOMPARENEAR( p.y(), 20.0, 0.01 ); + QCOMPARE( vId, QgsVertexId( 0, 0, 3 ) ); + QCOMPARE( leftOf, true ); + QGSCOMPARENEAR( c35.closestSegment( QgsPoint( 5.5, 21.5 ), p, vId, &leftOf ), 2.500000, 0.0001 ); + QGSCOMPARENEAR( p.x(), 5.0, 0.01 ); + QGSCOMPARENEAR( p.y(), 20.0, 0.01 ); + QCOMPARE( vId, QgsVertexId( 0, 0, 3 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( c35.closestSegment( QgsPoint( 5, 20 ), p, vId, &leftOf ), 0.0000, 0.0001 ); + QGSCOMPARENEAR( p.x(), 5.0, 0.01 ); + QGSCOMPARENEAR( p.y(), 20.0, 0.01 ); + QCOMPARE( vId, QgsVertexId( 0, 0, 3 ) ); + + //sumUpArea + QgsCompoundCurve c36; + QgsCircularString l36; + double area = 1.0; //sumUpArea adds to area, so start with non-zero value + c36.sumUpArea( area ); + QCOMPARE( area, 1.0 ); + l36.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) ); + c36.addCurve( l36.clone() ); + c36.sumUpArea( area ); + QCOMPARE( area, 1.0 ); + l36.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 10, 10 ) ); + c36.clear(); + c36.addCurve( l36.clone() ); + c36.sumUpArea( area ); + QCOMPARE( area, 1.0 ); + l36.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 2, 0 ) << QgsPoint( 2, 2 ) ); + c36.clear(); + c36.addCurve( l36.clone() ); + c36.sumUpArea( area ); + QGSCOMPARENEAR( area, 4.141593, 0.0001 ); + l36.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 2, 0 ) << QgsPoint( 2, 2 ) << QgsPoint( 0, 2 ) ); + c36.clear(); + c36.addCurve( l36.clone() ); + c36.sumUpArea( area ); + QGSCOMPARENEAR( area, 7.283185, 0.0001 ); + // full circle + l36.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 4, 0 ) << QgsPoint( 0, 0 ) ); + c36.clear(); + c36.addCurve( l36.clone() ); + area = 0.0; + c36.sumUpArea( area ); + QGSCOMPARENEAR( area, 12.566370614359172, 0.0001 ); + + //boundingBox - test that bounding box is updated after every modification to the circular string + QgsCompoundCurve c37; + QgsCircularString l37; + QVERIFY( c37.boundingBox().isNull() ); + l37.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 10, 15 ) ); + c37.addCurve( l37.clone() ); + QCOMPARE( c37.boundingBox(), QgsRectangle( 5, 10, 10, 15 ) ); + l37.setPoints( QgsPointSequence() << QgsPoint( -5, -10 ) << QgsPoint( -6, -10 ) << QgsPoint( -5.5, -9 ) ); + c37.clear(); + c37.addCurve( l37.clone() ); + QCOMPARE( c37.boundingBox(), QgsRectangle( -6.125, -10.25, -5, -9 ) ); + QByteArray wkbToAppend = c37.asWkb(); + c37.clear(); + QVERIFY( c37.boundingBox().isNull() ); + QgsConstWkbPtr wkbToAppendPtr( wkbToAppend ); + l37.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 10, 15 ) ); + c37.clear(); + c37.addCurve( l37.clone() ); + QCOMPARE( c37.boundingBox(), QgsRectangle( 5, 10, 10, 15 ) ); + c37.fromWkb( wkbToAppendPtr ); + QCOMPARE( c37.boundingBox(), QgsRectangle( -6.125, -10.25, -5, -9 ) ); + c37.fromWkt( QStringLiteral( "CompoundCurve(CircularString( 5 10, 6 10, 5.5 9 ))" ) ); + QCOMPARE( c37.boundingBox(), QgsRectangle( 5, 9, 6.125, 10.25 ) ); + c37.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( -1, 7 ) ); + QgsRectangle r = c37.boundingBox(); + QGSCOMPARENEAR( r.xMinimum(), -3.014, 0.01 ); + QGSCOMPARENEAR( r.xMaximum(), 14.014, 0.01 ); + QGSCOMPARENEAR( r.yMinimum(), -7.0146, 0.01 ); + QGSCOMPARENEAR( r.yMaximum(), 12.4988, 0.01 ); + c37.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( -3, 10 ) ); + r = c37.boundingBox(); + QGSCOMPARENEAR( r.xMinimum(), -10.294, 0.01 ); + QGSCOMPARENEAR( r.xMaximum(), 12.294, 0.01 ); + QGSCOMPARENEAR( r.yMinimum(), 9, 0.01 ); + QGSCOMPARENEAR( r.yMaximum(), 31.856, 0.01 ); + c37.deleteVertex( QgsVertexId( 0, 0, 1 ) ); + r = c37.boundingBox(); + QGSCOMPARENEAR( r.xMinimum(), 5, 0.01 ); + QGSCOMPARENEAR( r.xMaximum(), 6.125, 0.01 ); + QGSCOMPARENEAR( r.yMinimum(), 9, 0.01 ); + QGSCOMPARENEAR( r.yMaximum(), 10.25, 0.01 ); + + //angle + QgsCompoundCurve c38; + QgsCircularString l38; + ( void )c38.vertexAngle( QgsVertexId() ); //just want no crash + ( void )c38.vertexAngle( QgsVertexId( 0, 0, 0 ) ); //just want no crash + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) ); + c38.addCurve( l38.clone() ); + ( void )c38.vertexAngle( QgsVertexId( 0, 0, 0 ) ); //just want no crash, any answer is meaningless + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) ); + c38.clear(); + c38.addCurve( l38.clone() ); + ( void )c38.vertexAngle( QgsVertexId( 0, 0, 0 ) ); //just want no crash, any answer is meaningless + ( void )c38.vertexAngle( QgsVertexId( 0, 0, 1 ) ); //just want no crash, any answer is meaningless + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 1, 1 ) << QgsPoint( 0, 2 ) ); + c38.clear(); + c38.addCurve( l38.clone() ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 0, 0.0001 ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 4.712389, 0.0001 ); + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 2 ) << QgsPoint( 1, 1 ) << QgsPoint( 0, 0 ) ); + c38.clear(); + c38.addCurve( l38.clone() ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 3.141593, 0.0001 ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 4.712389, 0.0001 ); + ( void )c38.vertexAngle( QgsVertexId( 0, 0, 20 ) ); // no crash + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 1, 1 ) << QgsPoint( 0, 2 ) + << QgsPoint( -1, 3 ) << QgsPoint( 0, 4 ) ); + c38.clear(); + c38.addCurve( l38.clone() ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 0, 0.0001 ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 4.712389, 0.0001 ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 3 ) ), 0, 0.0001 ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 4 ) ), 1.5708, 0.0001 ); + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 4 ) << QgsPoint( -1, 3 ) << QgsPoint( 0, 2 ) + << QgsPoint( 1, 1 ) << QgsPoint( 0, 0 ) ); + c38.clear(); + c38.addCurve( l38.clone() ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 4.712389, 0.0001 ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 3.141592, 0.0001 ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 3 ) ), 3.141592, 0.0001 ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 4 ) ), 4.712389, 0.0001 ); + + // with second curve + QgsLineString ls38; + ls38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, -1 ) ); + c38.addCurve( ls38.clone() ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 4 ) ), 3.926991, 0.0001 ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 5 ) ), 3.141593, 0.0001 ); + + //closed circular string + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) << QgsPoint( 0, 0 ) ); + c38.clear(); + c38.addCurve( l38.clone() ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 0, 0.00001 ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 3.141592, 0.00001 ); + QGSCOMPARENEAR( c38.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 0, 0.00001 ); + + //removing a vertex from a 3 point comound curveshould remove the whole line + QgsCircularString l39; + QgsCompoundCurve c39; + l39.setPoints( QList() << QgsPoint( 0, 0 ) << QgsPoint( 1, 1 ) << QgsPoint( 0, 2 ) ); + c39.addCurve( l39.clone() ); + QCOMPARE( c39.numPoints(), 3 ); + c39.deleteVertex( QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( c39.numPoints(), 0 ); + + //boundary + QgsCompoundCurve cBoundary1; + QgsCircularString boundary1; + QVERIFY( !cBoundary1.boundary() ); + boundary1.setPoints( QList() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) << QgsPoint( 1, 1 ) ); + cBoundary1.addCurve( boundary1.clone() ); + QgsAbstractGeometry *boundary = cBoundary1.boundary(); + QgsMultiPointV2 *mpBoundary = dynamic_cast< QgsMultiPointV2 * >( boundary ); + QVERIFY( mpBoundary ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->x(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->y(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->x(), 1.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->y(), 1.0 ); + delete boundary; + + // closed string = no boundary + boundary1.setPoints( QList() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) << QgsPoint( 1, 1 ) << QgsPoint( 0, 0 ) ); + cBoundary1.clear(); + cBoundary1.addCurve( boundary1.clone() ); + QVERIFY( !cBoundary1.boundary() ); + + //boundary with z + boundary1.setPoints( QList() << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 10 ) << QgsPoint( QgsWkbTypes::PointZ, 1, 0, 15 ) << QgsPoint( QgsWkbTypes::PointZ, 1, 1, 20 ) ); + cBoundary1.clear(); + cBoundary1.addCurve( boundary1.clone() ); + boundary = cBoundary1.boundary(); + mpBoundary = dynamic_cast< QgsMultiPointV2 * >( boundary ); + QVERIFY( mpBoundary ); + QCOMPARE( mpBoundary->geometryN( 0 )->wkbType(), QgsWkbTypes::PointZ ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->x(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->y(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->z(), 10.0 ); + QCOMPARE( mpBoundary->geometryN( 1 )->wkbType(), QgsWkbTypes::PointZ ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->x(), 1.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->y(), 1.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->z(), 20.0 ); + delete boundary; + + // addToPainterPath (note most tests are in test_qgsgeometry.py) + QgsCompoundCurve ccPath; + QgsCircularString path; + QPainterPath pPath; + ccPath.addToPainterPath( pPath ); + QVERIFY( pPath.isEmpty() ); + path.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) + << QgsPoint( QgsWkbTypes::PointZ, 21, 2, 3 ) ); + ccPath.addCurve( path.clone() ); + ccPath.addToPainterPath( pPath ); + QGSCOMPARENEAR( pPath.currentPosition().x(), 21.0, 0.01 ); + QGSCOMPARENEAR( pPath.currentPosition().y(), 2.0, 0.01 ); + QVERIFY( !pPath.isEmpty() ); + QgsLineString lsPath; + lsPath.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 21, 2, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 31, 12, 3 ) ); + ccPath.addCurve( lsPath.clone() ); + pPath = QPainterPath(); + ccPath.addToPainterPath( pPath ); + QGSCOMPARENEAR( pPath.currentPosition().x(), 31.0, 0.01 ); + QGSCOMPARENEAR( pPath.currentPosition().y(), 12.0, 0.01 ); + + // even number of points - should still work + pPath = QPainterPath(); + path.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZ, 11, 12, 13 ) ); + ccPath.clear(); + ccPath.addCurve( path.clone() ); + ccPath.addToPainterPath( pPath ); + QGSCOMPARENEAR( pPath.currentPosition().x(), 11.0, 0.01 ); + QGSCOMPARENEAR( pPath.currentPosition().y(), 12.0, 0.01 ); + QVERIFY( !pPath.isEmpty() ); + + // toCurveType + QgsCircularString curveLine1; + QgsCompoundCurve cc1; + curveLine1.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 22 ) ); + cc1.addCurve( curveLine1.clone() ); + std::unique_ptr< QgsCurve > curveType( cc1.toCurveType() ); + QCOMPARE( curveType->wkbType(), QgsWkbTypes::CompoundCurve ); + QCOMPARE( curveType->numPoints(), 3 ); + QCOMPARE( curveType->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( 1, 2 ) ); + QCOMPARE( curveType->vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( 11, 12 ) ); + QCOMPARE( curveType->vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( 1, 22 ) ); + QgsLineString ccls1; + ccls1.setPoints( QgsPointSequence() << QgsPoint( 1, 22 ) << QgsPoint( 1, 25 ) ); + cc1.addCurve( ccls1.clone() ); + curveType.reset( cc1.toCurveType() ); + QCOMPARE( curveType->wkbType(), QgsWkbTypes::CompoundCurve ); + QCOMPARE( curveType->numPoints(), 4 ); + QCOMPARE( curveType->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( 1, 2 ) ); + QCOMPARE( curveType->vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( 11, 12 ) ); + QCOMPARE( curveType->vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( 1, 22 ) ); + QCOMPARE( curveType->vertexAt( QgsVertexId( 0, 0, 3 ) ), QgsPoint( 1, 25 ) ); + //test that area of a compound curve ring is equal to a closed linestring with the same vertices QgsCompoundCurve cc; - QgsLineString *l1 = new QgsLineString(); - l1->setPoints( QgsPointSequence() << QgsPoint( 1, 1 ) << QgsPoint( 0, 2 ) ); - cc.addCurve( l1 ); - QgsLineString *l2 = new QgsLineString(); - l2->setPoints( QgsPointSequence() << QgsPoint( 0, 2 ) << QgsPoint( -1, 0 ) << QgsPoint( 0, -1 ) ); - cc.addCurve( l2 ); - QgsLineString *l3 = new QgsLineString(); - l3->setPoints( QgsPointSequence() << QgsPoint( 0, -1 ) << QgsPoint( 1, 1 ) ); - cc.addCurve( l3 ); + QgsLineString *ll1 = new QgsLineString(); + ll1->setPoints( QgsPointSequence() << QgsPoint( 1, 1 ) << QgsPoint( 0, 2 ) ); + cc.addCurve( ll1 ); + QgsLineString *ll2 = new QgsLineString(); + ll2->setPoints( QgsPointSequence() << QgsPoint( 0, 2 ) << QgsPoint( -1, 0 ) << QgsPoint( 0, -1 ) ); + cc.addCurve( ll2 ); + QgsLineString *ll3 = new QgsLineString(); + ll3->setPoints( QgsPointSequence() << QgsPoint( 0, -1 ) << QgsPoint( 1, 1 ) ); + cc.addCurve( ll3 ); double ccArea = 0.0; cc.sumUpArea( ccArea ); @@ -4518,10 +9854,499 @@ void TestQgsGeometry::compoundCurve() double lsArea = 0.0; ls.sumUpArea( lsArea ); QGSCOMPARENEAR( ccArea, lsArea, 4 * DBL_EPSILON ); + + + //addVertex + QgsCompoundCurve ac1; + ac1.addVertex( QgsPoint( 1.0, 2.0 ) ); + QVERIFY( !ac1.isEmpty() ); + QCOMPARE( ac1.numPoints(), 1 ); + QCOMPARE( ac1.vertexCount(), 1 ); + QCOMPARE( ac1.nCoordinates(), 1 ); + QCOMPARE( ac1.ringCount(), 1 ); + QCOMPARE( ac1.partCount(), 1 ); + QVERIFY( !ac1.is3D() ); + QVERIFY( !ac1.isMeasure() ); + QCOMPARE( ac1.wkbType(), QgsWkbTypes::CompoundCurve ); + QVERIFY( !ac1.hasCurvedSegments() ); + QCOMPARE( ac1.area(), 0.0 ); + QCOMPARE( ac1.perimeter(), 0.0 ); + + //adding first vertex should set linestring z/m type + QgsCompoundCurve ac2; + ac2.addVertex( QgsPoint( QgsWkbTypes::PointZ, 1.0, 2.0, 3.0 ) ); + QVERIFY( !ac2.isEmpty() ); + QVERIFY( ac2.is3D() ); + QVERIFY( !ac2.isMeasure() ); + QCOMPARE( ac2.wkbType(), QgsWkbTypes::CompoundCurveZ ); + QCOMPARE( ac2.wktTypeStr(), QString( "CompoundCurveZ" ) ); + + QgsCompoundCurve ac3; + ac3.addVertex( QgsPoint( QgsWkbTypes::PointM, 1.0, 2.0, 0.0, 3.0 ) ); + QVERIFY( !ac3.isEmpty() ); + QVERIFY( !ac3.is3D() ); + QVERIFY( ac3.isMeasure() ); + QCOMPARE( ac3.wkbType(), QgsWkbTypes::CompoundCurveM ); + QCOMPARE( ac3.wktTypeStr(), QString( "CompoundCurveM" ) ); + + QgsCompoundCurve ac4; + ac4.addVertex( QgsPoint( QgsWkbTypes::PointZM, 1.0, 2.0, 3.0, 4.0 ) ); + QVERIFY( !ac4.isEmpty() ); + QVERIFY( ac4.is3D() ); + QVERIFY( ac4.isMeasure() ); + QCOMPARE( ac4.wkbType(), QgsWkbTypes::CompoundCurveZM ); + QCOMPARE( ac4.wktTypeStr(), QString( "CompoundCurveZM" ) ); + + //adding subsequent vertices should not alter z/m type, regardless of points type + QgsCompoundCurve ac5; + ac5.addVertex( QgsPoint( QgsWkbTypes::Point, 1.0, 2.0 ) ); //2d type + QCOMPARE( ac5.wkbType(), QgsWkbTypes::CompoundCurve ); + ac5.addVertex( QgsPoint( QgsWkbTypes::PointZ, 11.0, 12.0, 13.0 ) ); // add 3d point + QCOMPARE( ac5.numPoints(), 2 ); + QCOMPARE( ac5.vertexCount(), 2 ); + QCOMPARE( ac5.nCoordinates(), 2 ); + QCOMPARE( ac5.ringCount(), 1 ); + QCOMPARE( ac5.partCount(), 1 ); + QCOMPARE( ac5.wkbType(), QgsWkbTypes::CompoundCurve ); //should still be 2d + QVERIFY( !ac5.is3D() ); + QCOMPARE( ac5.area(), 0.0 ); + QCOMPARE( ac5.perimeter(), 0.0 ); + + QgsCompoundCurve ac6; + ac6.addVertex( QgsPoint( QgsWkbTypes::PointZ, 1.0, 2.0, 3.0 ) ); //3d type + QCOMPARE( ac6.wkbType(), QgsWkbTypes::CompoundCurveZ ); + ac6.addVertex( QgsPoint( QgsWkbTypes::Point, 11.0, 12.0 ) ); //add 2d point + QCOMPARE( ac6.wkbType(), QgsWkbTypes::CompoundCurveZ ); //should still be 3d + ac6.pointAt( 1, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::PointZ, 11.0, 12.0 ) ); + QVERIFY( ac6.is3D() ); + QCOMPARE( ac6.numPoints(), 2 ); + QCOMPARE( ac6.vertexCount(), 2 ); + QCOMPARE( ac6.nCoordinates(), 2 ); + QCOMPARE( ac6.ringCount(), 1 ); + QCOMPARE( ac6.partCount(), 1 ); + + //close + QgsLineString closeC1; + QgsCompoundCurve closeCc1; + closeCc1.close(); + QVERIFY( closeCc1.isEmpty() ); + closeC1.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 22 ) ); + closeCc1.addCurve( closeC1.clone() ); + QCOMPARE( closeCc1.numPoints(), 3 ); + QVERIFY( !closeCc1.isClosed() ); + closeCc1.close(); + QCOMPARE( closeCc1.numPoints(), 4 ); + QVERIFY( closeCc1.isClosed() ); + closeCc1.pointAt( 3, pt, v ); + QCOMPARE( pt, QgsPoint( QgsWkbTypes::Point, 1, 2 ) ); + closeCc1.close(); + QCOMPARE( closeCc1.numPoints(), 4 ); + QVERIFY( closeCc1.isClosed() ); + } void TestQgsGeometry::multiPoint() { + //test constructor + QgsMultiPointV2 c1; + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QVERIFY( !c1.is3D() ); + QVERIFY( !c1.isMeasure() ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiPoint ); + QCOMPARE( c1.wktTypeStr(), QString( "MultiPoint" ) ); + QCOMPARE( c1.geometryType(), QString( "MultiPoint" ) ); + QCOMPARE( c1.dimension(), 0 ); + QVERIFY( !c1.hasCurvedSegments() ); + QCOMPARE( c1.area(), 0.0 ); + QCOMPARE( c1.perimeter(), 0.0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + QCOMPARE( c1.vertexCount( 0, 0 ), 0 ); + QCOMPARE( c1.vertexCount( 0, 1 ), 0 ); + QCOMPARE( c1.vertexCount( 1, 0 ), 0 ); + + //addGeometry + + //try with nullptr + c1.addGeometry( nullptr ); + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiPoint ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + + // not a point + QVERIFY( !c1.addGeometry( new QgsLineString() ) ); + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiPoint ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + + //valid geometry + QgsPoint part( 1, 10 ); + c1.addGeometry( part.clone() ); + QVERIFY( !c1.isEmpty() ); + QCOMPARE( c1.numGeometries(), 1 ); + QCOMPARE( c1.nCoordinates(), 1 ); + QCOMPARE( c1.ringCount(), 1 ); + QCOMPARE( c1.partCount(), 1 ); + QVERIFY( !c1.is3D() ); + QVERIFY( !c1.isMeasure() ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiPoint ); + QCOMPARE( c1.wktTypeStr(), QString( "MultiPoint" ) ); + QCOMPARE( c1.geometryType(), QString( "MultiPoint" ) ); + QCOMPARE( c1.dimension(), 0 ); + QVERIFY( !c1.hasCurvedSegments() ); + QCOMPARE( c1.area(), 0.0 ); + QCOMPARE( c1.perimeter(), 0.0 ); + QVERIFY( c1.geometryN( 0 ) ); + QCOMPARE( *static_cast< const QgsPoint * >( c1.geometryN( 0 ) ), part ); + QVERIFY( !c1.geometryN( 100 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + QCOMPARE( c1.vertexCount( 0, 0 ), 1 ); + QCOMPARE( c1.vertexCount( 1, 0 ), 0 ); + + //initial adding of geometry should set z/m type + part = QgsPoint( QgsWkbTypes::PointZ, 10, 11, 1 ); + QgsMultiPointV2 c2; + c2.addGeometry( part.clone() ); + QVERIFY( c2.is3D() ); + QVERIFY( !c2.isMeasure() ); + QCOMPARE( c2.wkbType(), QgsWkbTypes::MultiPointZ ); + QCOMPARE( c2.wktTypeStr(), QString( "MultiPointZ" ) ); + QCOMPARE( c2.geometryType(), QString( "MultiPoint" ) ); + QCOMPARE( *( static_cast< const QgsPoint * >( c2.geometryN( 0 ) ) ), part ); + QgsMultiPointV2 c3; + part = QgsPoint( QgsWkbTypes::PointM, 10, 10, 0, 3 ); + c3.addGeometry( part.clone() ); + QVERIFY( !c3.is3D() ); + QVERIFY( c3.isMeasure() ); + QCOMPARE( c3.wkbType(), QgsWkbTypes::MultiPointM ); + QCOMPARE( c3.wktTypeStr(), QString( "MultiPointM" ) ); + QCOMPARE( *( static_cast< const QgsPoint * >( c3.geometryN( 0 ) ) ), part ); + QgsMultiPointV2 c4; + part = QgsPoint( QgsWkbTypes::PointZM, 10, 10, 5, 3 ); + c4.addGeometry( part.clone() ); + QVERIFY( c4.is3D() ); + QVERIFY( c4.isMeasure() ); + QCOMPARE( c4.wkbType(), QgsWkbTypes::MultiPointZM ); + QCOMPARE( c4.wktTypeStr(), QString( "MultiPointZM" ) ); + QCOMPARE( *( static_cast< const QgsPoint * >( c4.geometryN( 0 ) ) ), part ); + + //add another part + QgsMultiPointV2 c6; + part = QgsPoint( 10, 11 ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.vertexCount( 0, 0 ), 1 ); + part = QgsPoint( 9, 1 ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.vertexCount( 1, 0 ), 1 ); + QCOMPARE( c6.numGeometries(), 2 ); + QVERIFY( c6.geometryN( 0 ) ); + QCOMPARE( *static_cast< const QgsPoint * >( c6.geometryN( 1 ) ), part ); + + QgsCoordinateSequence seq = c6.coordinateSequence(); + QCOMPARE( seq, QgsCoordinateSequence() << ( QgsRingSequence() << ( QgsPointSequence() << QgsPoint( 10, 11 ) ) ) + << ( QgsRingSequence() << ( QgsPointSequence() << QgsPoint( 9, 1 ) ) ) ); + QCOMPARE( c6.nCoordinates(), 2 ); + + //adding subsequent points should not alter z/m type, regardless of points type + c6.clear(); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPoint ); + c6.addGeometry( new QgsPoint( QgsWkbTypes::PointZ, 1.0, 2.0, 3 ) ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPoint ); + QCOMPARE( c6.vertexCount( 0, 0 ), 1 ); + QCOMPARE( c6.vertexCount( 1, 0 ), 1 ); + QCOMPARE( c6.vertexCount( 2, 0 ), 0 ); + QCOMPARE( c6.vertexCount( -1, 0 ), 0 ); + QCOMPARE( c6.nCoordinates(), 2 ); + QCOMPARE( c6.ringCount(), 1 ); + QCOMPARE( c6.partCount(), 2 ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPoint ); //should still be 2d + QVERIFY( !c6.is3D() ); + QCOMPARE( *( static_cast< const QgsPoint * >( c6.geometryN( 1 ) ) ), QgsPoint( 1, 2 ) ); + c6.addGeometry( new QgsPoint( QgsWkbTypes::PointM, 11.0, 12.0, 0, 3 ) ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPoint ); + QCOMPARE( c6.vertexCount( 0, 0 ), 1 ); + QCOMPARE( c6.vertexCount( 1, 0 ), 1 ); + QCOMPARE( c6.vertexCount( 2, 0 ), 1 ); + QCOMPARE( c6.nCoordinates(), 3 ); + QCOMPARE( c6.ringCount(), 1 ); + QCOMPARE( c6.partCount(), 3 ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPoint ); //should still be 2d + QVERIFY( !c6.is3D() ); + QVERIFY( !c6.isMeasure() ); + QCOMPARE( *( static_cast< const QgsPoint * >( c6.geometryN( 2 ) ) ), QgsPoint( 11, 12 ) ); + + c6.clear(); + c6.addGeometry( new QgsPoint( QgsWkbTypes::PointZ, 1.0, 2.0, 3 ) ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPointZ ); + c6.addGeometry( new QgsPoint( QgsWkbTypes::Point, 11.0, 12.0 ) ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPointZ ); + QVERIFY( c6.is3D() ); + QCOMPARE( *( static_cast< const QgsPoint * >( c6.geometryN( 0 ) ) ), QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) ); + QCOMPARE( *( static_cast< const QgsPoint * >( c6.geometryN( 1 ) ) ), QgsPoint( QgsWkbTypes::PointZ, 11, 12, 0 ) ); + c6.addGeometry( new QgsPoint( QgsWkbTypes::PointM, 21.0, 22.0, 0, 3 ) ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPointZ ); + QVERIFY( c6.is3D() ); + QVERIFY( !c6.isMeasure() ); + QCOMPARE( *( static_cast< const QgsPoint * >( c6.geometryN( 2 ) ) ), QgsPoint( QgsWkbTypes::PointZ, 21, 22, 0 ) ); + + c6.clear(); + c6.addGeometry( new QgsPoint( QgsWkbTypes::PointM, 1.0, 2.0, 0, 3 ) ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPointM ); + c6.addGeometry( new QgsPoint( QgsWkbTypes::Point, 11.0, 12.0 ) ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPointM ); + QVERIFY( c6.isMeasure() ); + QCOMPARE( *( static_cast< const QgsPoint * >( c6.geometryN( 0 ) ) ), QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) ); + QCOMPARE( *( static_cast< const QgsPoint * >( c6.geometryN( 1 ) ) ), QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 0 ) ); + c6.addGeometry( new QgsPoint( QgsWkbTypes::PointZ, 21.0, 22.0, 3 ) ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPointM ); + QVERIFY( !c6.is3D() ); + QVERIFY( c6.isMeasure() ); + QCOMPARE( *( static_cast< const QgsPoint * >( c6.geometryN( 2 ) ) ), QgsPoint( QgsWkbTypes::PointM, 21, 22, 0, 0 ) ); + + c6.clear(); + c6.addGeometry( new QgsPoint( QgsWkbTypes::PointZM, 1.0, 2.0, 4, 3 ) ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPointZM ); + c6.addGeometry( new QgsPoint( QgsWkbTypes::Point, 11.0, 12.0 ) ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPointZM ); + QVERIFY( c6.isMeasure() ); + QVERIFY( c6.is3D() ); + QCOMPARE( *( static_cast< const QgsPoint * >( c6.geometryN( 0 ) ) ), QgsPoint( QgsWkbTypes::PointZM, 1, 2, 4, 3 ) ); + QCOMPARE( *( static_cast< const QgsPoint * >( c6.geometryN( 1 ) ) ), QgsPoint( QgsWkbTypes::PointZM, 11, 12, 0, 0 ) ); + c6.addGeometry( new QgsPoint( QgsWkbTypes::PointZ, 21.0, 22.0, 3 ) ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPointZM ); + QVERIFY( c6.is3D() ); + QVERIFY( c6.isMeasure() ); + QCOMPARE( *( static_cast< const QgsPoint * >( c6.geometryN( 2 ) ) ), QgsPoint( QgsWkbTypes::PointZM, 21, 22, 3, 0 ) ); + c6.addGeometry( new QgsPoint( QgsWkbTypes::PointM, 31.0, 32.0, 0, 4 ) ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPointZM ); + QVERIFY( c6.is3D() ); + QVERIFY( c6.isMeasure() ); + QCOMPARE( *( static_cast< const QgsPoint * >( c6.geometryN( 3 ) ) ), QgsPoint( QgsWkbTypes::PointZM, 31, 32, 0, 4 ) ); + + //clear + QgsMultiPointV2 c7; + c7.addGeometry( new QgsPoint( QgsWkbTypes::PointZ, 0, 10, 2 ) ); + c7.addGeometry( new QgsPoint( QgsWkbTypes::PointZ, 11, 12, 3 ) ); + QCOMPARE( c7.numGeometries(), 2 ); + c7.clear(); + QVERIFY( c7.isEmpty() ); + QCOMPARE( c7.numGeometries(), 0 ); + QCOMPARE( c7.nCoordinates(), 0 ); + QCOMPARE( c7.ringCount(), 0 ); + QCOMPARE( c7.partCount(), 0 ); + QVERIFY( !c7.is3D() ); + QVERIFY( !c7.isMeasure() ); + QCOMPARE( c7.wkbType(), QgsWkbTypes::MultiPoint ); + + //clone + QgsMultiPointV2 c11; + std::unique_ptr< QgsMultiPointV2 >cloned( c11.clone() ); + QVERIFY( cloned->isEmpty() ); + c11.addGeometry( new QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 5 ) ); + c11.addGeometry( new QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) ); + cloned.reset( c11.clone() ); + QCOMPARE( cloned->numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsPoint * >( cloned->geometryN( 0 ) ), QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 5 ) ); + QCOMPARE( *static_cast< const QgsPoint * >( cloned->geometryN( 1 ) ), QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) ); + + //copy constructor + QgsMultiPointV2 c12; + QgsMultiPointV2 c13( c12 ); + QVERIFY( c13.isEmpty() ); + c12.addGeometry( new QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4, 8 ) ); + c12.addGeometry( new QgsPoint( QgsWkbTypes::PointZM, 20, 10, 14, 18 ) ); + QgsMultiPointV2 c14( c12 ); + QCOMPARE( c14.numGeometries(), 2 ); + QCOMPARE( c14.wkbType(), QgsWkbTypes::MultiPointZM ); + QCOMPARE( *static_cast< const QgsPoint * >( c14.geometryN( 0 ) ), QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4, 8 ) ); + QCOMPARE( *static_cast< const QgsPoint * >( c14.geometryN( 1 ) ), QgsPoint( QgsWkbTypes::PointZM, 20, 10, 14, 18 ) ); + + //assignment operator + QgsMultiPointV2 c15; + c15 = c13; + QCOMPARE( c15.numGeometries(), 0 ); + c15 = c14; + QCOMPARE( c15.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsPoint * >( c15.geometryN( 0 ) ), QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4, 8 ) ); + QCOMPARE( *static_cast< const QgsPoint * >( c15.geometryN( 1 ) ), QgsPoint( QgsWkbTypes::PointZM, 20, 10, 14, 18 ) ); + + //toCurveType + std::unique_ptr< QgsMultiPointV2 > curveType( c12.toCurveType() ); + QCOMPARE( curveType->wkbType(), QgsWkbTypes::MultiPointZM ); + QCOMPARE( curveType->numGeometries(), 2 ); + const QgsPoint *curve = static_cast< const QgsPoint * >( curveType->geometryN( 0 ) ); + QCOMPARE( *curve, QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4, 8 ) ); + curve = static_cast< const QgsPoint * >( curveType->geometryN( 1 ) ); + QCOMPARE( *curve, QgsPoint( QgsWkbTypes::PointZM, 20, 10, 14, 18 ) ); + + //to/fromWKB + QgsMultiPointV2 c16; + c16.addGeometry( new QgsPoint( QgsWkbTypes::Point, 10, 11 ) ); + c16.addGeometry( new QgsPoint( QgsWkbTypes::Point, 20, 21 ) ); + QByteArray wkb16 = c16.asWkb(); + QgsMultiPointV2 c17; + QgsConstWkbPtr wkb16ptr( wkb16 ); + c17.fromWkb( wkb16ptr ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsPoint * >( c17.geometryN( 0 ) ), QgsPoint( QgsWkbTypes::Point, 10, 11 ) ); + QCOMPARE( *static_cast< const QgsPoint * >( c17.geometryN( 1 ) ), QgsPoint( QgsWkbTypes::Point, 20, 21 ) ); + + //parts with Z + c16.clear(); + c17.clear(); + c16.addGeometry( new QgsPoint( QgsWkbTypes::PointZ, 10, 0, 4 ) ); + c16.addGeometry( new QgsPoint( QgsWkbTypes::PointZ, 9, 1, 4 ) ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr2( wkb16 ); + c17.fromWkb( wkb16ptr2 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiPointZ ); + QCOMPARE( *static_cast< const QgsPoint * >( c17.geometryN( 0 ) ), QgsPoint( QgsWkbTypes::PointZ, 10, 0, 4 ) ); + QCOMPARE( *static_cast< const QgsPoint * >( c17.geometryN( 1 ) ), QgsPoint( QgsWkbTypes::PointZ, 9, 1, 4 ) ); + + //parts with m + c16.clear(); + c17.clear(); + c16.addGeometry( new QgsPoint( QgsWkbTypes::PointM, 10, 0, 0, 4 ) ); + c16.addGeometry( new QgsPoint( QgsWkbTypes::PointM, 9, 1, 0, 4 ) ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr3( wkb16 ); + c17.fromWkb( wkb16ptr3 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiPointM ); + QCOMPARE( *static_cast< const QgsPoint * >( c17.geometryN( 0 ) ), QgsPoint( QgsWkbTypes::PointM, 10, 0, 0, 4 ) ); + QCOMPARE( *static_cast< const QgsPoint * >( c17.geometryN( 1 ) ), QgsPoint( QgsWkbTypes::PointM, 9, 1, 0, 4 ) ); + + // parts with ZM + c16.clear(); + c17.clear(); + c16.addGeometry( new QgsPoint( QgsWkbTypes::PointZM, 10, 0, 70, 4 ) ); + c16.addGeometry( new QgsPoint( QgsWkbTypes::PointZM, 9, 1, 3, 4 ) ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr4( wkb16 ); + c17.fromWkb( wkb16ptr4 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiPointZM ); + QCOMPARE( *static_cast< const QgsPoint * >( c17.geometryN( 0 ) ), QgsPoint( QgsWkbTypes::PointZM, 10, 0, 70, 4 ) ); + QCOMPARE( *static_cast< const QgsPoint * >( c17.geometryN( 1 ) ), QgsPoint( QgsWkbTypes::PointZM, 9, 1, 3, 4 ) ); + + //bad WKB - check for no crash + c17.clear(); + QgsConstWkbPtr nullPtr( nullptr, 0 ); + QVERIFY( !c17.fromWkb( nullPtr ) ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiPoint ); + QgsPoint point( 1, 2 ); + QByteArray wkbPoint = point.asWkb(); + QgsConstWkbPtr wkbPointPtr( wkbPoint ); + QVERIFY( !c17.fromWkb( wkbPointPtr ) ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiPoint ); + + //to/from WKT + QgsMultiPointV2 c18; + c18.addGeometry( new QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4, 8 ) ); + c18.addGeometry( new QgsPoint( QgsWkbTypes::PointZM, 9, 1, 4, 4 ) ); + + QString wkt = c18.asWkt(); + QVERIFY( !wkt.isEmpty() ); + QgsMultiPointV2 c19; + QVERIFY( c19.fromWkt( wkt ) ); + QCOMPARE( c19.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsPoint * >( c19.geometryN( 0 ) ), QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4, 8 ) ); + QCOMPARE( *static_cast< const QgsPoint * >( c19.geometryN( 1 ) ), QgsPoint( QgsWkbTypes::PointZM, 9, 1, 4, 4 ) ); + + //bad WKT + QgsMultiPointV2 c20; + QVERIFY( !c20.fromWkt( "Point()" ) ); + QVERIFY( c20.isEmpty() ); + QCOMPARE( c20.numGeometries(), 0 ); + QCOMPARE( c20.wkbType(), QgsWkbTypes::MultiPoint ); + + //as JSON + QgsMultiPointV2 exportC; + exportC.addGeometry( new QgsPoint( QgsWkbTypes::Point, 0, 10 ) ); + exportC.addGeometry( new QgsPoint( QgsWkbTypes::Point, 10, 0 ) ); + + // GML document for compare + QDomDocument doc( "gml" ); + + // as GML2 + QString expectedSimpleGML2( QStringLiteral( "0,1010,0" ) ); + QString res = elemToString( exportC.asGML2( doc ) ); + QGSCOMPAREGML( res, expectedSimpleGML2 ); + + //as GML3 + QString expectedSimpleGML3( QStringLiteral( "0 1010 0" ) ); + res = elemToString( exportC.asGML3( doc ) ); + QCOMPARE( res, expectedSimpleGML3 ); + + // as JSON + QString expectedSimpleJson( "{\"type\": \"MultiPoint\", \"coordinates\": [ [0, 10], [10, 0]] }" ); + res = exportC.asJSON(); + QCOMPARE( res, expectedSimpleJson ); + + QgsMultiPointV2 exportFloat; + exportFloat.addGeometry( new QgsPoint( QgsWkbTypes::Point, 10 / 9.0, 100 / 9.0 ) ); + exportFloat.addGeometry( new QgsPoint( QgsWkbTypes::Point, 4 / 3.0, 2 / 3.0 ) ); + + QString expectedJsonPrec3( QStringLiteral( "{\"type\": \"MultiPoint\", \"coordinates\": [ [1.111, 11.111], [1.333, 0.667]] }" ) ); + res = exportFloat.asJSON( 3 ); + QCOMPARE( res, expectedJsonPrec3 ); + + // as GML2 + QString expectedGML2prec3( QStringLiteral( "1.111,11.1111.333,0.667" ) ); + res = elemToString( exportFloat.asGML2( doc, 3 ) ); + QGSCOMPAREGML( res, expectedGML2prec3 ); + + //as GML3 + QString expectedGML3prec3( QStringLiteral( "1.111 11.1111.333 0.667" ) ); + res = elemToString( exportFloat.asGML3( doc, 3 ) ); + QCOMPARE( res, expectedGML3prec3 ); + + // insert geometry + QgsMultiPointV2 rc; + rc.clear(); + rc.insertGeometry( nullptr, 0 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + rc.insertGeometry( nullptr, -1 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + rc.insertGeometry( nullptr, 100 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + + rc.insertGeometry( new QgsLineString(), 0 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + + // cast + QVERIFY( !QgsMultiPointV2().cast( nullptr ) ); + QgsMultiPointV2 pCast; + QVERIFY( QgsMultiPointV2().cast( &pCast ) ); + QgsMultiPointV2 pCast2; + pCast2.fromWkt( QStringLiteral( "MultiPointZ(PointZ(0 1 1))" ) ); + QVERIFY( QgsMultiPointV2().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "MultiPointM(PointM(0 1 1))" ) ); + QVERIFY( QgsMultiPointV2().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "MultiPointZM(PointZM(0 1 1 2))" ) ); + QVERIFY( QgsMultiPointV2().cast( &pCast2 ) ); + //boundary //multipoints have no boundary defined @@ -4536,11 +10361,469 @@ void TestQgsGeometry::multiPoint() QgsPoint closest; QgsVertexId after; // return error - points have no segments - QVERIFY( boundaryMP.closestSegment( QgsPoint( 0.5, 0.5 ), closest, after, 0, 0 ) < 0 ); + QVERIFY( boundaryMP.closestSegment( QgsPoint( 0.5, 0.5 ), closest, after ) < 0 ); } void TestQgsGeometry::multiLineString() { + //test constructor + QgsMultiLineString c1; + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QVERIFY( !c1.is3D() ); + QVERIFY( !c1.isMeasure() ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiLineString ); + QCOMPARE( c1.wktTypeStr(), QString( "MultiLineString" ) ); + QCOMPARE( c1.geometryType(), QString( "MultiLineString" ) ); + QCOMPARE( c1.dimension(), 0 ); + QVERIFY( !c1.hasCurvedSegments() ); + QCOMPARE( c1.area(), 0.0 ); + QCOMPARE( c1.perimeter(), 0.0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + QCOMPARE( c1.vertexCount( 0, 0 ), 0 ); + QCOMPARE( c1.vertexCount( 0, 1 ), 0 ); + QCOMPARE( c1.vertexCount( 1, 0 ), 0 ); + + //addGeometry + + //try with nullptr + c1.addGeometry( nullptr ); + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiLineString ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + + // not a linestring + QVERIFY( !c1.addGeometry( new QgsPoint() ) ); + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiLineString ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + + //valid geometry + QgsLineString part; + part.setPoints( QgsPointSequence() << QgsPoint( 1, 10 ) << QgsPoint( 2, 11 ) ); + c1.addGeometry( part.clone() ); + QVERIFY( !c1.isEmpty() ); + QCOMPARE( c1.numGeometries(), 1 ); + QCOMPARE( c1.nCoordinates(), 2 ); + QCOMPARE( c1.ringCount(), 1 ); + QCOMPARE( c1.partCount(), 1 ); + QVERIFY( !c1.is3D() ); + QVERIFY( !c1.isMeasure() ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiLineString ); + QCOMPARE( c1.wktTypeStr(), QString( "MultiLineString" ) ); + QCOMPARE( c1.geometryType(), QString( "MultiLineString" ) ); + QCOMPARE( c1.dimension(), 1 ); + QVERIFY( !c1.hasCurvedSegments() ); + QCOMPARE( c1.area(), 0.0 ); + QCOMPARE( c1.perimeter(), 0.0 ); + QVERIFY( c1.geometryN( 0 ) ); + QCOMPARE( *static_cast< const QgsLineString * >( c1.geometryN( 0 ) ), part ); + QVERIFY( !c1.geometryN( 100 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + QCOMPARE( c1.vertexCount( 0, 0 ), 2 ); + QCOMPARE( c1.vertexCount( 1, 0 ), 0 ); + + //initial adding of geometry should set z/m type + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 10, 11, 1 ) << QgsPoint( QgsWkbTypes::PointZ, 20, 21, 2 ) ); + QgsMultiLineString c2; + c2.addGeometry( part.clone() ); + QVERIFY( c2.is3D() ); + QVERIFY( !c2.isMeasure() ); + QCOMPARE( c2.wkbType(), QgsWkbTypes::MultiLineStringZ ); + QCOMPARE( c2.wktTypeStr(), QString( "MultiLineStringZ" ) ); + QCOMPARE( c2.geometryType(), QString( "MultiLineString" ) ); + QCOMPARE( *( static_cast< const QgsLineString * >( c2.geometryN( 0 ) ) ), part ); + QgsMultiLineString c3; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 10, 11, 0, 1 ) << QgsPoint( QgsWkbTypes::PointM, 20, 21, 0, 2 ) ); + c3.addGeometry( part.clone() ); + QVERIFY( !c3.is3D() ); + QVERIFY( c3.isMeasure() ); + QCOMPARE( c3.wkbType(), QgsWkbTypes::MultiLineStringM ); + QCOMPARE( c3.wktTypeStr(), QString( "MultiLineStringM" ) ); + QCOMPARE( *( static_cast< const QgsLineString * >( c3.geometryN( 0 ) ) ), part ); + QgsMultiLineString c4; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 10, 11, 2, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 20, 21, 3, 2 ) ); + c4.addGeometry( part.clone() ); + QVERIFY( c4.is3D() ); + QVERIFY( c4.isMeasure() ); + QCOMPARE( c4.wkbType(), QgsWkbTypes::MultiLineStringZM ); + QCOMPARE( c4.wktTypeStr(), QString( "MultiLineStringZM" ) ); + QCOMPARE( *( static_cast< const QgsLineString * >( c4.geometryN( 0 ) ) ), part ); + + //add another part + QgsMultiLineString c6; + part.setPoints( QgsPointSequence() << QgsPoint( 1, 10 ) << QgsPoint( 2, 11 ) ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.vertexCount( 0, 0 ), 2 ); + part.setPoints( QgsPointSequence() << QgsPoint( 9, 12 ) << QgsPoint( 3, 13 ) ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.vertexCount( 1, 0 ), 2 ); + QCOMPARE( c6.numGeometries(), 2 ); + QVERIFY( c6.geometryN( 0 ) ); + QCOMPARE( *static_cast< const QgsLineString * >( c6.geometryN( 1 ) ), part ); + + //adding subsequent points should not alter z/m type, regardless of points type + c6.clear(); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiLineString ); + part.setPoints( QgsPointSequence() << QgsPoint( 1, 10, 2 ) << QgsPoint( 2, 11, 3 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiLineString ); + QCOMPARE( c6.vertexCount( 0, 0 ), 2 ); + QCOMPARE( c6.vertexCount( 1, 0 ), 2 ); + QCOMPARE( c6.vertexCount( 2, 0 ), 0 ); + QCOMPARE( c6.vertexCount( -1, 0 ), 0 ); + QCOMPARE( c6.nCoordinates(), 4 ); + QCOMPARE( c6.ringCount(), 1 ); + QCOMPARE( c6.partCount(), 2 ); + QVERIFY( !c6.is3D() ); + const QgsLineString *ls = static_cast< const QgsLineString * >( c6.geometryN( 0 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( 9, 12 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( 3, 13 ) ); + ls = static_cast< const QgsLineString * >( c6.geometryN( 1 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( 1, 10 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( 2, 11 ) ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 21, 30, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 32, 41, 0, 3 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiLineString ); + QCOMPARE( c6.vertexCount( 0, 0 ), 2 ); + QCOMPARE( c6.vertexCount( 1, 0 ), 2 ); + QCOMPARE( c6.vertexCount( 2, 0 ), 2 ); + QCOMPARE( c6.nCoordinates(), 6 ); + QCOMPARE( c6.ringCount(), 1 ); + QCOMPARE( c6.partCount(), 3 ); + QVERIFY( !c6.is3D() ); + QVERIFY( !c6.isMeasure() ); + ls = static_cast< const QgsLineString * >( c6.geometryN( 2 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( 21, 30 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( 32, 41 ) ); + + c6.clear(); + part.setPoints( QgsPointSequence() << QgsPoint( 1, 10, 2 ) << QgsPoint( 2, 11, 3 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiLineStringZ ); + part.setPoints( QgsPointSequence() << QgsPoint( 2, 20 ) << QgsPoint( 3, 31 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiLineStringZ ); + QVERIFY( c6.is3D() ); + ls = static_cast< const QgsLineString * >( c6.geometryN( 0 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( 1, 10, 2 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( 2, 11, 3 ) ); + ls = static_cast< const QgsLineString * >( c6.geometryN( 1 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( 2, 20, 0 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( 3, 31, 0 ) ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 6, 61, 0, 5 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiLineStringZ ); + QVERIFY( c6.is3D() ); + QVERIFY( !c6.isMeasure() ); + ls = static_cast< const QgsLineString * >( c6.geometryN( 2 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( 5, 50, 0 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( 6, 61, 0 ) ); + + c6.clear(); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiLineString ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 6, 61, 0, 5 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiLineStringM ); + part.setPoints( QgsPointSequence() << QgsPoint( 2, 20 ) << QgsPoint( 3, 31 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiLineStringM ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsLineString * >( c6.geometryN( 0 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointM, 6, 61, 0, 5 ) ); + ls = static_cast< const QgsLineString * >( c6.geometryN( 1 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 2, 20, 0, 0 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointM, 3, 31, 0, 0 ) ); + part.setPoints( QgsPointSequence() << QgsPoint( 11, 12, 13 ) << QgsPoint( 14, 15, 16 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiLineStringM ); + QVERIFY( !c6.is3D() ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsLineString * >( c6.geometryN( 2 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 0 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointM, 14, 15, 0, 0 ) ); + + c6.clear(); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 6, 61, 3, 5 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiLineStringZM ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7, 17 ) << QgsPoint( QgsWkbTypes::Point, 3, 13 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiLineStringZM ); + QVERIFY( c6.isMeasure() ); + QVERIFY( c6.is3D() ); + ls = static_cast< const QgsLineString * >( c6.geometryN( 0 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 6, 61, 3, 5 ) ); + ls = static_cast< const QgsLineString * >( c6.geometryN( 1 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 7, 17, 0, 0 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 3, 13, 0, 0 ) ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 77, 87, 7 ) << QgsPoint( QgsWkbTypes::PointZ, 83, 83, 8 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiLineStringZM ); + QVERIFY( c6.is3D() ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsLineString * >( c6.geometryN( 2 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 77, 87, 7, 0 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 83, 83, 8, 0 ) ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 177, 187, 0, 9 ) << QgsPoint( QgsWkbTypes::PointM, 183, 183, 0, 11 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiLineStringZM ); + QVERIFY( c6.is3D() ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsLineString * >( c6.geometryN( 3 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 177, 187, 0, 9 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 183, 183, 0, 11 ) ); + + //clear + QgsMultiLineString c7; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 6, 61, 3, 5 ) ) ; + c7.addGeometry( part.clone() ); + c7.addGeometry( part.clone() ); + QCOMPARE( c7.numGeometries(), 2 ); + c7.clear(); + QVERIFY( c7.isEmpty() ); + QCOMPARE( c7.numGeometries(), 0 ); + QCOMPARE( c7.nCoordinates(), 0 ); + QCOMPARE( c7.ringCount(), 0 ); + QCOMPARE( c7.partCount(), 0 ); + QVERIFY( !c7.is3D() ); + QVERIFY( !c7.isMeasure() ); + QCOMPARE( c7.wkbType(), QgsWkbTypes::MultiLineString ); + + //clone + QgsMultiLineString c11; + std::unique_ptr< QgsMultiLineString >cloned( c11.clone() ); + QVERIFY( cloned->isEmpty() ); + c11.addGeometry( part.clone() ); + c11.addGeometry( part.clone() ); + cloned.reset( c11.clone() ); + QCOMPARE( cloned->numGeometries(), 2 ); + ls = static_cast< const QgsLineString * >( cloned->geometryN( 0 ) ); + QCOMPARE( *ls, part ); + ls = static_cast< const QgsLineString * >( cloned->geometryN( 1 ) ); + QCOMPARE( *ls, part ); + + //copy constructor + QgsMultiLineString c12; + QgsMultiLineString c13( c12 ); + QVERIFY( c13.isEmpty() ); + c12.addGeometry( part.clone() ); + c12.addGeometry( part.clone() ); + QgsMultiLineString c14( c12 ); + QCOMPARE( c14.numGeometries(), 2 ); + QCOMPARE( c14.wkbType(), QgsWkbTypes::MultiLineStringZM ); + ls = static_cast< const QgsLineString * >( c14.geometryN( 0 ) ); + QCOMPARE( *ls, part ); + ls = static_cast< const QgsLineString * >( c14.geometryN( 1 ) ); + QCOMPARE( *ls, part ); + + //assignment operator + QgsMultiLineString c15; + c15 = c13; + QCOMPARE( c15.numGeometries(), 0 ); + c15 = c14; + QCOMPARE( c15.numGeometries(), 2 ); + ls = static_cast< const QgsLineString * >( c15.geometryN( 0 ) ); + QCOMPARE( *ls, part ); + ls = static_cast< const QgsLineString * >( c15.geometryN( 1 ) ); + QCOMPARE( *ls, part ); + + //toCurveType + std::unique_ptr< QgsMultiCurve > curveType( c12.toCurveType() ); + QCOMPARE( curveType->wkbType(), QgsWkbTypes::MultiCurveZM ); + QCOMPARE( curveType->numGeometries(), 2 ); + const QgsCompoundCurve *curve = static_cast< const QgsCompoundCurve * >( curveType->geometryN( 0 ) ); + QCOMPARE( curve->asWkt(), QStringLiteral( "CompoundCurveZM ((5 50 1 4, 6 61 3 5))" ) ); + curve = static_cast< const QgsCompoundCurve * >( curveType->geometryN( 1 ) ); + QCOMPARE( curve->asWkt(), QStringLiteral( "CompoundCurveZM ((5 50 1 4, 6 61 3 5))" ) ); + + //to/fromWKB + QgsMultiLineString c16; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7, 17 ) << QgsPoint( QgsWkbTypes::Point, 3, 13 ) ) ; + c16.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 27, 37 ) << QgsPoint( QgsWkbTypes::Point, 43, 43 ) ) ; + c16.addGeometry( part.clone() ); + QByteArray wkb16 = c16.asWkb(); + QgsMultiLineString c17; + QgsConstWkbPtr wkb16ptr( wkb16 ); + c17.fromWkb( wkb16ptr ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 0 ) ), *static_cast< const QgsLineString * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 1 ) ), *static_cast< const QgsLineString * >( c16.geometryN( 1 ) ) ); + + //parts with Z + c16.clear(); + c17.clear(); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 7, 17, 1 ) << QgsPoint( QgsWkbTypes::PointZ, 3, 13, 4 ) ) ; + c16.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 27, 37, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 43, 43, 5 ) ) ; + c16.addGeometry( part.clone() ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr2( wkb16 ); + c17.fromWkb( wkb16ptr2 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiLineStringZ ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 0 ) ), *static_cast< const QgsLineString * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 1 ) ), *static_cast< const QgsLineString * >( c16.geometryN( 1 ) ) ); + + //parts with m + c16.clear(); + c17.clear(); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 7, 17, 0, 1 ) << QgsPoint( QgsWkbTypes::PointM, 3, 13, 0, 4 ) ) ; + c16.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 27, 37, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 43, 43, 0, 5 ) ) ; + c16.addGeometry( part.clone() ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr3( wkb16 ); + c17.fromWkb( wkb16ptr3 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiLineStringM ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 0 ) ), *static_cast< const QgsLineString * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 1 ) ), *static_cast< const QgsLineString * >( c16.geometryN( 1 ) ) ); + + // parts with ZM + c16.clear(); + c17.clear(); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 7, 17, 4, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 3, 13, 1, 4 ) ) ; + c16.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 27, 37, 6, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 43, 43, 11, 5 ) ) ; + c16.addGeometry( part.clone() ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr4( wkb16 ); + c17.fromWkb( wkb16ptr4 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiLineStringZM ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 0 ) ), *static_cast< const QgsLineString * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 1 ) ), *static_cast< const QgsLineString * >( c16.geometryN( 1 ) ) ); + + //bad WKB - check for no crash + c17.clear(); + QgsConstWkbPtr nullPtr( nullptr, 0 ); + QVERIFY( !c17.fromWkb( nullPtr ) ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiLineString ); + QgsPoint point( 1, 2 ); + QByteArray wkbPoint = point.asWkb(); + QgsConstWkbPtr wkbPointPtr( wkbPoint ); + QVERIFY( !c17.fromWkb( wkbPointPtr ) ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiLineString ); + + //to/from WKT + QgsMultiLineString c18; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 7, 17, 4, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 3, 13, 1, 4 ) ) ; + c18.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 27, 37, 6, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 43, 43, 11, 5 ) ) ; + c18.addGeometry( part.clone() ); + + QString wkt = c18.asWkt(); + QVERIFY( !wkt.isEmpty() ); + QgsMultiLineString c19; + QVERIFY( c19.fromWkt( wkt ) ); + QCOMPARE( c19.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsLineString * >( c19.geometryN( 0 ) ), *static_cast< const QgsLineString * >( c18.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsLineString * >( c19.geometryN( 1 ) ), *static_cast< const QgsLineString * >( c18.geometryN( 1 ) ) ); + + //bad WKT + QgsMultiLineString c20; + QVERIFY( !c20.fromWkt( "Point()" ) ); + QVERIFY( c20.isEmpty() ); + QCOMPARE( c20.numGeometries(), 0 ); + QCOMPARE( c20.wkbType(), QgsWkbTypes::MultiLineString ); + + //as JSON + QgsMultiLineString exportC; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7, 17 ) << QgsPoint( QgsWkbTypes::Point, 3, 13 ) ) ; + exportC.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 27, 37 ) << QgsPoint( QgsWkbTypes::Point, 43, 43 ) ) ; + exportC.addGeometry( part.clone() ); + + // GML document for compare + QDomDocument doc( "gml" ); + + // as GML2 + QString expectedSimpleGML2( QStringLiteral( "7,17 3,1327,37 43,43" ) ); + QString res = elemToString( exportC.asGML2( doc ) ); + QGSCOMPAREGML( res, expectedSimpleGML2 ); + + //as GML3 + QString expectedSimpleGML3( QStringLiteral( "7 17 3 1327 37 43 43" ) ); + res = elemToString( exportC.asGML3( doc ) ); + QCOMPARE( res, expectedSimpleGML3 ); + + // as JSON + QString expectedSimpleJson( "{\"type\": \"MultiLineString\", \"coordinates\": [[ [7, 17], [3, 13]], [ [27, 37], [43, 43]]] }" ); + res = exportC.asJSON(); + QCOMPARE( res, expectedSimpleJson ); + + QgsMultiLineString exportFloat; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7 / 3.0, 17 / 3.0 ) << QgsPoint( QgsWkbTypes::Point, 3 / 5.0, 13 / 3.0 ) ) ; + exportFloat.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 27 / 3.0, 37 / 9.0 ) << QgsPoint( QgsWkbTypes::Point, 43 / 41.0, 43 / 42.0 ) ) ; + exportFloat.addGeometry( part.clone() ); + + QString expectedJsonPrec3( QStringLiteral( "{\"type\": \"MultiLineString\", \"coordinates\": [[ [2.333, 5.667], [0.6, 4.333]], [ [9, 4.111], [1.049, 1.024]]] }" ) ); + res = exportFloat.asJSON( 3 ); + QCOMPARE( res, expectedJsonPrec3 ); + + // as GML2 + QString expectedGML2prec3( QStringLiteral( "2.333,5.667 0.6,4.3339,4.111 1.049,1.024" ) ); + res = elemToString( exportFloat.asGML2( doc, 3 ) ); + QGSCOMPAREGML( res, expectedGML2prec3 ); + + //as GML3 + QString expectedGML3prec3( QStringLiteral( "2.333 5.667 0.6 4.3339 4.111 1.049 1.024" ) ); + res = elemToString( exportFloat.asGML3( doc, 3 ) ); + QCOMPARE( res, expectedGML3prec3 ); + + // insert geometry + QgsMultiLineString rc; + rc.clear(); + rc.insertGeometry( nullptr, 0 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + rc.insertGeometry( nullptr, -1 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + rc.insertGeometry( nullptr, 100 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + + rc.insertGeometry( new QgsPoint(), 0 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + + rc.insertGeometry( part.clone(), 0 ); + QVERIFY( !rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 1 ); + + // cast + QVERIFY( !QgsMultiLineString().cast( nullptr ) ); + QgsMultiLineString pCast; + QVERIFY( QgsMultiLineString().cast( &pCast ) ); + QgsMultiLineString pCast2; + pCast2.fromWkt( QStringLiteral( "MultiLineStringZ()" ) ); + QVERIFY( QgsMultiLineString().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "MultiLineStringM()" ) ); + QVERIFY( QgsMultiLineString().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "MultiLineStringZM()" ) ); + QVERIFY( QgsMultiLineString().cast( &pCast2 ) ); + //boundary QgsMultiLineString multiLine1; QVERIFY( !multiLine1.boundary() ); @@ -4624,8 +10907,1775 @@ void TestQgsGeometry::multiLineString() delete boundary; } +void TestQgsGeometry::multiCurve() +{ + //test constructor + QgsMultiCurve c1; + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QVERIFY( !c1.is3D() ); + QVERIFY( !c1.isMeasure() ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiCurve ); + QCOMPARE( c1.wktTypeStr(), QString( "MultiCurve" ) ); + QCOMPARE( c1.geometryType(), QString( "MultiCurve" ) ); + QCOMPARE( c1.dimension(), 0 ); + QVERIFY( !c1.hasCurvedSegments() ); + QCOMPARE( c1.area(), 0.0 ); + QCOMPARE( c1.perimeter(), 0.0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + QCOMPARE( c1.vertexCount( 0, 0 ), 0 ); + QCOMPARE( c1.vertexCount( 0, 1 ), 0 ); + QCOMPARE( c1.vertexCount( 1, 0 ), 0 ); + + //addGeometry + //try with nullptr + c1.addGeometry( nullptr ); + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiCurve ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + + // not a curve + QVERIFY( !c1.addGeometry( new QgsPoint() ) ); + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiCurve ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + + //valid geometry + QgsCircularString part; + part.setPoints( QgsPointSequence() << QgsPoint( 1, 10 ) << QgsPoint( 2, 11 ) << QgsPoint( 1, 12 ) ); + c1.addGeometry( part.clone() ); + QVERIFY( !c1.isEmpty() ); + QCOMPARE( c1.numGeometries(), 1 ); + QCOMPARE( c1.nCoordinates(), 3 ); + QCOMPARE( c1.ringCount(), 1 ); + QCOMPARE( c1.partCount(), 1 ); + QVERIFY( !c1.is3D() ); + QVERIFY( !c1.isMeasure() ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiCurve ); + QCOMPARE( c1.wktTypeStr(), QString( "MultiCurve" ) ); + QCOMPARE( c1.geometryType(), QString( "MultiCurve" ) ); + QCOMPARE( c1.dimension(), 1 ); + QVERIFY( c1.hasCurvedSegments() ); + QCOMPARE( c1.area(), 0.0 ); + QCOMPARE( c1.perimeter(), 0.0 ); + QVERIFY( c1.geometryN( 0 ) ); + QCOMPARE( *static_cast< const QgsCircularString * >( c1.geometryN( 0 ) ), part ); + QVERIFY( !c1.geometryN( 100 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + QCOMPARE( c1.vertexCount( 0, 0 ), 3 ); + QCOMPARE( c1.vertexCount( 1, 0 ), 0 ); + + //initial adding of geometry should set z/m type + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 10, 11, 1 ) << QgsPoint( QgsWkbTypes::PointZ, 20, 21, 2 ) + << QgsPoint( QgsWkbTypes::PointZ, 10, 31, 3 ) ); + QgsMultiCurve c2; + c2.addGeometry( part.clone() ); + QVERIFY( c2.is3D() ); + QVERIFY( !c2.isMeasure() ); + QCOMPARE( c2.wkbType(), QgsWkbTypes::MultiCurveZ ); + QCOMPARE( c2.wktTypeStr(), QString( "MultiCurveZ" ) ); + QCOMPARE( c2.geometryType(), QString( "MultiCurve" ) ); + QCOMPARE( *( static_cast< const QgsCircularString * >( c2.geometryN( 0 ) ) ), part ); + QgsMultiCurve c3; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 10, 11, 0, 1 ) << QgsPoint( QgsWkbTypes::PointM, 20, 21, 0, 2 ) + << QgsPoint( QgsWkbTypes::PointM, 10, 31, 0, 3 ) ); + c3.addGeometry( part.clone() ); + QVERIFY( !c3.is3D() ); + QVERIFY( c3.isMeasure() ); + QCOMPARE( c3.wkbType(), QgsWkbTypes::MultiCurveM ); + QCOMPARE( c3.wktTypeStr(), QString( "MultiCurveM" ) ); + QCOMPARE( *( static_cast< const QgsCircularString * >( c3.geometryN( 0 ) ) ), part ); + QgsMultiCurve c4; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 10, 11, 2, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 20, 21, 3, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 10, 31, 4, 5 ) ); + c4.addGeometry( part.clone() ); + QVERIFY( c4.is3D() ); + QVERIFY( c4.isMeasure() ); + QCOMPARE( c4.wkbType(), QgsWkbTypes::MultiCurveZM ); + QCOMPARE( c4.wktTypeStr(), QString( "MultiCurveZM" ) ); + QCOMPARE( *( static_cast< const QgsCircularString * >( c4.geometryN( 0 ) ) ), part ); + + //add another part + QgsMultiCurve c6; + part.setPoints( QgsPointSequence() << QgsPoint( 1, 10 ) << QgsPoint( 2, 11 ) << QgsPoint( 1, 20 ) ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.vertexCount( 0, 0 ), 3 ); + part.setPoints( QgsPointSequence() << QgsPoint( 9, 12 ) << QgsPoint( 3, 13 ) << QgsPoint( 9, 20 ) ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.vertexCount( 1, 0 ), 3 ); + QCOMPARE( c6.numGeometries(), 2 ); + QVERIFY( c6.geometryN( 0 ) ); + QCOMPARE( *static_cast< const QgsCircularString * >( c6.geometryN( 1 ) ), part ); + + //adding subsequent points should not alter z/m type, regardless of parts type + c6.clear(); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiCurve ); + part.setPoints( QgsPointSequence() << QgsPoint( 1, 10, 2 ) << QgsPoint( 2, 11, 3 ) << QgsPoint( 1, 20, 4 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiCurve ); + QCOMPARE( c6.vertexCount( 0, 0 ), 3 ); + QCOMPARE( c6.vertexCount( 1, 0 ), 3 ); + QCOMPARE( c6.vertexCount( 2, 0 ), 0 ); + QCOMPARE( c6.vertexCount( -1, 0 ), 0 ); + QCOMPARE( c6.nCoordinates(), 6 ); + QCOMPARE( c6.ringCount(), 1 ); + QCOMPARE( c6.partCount(), 2 ); + QVERIFY( !c6.is3D() ); + const QgsCircularString *ls = static_cast< const QgsCircularString * >( c6.geometryN( 0 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( 9, 12 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( 3, 13 ) ); + QCOMPARE( ls->pointN( 2 ), QgsPoint( 9, 20 ) ); + ls = static_cast< const QgsCircularString * >( c6.geometryN( 1 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( 1, 10 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( 2, 11 ) ); + QCOMPARE( ls->pointN( 2 ), QgsPoint( 1, 20 ) ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 21, 30, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 32, 41, 0, 3 ) + << QgsPoint( QgsWkbTypes::PointM, 21, 51, 0, 4 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiCurve ); + QCOMPARE( c6.vertexCount( 0, 0 ), 3 ); + QCOMPARE( c6.vertexCount( 1, 0 ), 3 ); + QCOMPARE( c6.vertexCount( 2, 0 ), 3 ); + QCOMPARE( c6.nCoordinates(), 9 ); + QCOMPARE( c6.ringCount(), 1 ); + QCOMPARE( c6.partCount(), 3 ); + QVERIFY( !c6.is3D() ); + QVERIFY( !c6.isMeasure() ); + ls = static_cast< const QgsCircularString * >( c6.geometryN( 2 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( 21, 30 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( 32, 41 ) ); + QCOMPARE( ls->pointN( 2 ), QgsPoint( 21, 51 ) ); + + c6.clear(); + part.setPoints( QgsPointSequence() << QgsPoint( 1, 10, 2 ) << QgsPoint( 2, 11, 3 ) << QgsPoint( 1, 21, 4 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiCurveZ ); + part.setPoints( QgsPointSequence() << QgsPoint( 2, 20 ) << QgsPoint( 3, 31 ) << QgsPoint( 2, 41 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiCurveZ ); + QVERIFY( c6.is3D() ); + ls = static_cast< const QgsCircularString * >( c6.geometryN( 0 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( 1, 10, 2 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( 2, 11, 3 ) ); + QCOMPARE( ls->pointN( 2 ), QgsPoint( 1, 21, 4 ) ); + ls = static_cast< const QgsCircularString * >( c6.geometryN( 1 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( 2, 20, 0 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( 3, 31, 0 ) ); + QCOMPARE( ls->pointN( 2 ), QgsPoint( 2, 41, 0 ) ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 6, 61, 0, 5 ) + << QgsPoint( QgsWkbTypes::PointM, 5, 71, 0, 6 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiCurveZ ); + QVERIFY( c6.is3D() ); + QVERIFY( !c6.isMeasure() ); + ls = static_cast< const QgsCircularString * >( c6.geometryN( 2 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( 5, 50, 0 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( 6, 61, 0 ) ); + QCOMPARE( ls->pointN( 2 ), QgsPoint( 5, 71, 0 ) ); + + c6.clear(); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiCurve ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 6, 61, 0, 5 ) + << QgsPoint( QgsWkbTypes::PointM, 5, 71, 0, 5 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiCurveM ); + part.setPoints( QgsPointSequence() << QgsPoint( 2, 20 ) << QgsPoint( 3, 31 ) << QgsPoint( 2, 41 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiCurveM ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsCircularString * >( c6.geometryN( 0 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointM, 6, 61, 0, 5 ) ); + QCOMPARE( ls->pointN( 2 ), QgsPoint( QgsWkbTypes::PointM, 5, 71, 0, 5 ) ); + ls = static_cast< const QgsCircularString * >( c6.geometryN( 1 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 2, 20, 0, 0 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointM, 3, 31, 0, 0 ) ); + QCOMPARE( ls->pointN( 2 ), QgsPoint( QgsWkbTypes::PointM, 2, 41, 0, 0 ) ); + part.setPoints( QgsPointSequence() << QgsPoint( 11, 12, 13 ) << QgsPoint( 14, 15, 16 ) << QgsPoint( 11, 25, 17 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiCurveM ); + QVERIFY( !c6.is3D() ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsCircularString * >( c6.geometryN( 2 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 0 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointM, 14, 15, 0, 0 ) ); + QCOMPARE( ls->pointN( 2 ), QgsPoint( QgsWkbTypes::PointM, 11, 25, 0, 0 ) ); + + c6.clear(); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 6, 61, 3, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 5, 71, 4, 6 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiCurveZM ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7, 17 ) << QgsPoint( QgsWkbTypes::Point, 3, 13 ) + << QgsPoint( QgsWkbTypes::Point, 7, 11 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiCurveZM ); + QVERIFY( c6.isMeasure() ); + QVERIFY( c6.is3D() ); + ls = static_cast< const QgsCircularString * >( c6.geometryN( 0 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 6, 61, 3, 5 ) ); + QCOMPARE( ls->pointN( 2 ), QgsPoint( QgsWkbTypes::PointZM, 5, 71, 4, 6 ) ); + ls = static_cast< const QgsCircularString * >( c6.geometryN( 1 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 7, 17, 0, 0 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 3, 13, 0, 0 ) ); + QCOMPARE( ls->pointN( 2 ), QgsPoint( QgsWkbTypes::PointZM, 7, 11, 0, 0 ) ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 77, 87, 7 ) << QgsPoint( QgsWkbTypes::PointZ, 83, 83, 8 ) + << QgsPoint( QgsWkbTypes::PointZ, 77, 81, 9 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiCurveZM ); + QVERIFY( c6.is3D() ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsCircularString * >( c6.geometryN( 2 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 77, 87, 7, 0 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 83, 83, 8, 0 ) ); + QCOMPARE( ls->pointN( 2 ), QgsPoint( QgsWkbTypes::PointZM, 77, 81, 9, 0 ) ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 177, 187, 0, 9 ) << QgsPoint( QgsWkbTypes::PointM, 183, 183, 0, 11 ) + << QgsPoint( QgsWkbTypes::PointM, 177, 181, 0, 13 ) ) ; + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiCurveZM ); + QVERIFY( c6.is3D() ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsCircularString * >( c6.geometryN( 3 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 177, 187, 0, 9 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 183, 183, 0, 11 ) ); + QCOMPARE( ls->pointN( 2 ), QgsPoint( QgsWkbTypes::PointZM, 177, 181, 0, 13 ) ); + + //clear + QgsMultiCurve c7; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 6, 61, 3, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 5, 71, 4, 6 ) ) ; + c7.addGeometry( part.clone() ); + c7.addGeometry( part.clone() ); + QCOMPARE( c7.numGeometries(), 2 ); + c7.clear(); + QVERIFY( c7.isEmpty() ); + QCOMPARE( c7.numGeometries(), 0 ); + QCOMPARE( c7.nCoordinates(), 0 ); + QCOMPARE( c7.ringCount(), 0 ); + QCOMPARE( c7.partCount(), 0 ); + QVERIFY( !c7.is3D() ); + QVERIFY( !c7.isMeasure() ); + QCOMPARE( c7.wkbType(), QgsWkbTypes::MultiCurve ); + + //clone + QgsMultiCurve c11; + std::unique_ptr< QgsMultiCurve >cloned( c11.clone() ); + QVERIFY( cloned->isEmpty() ); + c11.addGeometry( part.clone() ); + c11.addGeometry( part.clone() ); + cloned.reset( c11.clone() ); + QCOMPARE( cloned->numGeometries(), 2 ); + ls = static_cast< const QgsCircularString * >( cloned->geometryN( 0 ) ); + QCOMPARE( *ls, part ); + ls = static_cast< const QgsCircularString * >( cloned->geometryN( 1 ) ); + QCOMPARE( *ls, part ); + + //copy constructor + QgsMultiCurve c12; + QgsMultiCurve c13( c12 ); + QVERIFY( c13.isEmpty() ); + c12.addGeometry( part.clone() ); + c12.addGeometry( part.clone() ); + QgsMultiCurve c14( c12 ); + QCOMPARE( c14.numGeometries(), 2 ); + QCOMPARE( c14.wkbType(), QgsWkbTypes::MultiCurveZM ); + ls = static_cast< const QgsCircularString * >( c14.geometryN( 0 ) ); + QCOMPARE( *ls, part ); + ls = static_cast< const QgsCircularString * >( c14.geometryN( 1 ) ); + QCOMPARE( *ls, part ); + + //assignment operator + QgsMultiCurve c15; + c15 = c13; + QCOMPARE( c15.numGeometries(), 0 ); + c15 = c14; + QCOMPARE( c15.numGeometries(), 2 ); + ls = static_cast< const QgsCircularString * >( c15.geometryN( 0 ) ); + QCOMPARE( *ls, part ); + ls = static_cast< const QgsCircularString * >( c15.geometryN( 1 ) ); + QCOMPARE( *ls, part ); + + //toCurveType + std::unique_ptr< QgsMultiCurve > curveType( c12.toCurveType() ); + QCOMPARE( curveType->wkbType(), QgsWkbTypes::MultiCurveZM ); + QCOMPARE( curveType->numGeometries(), 2 ); + const QgsCircularString *curve = static_cast< const QgsCircularString * >( curveType->geometryN( 0 ) ); + QCOMPARE( *curve, *static_cast< const QgsCircularString * >( c12.geometryN( 0 ) ) ); + curve = static_cast< const QgsCircularString * >( curveType->geometryN( 1 ) ); + QCOMPARE( *curve, *static_cast< const QgsCircularString * >( c12.geometryN( 1 ) ) ); + + //to/fromWKB + QgsMultiCurve c16; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7, 17 ) << QgsPoint( QgsWkbTypes::Point, 3, 13 ) << QgsPoint( QgsWkbTypes::Point, 7, 11 ) ) ; + c16.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 27, 37 ) << QgsPoint( QgsWkbTypes::Point, 43, 43 ) << QgsPoint( QgsWkbTypes::Point, 27, 54 ) ) ; + c16.addGeometry( part.clone() ); + QByteArray wkb16 = c16.asWkb(); + QgsMultiCurve c17; + QgsConstWkbPtr wkb16ptr( wkb16 ); + c17.fromWkb( wkb16ptr ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsCircularString * >( c17.geometryN( 0 ) ), *static_cast< const QgsCircularString * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsCircularString * >( c17.geometryN( 1 ) ), *static_cast< const QgsCircularString * >( c16.geometryN( 1 ) ) ); + + //parts with Z + c16.clear(); + c17.clear(); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 7, 17, 1 ) << QgsPoint( QgsWkbTypes::PointZ, 3, 13, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 7, 11, 3 ) ) ; + c16.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 27, 37, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 43, 43, 5 ) << QgsPoint( QgsWkbTypes::PointZ, 27, 53, 6 ) ) ; + c16.addGeometry( part.clone() ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr2( wkb16 ); + c17.fromWkb( wkb16ptr2 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiCurveZ ); + QCOMPARE( *static_cast< const QgsCircularString * >( c17.geometryN( 0 ) ), *static_cast< const QgsCircularString * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsCircularString * >( c17.geometryN( 1 ) ), *static_cast< const QgsCircularString * >( c16.geometryN( 1 ) ) ); + + //parts with m + c16.clear(); + c17.clear(); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 7, 17, 0, 1 ) << QgsPoint( QgsWkbTypes::PointM, 3, 13, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 7, 11, 0, 5 ) ) ; + c16.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 27, 37, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 43, 43, 0, 5 ) << QgsPoint( QgsWkbTypes::PointM, 27, 53, 0, 7 ) ) ; + c16.addGeometry( part.clone() ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr3( wkb16 ); + c17.fromWkb( wkb16ptr3 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiCurveM ); + QCOMPARE( *static_cast< const QgsCircularString * >( c17.geometryN( 0 ) ), *static_cast< const QgsCircularString * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsCircularString * >( c17.geometryN( 1 ) ), *static_cast< const QgsCircularString * >( c16.geometryN( 1 ) ) ); + + // parts with ZM + c16.clear(); + c17.clear(); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 7, 17, 4, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 3, 13, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 7, 11, 2, 5 ) ) ; + c16.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 27, 37, 6, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 43, 43, 11, 5 ) << QgsPoint( QgsWkbTypes::PointZM, 27, 47, 12, 15 ) ) ; + c16.addGeometry( part.clone() ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr4( wkb16 ); + c17.fromWkb( wkb16ptr4 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiCurveZM ); + QCOMPARE( *static_cast< const QgsCircularString * >( c17.geometryN( 0 ) ), *static_cast< const QgsCircularString * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsCircularString * >( c17.geometryN( 1 ) ), *static_cast< const QgsCircularString * >( c16.geometryN( 1 ) ) ); + + //bad WKB - check for no crash + c17.clear(); + QgsConstWkbPtr nullPtr( nullptr, 0 ); + QVERIFY( !c17.fromWkb( nullPtr ) ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiCurve ); + QgsPoint point( 1, 2 ); + QByteArray wkbPoint = point.asWkb(); + QgsConstWkbPtr wkbPointPtr( wkbPoint ); + QVERIFY( !c17.fromWkb( wkbPointPtr ) ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiCurve ); + + //to/from WKT + QgsMultiCurve c18; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 7, 17, 4, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 3, 13, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 7, 11, 2, 8 ) ) ; + c18.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 27, 37, 6, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 43, 43, 11, 5 ) << QgsPoint( QgsWkbTypes::PointZM, 27, 53, 21, 52 ) ) ; + c18.addGeometry( part.clone() ); + + QString wkt = c18.asWkt(); + QVERIFY( !wkt.isEmpty() ); + QgsMultiCurve c19; + QVERIFY( c19.fromWkt( wkt ) ); + QCOMPARE( c19.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsCircularString * >( c19.geometryN( 0 ) ), *static_cast< const QgsCircularString * >( c18.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsCircularString * >( c19.geometryN( 1 ) ), *static_cast< const QgsCircularString * >( c18.geometryN( 1 ) ) ); + + //bad WKT + QgsMultiCurve c20; + QVERIFY( !c20.fromWkt( "Point()" ) ); + QVERIFY( c20.isEmpty() ); + QCOMPARE( c20.numGeometries(), 0 ); + QCOMPARE( c20.wkbType(), QgsWkbTypes::MultiCurve ); + + //as JSON + QgsMultiCurve exportC; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7, 17 ) << QgsPoint( QgsWkbTypes::Point, 3, 13 ) << QgsPoint( QgsWkbTypes::Point, 7, 11 ) ) ; + exportC.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 27, 37 ) << QgsPoint( QgsWkbTypes::Point, 43, 43 ) << QgsPoint( QgsWkbTypes::Point, 27, 47 ) ) ; + exportC.addGeometry( part.clone() ); + + // GML document for compare + QDomDocument doc( "gml" ); + + // as GML2 + QString expectedSimpleGML2( QStringLiteral( "7,17 6.9,17 6.9,17 6.8,17 6.8,17.1 6.7,17.1 6.7,17.1 6.6,17.1 6.6,17.1 6.5,17.1 6.5,17.1 6.4,17.1 6.4,17.1 6.3,17.1 6.2,17.2 6.2,17.2 6.1,17.2 6.1,17.2 6,17.2 6,17.2 5.9,17.2 5.9,17.2 5.8,17.2 5.7,17.2 5.7,17.1 5.6,17.1 5.6,17.1 5.5,17.1 5.5,17.1 5.4,17.1 5.4,17.1 5.3,17.1 5.3,17.1 5.2,17.1 5.2,17 5.1,17 5,17 5,17 4.9,17 4.9,17 4.8,16.9 4.8,16.9 4.7,16.9 4.7,16.9 4.6,16.9 4.6,16.8 4.5,16.8 4.5,16.8 4.4,16.8 4.4,16.7 4.3,16.7 4.3,16.7 4.3,16.6 4.2,16.6 4.2,16.6 4.1,16.5 4.1,16.5 4,16.5 4,16.4 3.9,16.4 3.9,16.4 3.9,16.3 3.8,16.3 3.8,16.3 3.7,16.2 3.7,16.2 3.7,16.1 3.6,16.1 3.6,16.1 3.6,16 3.5,16 3.5,15.9 3.5,15.9 3.4,15.8 3.4,15.8 3.4,15.7 3.3,15.7 3.3,15.7 3.3,15.6 3.2,15.6 3.2,15.5 3.2,15.5 3.2,15.4 3.1,15.4 3.1,15.3 3.1,15.3 3.1,15.2 3.1,15.2 3,15.1 3,15.1 3,15 3,15 3,14.9 3,14.8 2.9,14.8 2.9,14.7 2.9,14.7 2.9,14.6 2.9,14.6 2.9,14.5 2.9,14.5 2.9,14.4 2.9,14.4 2.9,14.3 2.8,14.2 2.8,14.2 2.8,14.1 2.8,14.1 2.8,14 2.8,14 2.8,13.9 2.8,13.9 2.8,13.8 2.8,13.8 2.9,13.7 2.9,13.6 2.9,13.6 2.9,13.5 2.9,13.5 2.9,13.4 2.9,13.4 2.9,13.3 2.9,13.3 2.9,13.2 3,13.2 3,13.1 3,13 3,13 3,12.9 3,12.9 3.1,12.8 3.1,12.8 3.1,12.7 3.1,12.7 3.1,12.6 3.2,12.6 3.2,12.5 3.2,12.5 3.2,12.4 3.3,12.4 3.3,12.3 3.3,12.3 3.4,12.3 3.4,12.2 3.4,12.2 3.5,12.1 3.5,12.1 3.5,12 3.6,12 3.6,11.9 3.6,11.9 3.7,11.9 3.7,11.8 3.7,11.8 3.8,11.7 3.8,11.7 3.9,11.7 3.9,11.6 3.9,11.6 4,11.6 4,11.5 4.1,11.5 4.1,11.5 4.2,11.4 4.2,11.4 4.3,11.4 4.3,11.3 4.3,11.3 4.4,11.3 4.4,11.2 4.5,11.2 4.5,11.2 4.6,11.2 4.6,11.1 4.7,11.1 4.7,11.1 4.8,11.1 4.8,11.1 4.9,11 4.9,11 5,11 5,11 5.1,11 5.2,11 5.2,10.9 5.3,10.9 5.3,10.9 5.4,10.9 5.4,10.9 5.5,10.9 5.5,10.9 5.6,10.9 5.6,10.9 5.7,10.9 5.7,10.8 5.8,10.8 5.9,10.8 5.9,10.8 6,10.8 6,10.8 6.1,10.8 6.1,10.8 6.2,10.8 6.2,10.8 6.3,10.9 6.4,10.9 6.4,10.9 6.5,10.9 6.5,10.9 6.6,10.9 6.6,10.9 6.7,10.9 6.7,10.9 6.8,10.9 6.8,11 6.9,11 6.9,11 7,1127,37 27.1,36.9 27.2,36.8 27.3,36.6 27.4,36.5 27.5,36.4 27.6,36.3 27.7,36.2 27.8,36 27.9,35.9 28,35.8 28.1,35.7 28.2,35.6 28.3,35.5 28.4,35.4 28.5,35.3 28.7,35.2 28.8,35.1 28.9,35 29,34.9 29.1,34.8 29.3,34.7 29.4,34.6 29.5,34.6 29.7,34.5 29.8,34.4 29.9,34.3 30.1,34.3 30.2,34.2 30.3,34.1 30.5,34 30.6,34 30.7,33.9 30.9,33.9 31,33.8 31.2,33.7 31.3,33.7 31.5,33.6 31.6,33.6 31.8,33.6 31.9,33.5 32.1,33.5 32.2,33.4 32.4,33.4 32.5,33.4 32.7,33.3 32.8,33.3 33,33.3 33.1,33.3 33.3,33.2 33.4,33.2 33.6,33.2 33.7,33.2 33.9,33.2 34,33.2 34.2,33.2 34.3,33.2 34.5,33.2 34.6,33.2 34.8,33.2 34.9,33.2 35.1,33.2 35.3,33.3 35.4,33.3 35.6,33.3 35.7,33.3 35.9,33.3 36,33.4 36.2,33.4 36.3,33.4 36.5,33.5 36.6,33.5 36.8,33.6 36.9,33.6 37.1,33.7 37.2,33.7 37.3,33.8 37.5,33.8 37.6,33.9 37.8,33.9 37.9,34 38,34.1 38.2,34.1 38.3,34.2 38.5,34.3 38.6,34.3 38.7,34.4 38.9,34.5 39,34.6 39.1,34.7 39.2,34.7 39.4,34.8 39.5,34.9 39.6,35 39.7,35.1 39.9,35.2 40,35.3 40.1,35.4 40.2,35.5 40.3,35.6 40.4,35.7 40.5,35.8 40.6,35.9 40.7,36.1 40.8,36.2 40.9,36.3 41,36.4 41.1,36.5 41.2,36.6 41.3,36.8 41.4,36.9 41.5,37 41.6,37.1 41.7,37.3 41.8,37.4 41.8,37.5 41.9,37.7 42,37.8 42.1,37.9 42.1,38.1 42.2,38.2 42.3,38.3 42.3,38.5 42.4,38.6 42.4,38.8 42.5,38.9 42.6,39.1 42.6,39.2 42.6,39.4 42.7,39.5 42.7,39.6 42.8,39.8 42.8,39.9 42.8,40.1 42.9,40.2 42.9,40.4 42.9,40.5 43,40.7 43,40.9 43,41 43,41.2 43,41.3 43,41.5 43,41.6 43.1,41.8 43.1,41.9 43.1,42.1 43.1,42.2 43,42.4 43,42.5 43,42.7 43,42.8 43,43 43,43.1 43,43.3 42.9,43.5 42.9,43.6 42.9,43.8 42.8,43.9 42.8,44.1 42.8,44.2 42.7,44.4 42.7,44.5 42.6,44.6 42.6,44.8 42.6,44.9 42.5,45.1 42.4,45.2 42.4,45.4 42.3,45.5 42.3,45.7 42.2,45.8 42.1,45.9 42.1,46.1 42,46.2 41.9,46.3 41.8,46.5 41.8,46.6 41.7,46.7 41.6,46.9 41.5,47 41.4,47.1 41.3,47.2 41.2,47.4 41.1,47.5 41,47.6 40.9,47.7 40.8,47.8 40.7,47.9 40.6,48.1 40.5,48.2 40.4,48.3 40.3,48.4 40.2,48.5 40.1,48.6 40,48.7 39.9,48.8 39.7,48.9 39.6,49 39.5,49.1 39.4,49.2 39.2,49.3 39.1,49.3 39,49.4 38.9,49.5 38.7,49.6 38.6,49.7 38.5,49.7 38.3,49.8 38.2,49.9 38,49.9 37.9,50 37.8,50.1 37.6,50.1 37.5,50.2 37.3,50.2 37.2,50.3 37.1,50.3 36.9,50.4 36.8,50.4 36.6,50.5 36.5,50.5 36.3,50.6 36.2,50.6 36,50.6 35.9,50.7 35.7,50.7 35.6,50.7 35.4,50.7 35.3,50.7 35.1,50.8 34.9,50.8 34.8,50.8 34.6,50.8 34.5,50.8 34.3,50.8 34.2,50.8 34,50.8 33.9,50.8 33.7,50.8 33.6,50.8 33.4,50.8 33.3,50.8 33.1,50.7 33,50.7 32.8,50.7 32.7,50.7 32.5,50.6 32.4,50.6 32.2,50.6 32.1,50.5 31.9,50.5 31.8,50.4 31.6,50.4 31.5,50.4 31.3,50.3 31.2,50.3 31,50.2 30.9,50.1 30.7,50.1 30.6,50 30.5,50 30.3,49.9 30.2,49.8 30.1,49.7 29.9,49.7 29.8,49.6 29.7,49.5 29.5,49.4 29.4,49.4 29.3,49.3 29.1,49.2 29,49.1 28.9,49 28.8,48.9 28.7,48.8 28.5,48.7 28.4,48.6 28.3,48.5 28.2,48.4 28.1,48.3 28,48.2 27.9,48.1 27.8,48 27.7,47.8 27.6,47.7 27.5,47.6 27.4,47.5 27.3,47.4 27.2,47.2 27.1,47.1 27,47" ) ); + QString res = elemToString( exportC.asGML2( doc, 1 ) ); + QGSCOMPAREGML( res, expectedSimpleGML2 ); + + //as GML3 + QString expectedSimpleGML3( QStringLiteral( "7 17 3 13 7 1127 37 43 43 27 47" ) ); + res = elemToString( exportC.asGML3( doc ) ); + QCOMPARE( res, expectedSimpleGML3 ); + + // as JSON + QString expectedSimpleJson( "{\"type\": \"MultiLineString\", \"coordinates\": [[ [7, 17], [6.9, 17], [6.9, 17], [6.8, 17], [6.8, 17.1], [6.7, 17.1], [6.7, 17.1], [6.6, 17.1], [6.6, 17.1], [6.5, 17.1], [6.5, 17.1], [6.4, 17.1], [6.4, 17.1], [6.3, 17.1], [6.2, 17.2], [6.2, 17.2], [6.1, 17.2], [6.1, 17.2], [6, 17.2], [6, 17.2], [5.9, 17.2], [5.9, 17.2], [5.8, 17.2], [5.7, 17.2], [5.7, 17.1], [5.6, 17.1], [5.6, 17.1], [5.5, 17.1], [5.5, 17.1], [5.4, 17.1], [5.4, 17.1], [5.3, 17.1], [5.3, 17.1], [5.2, 17.1], [5.2, 17], [5.1, 17], [5, 17], [5, 17], [4.9, 17], [4.9, 17], [4.8, 16.9], [4.8, 16.9], [4.7, 16.9], [4.7, 16.9], [4.6, 16.9], [4.6, 16.8], [4.5, 16.8], [4.5, 16.8], [4.4, 16.8], [4.4, 16.7], [4.3, 16.7], [4.3, 16.7], [4.3, 16.6], [4.2, 16.6], [4.2, 16.6], [4.1, 16.5], [4.1, 16.5], [4, 16.5], [4, 16.4], [3.9, 16.4], [3.9, 16.4], [3.9, 16.3], [3.8, 16.3], [3.8, 16.3], [3.7, 16.2], [3.7, 16.2], [3.7, 16.1], [3.6, 16.1], [3.6, 16.1], [3.6, 16], [3.5, 16], [3.5, 15.9], [3.5, 15.9], [3.4, 15.8], [3.4, 15.8], [3.4, 15.7], [3.3, 15.7], [3.3, 15.7], [3.3, 15.6], [3.2, 15.6], [3.2, 15.5], [3.2, 15.5], [3.2, 15.4], [3.1, 15.4], [3.1, 15.3], [3.1, 15.3], [3.1, 15.2], [3.1, 15.2], [3, 15.1], [3, 15.1], [3, 15], [3, 15], [3, 14.9], [3, 14.8], [2.9, 14.8], [2.9, 14.7], [2.9, 14.7], [2.9, 14.6], [2.9, 14.6], [2.9, 14.5], [2.9, 14.5], [2.9, 14.4], [2.9, 14.4], [2.9, 14.3], [2.8, 14.2], [2.8, 14.2], [2.8, 14.1], [2.8, 14.1], [2.8, 14], [2.8, 14], [2.8, 13.9], [2.8, 13.9], [2.8, 13.8], [2.8, 13.8], [2.9, 13.7], [2.9, 13.6], [2.9, 13.6], [2.9, 13.5], [2.9, 13.5], [2.9, 13.4], [2.9, 13.4], [2.9, 13.3], [2.9, 13.3], [2.9, 13.2], [3, 13.2], [3, 13.1], [3, 13], [3, 13], [3, 12.9], [3, 12.9], [3.1, 12.8], [3.1, 12.8], [3.1, 12.7], [3.1, 12.7], [3.1, 12.6], [3.2, 12.6], [3.2, 12.5], [3.2, 12.5], [3.2, 12.4], [3.3, 12.4], [3.3, 12.3], [3.3, 12.3], [3.4, 12.3], [3.4, 12.2], [3.4, 12.2], [3.5, 12.1], [3.5, 12.1], [3.5, 12], [3.6, 12], [3.6, 11.9], [3.6, 11.9], [3.7, 11.9], [3.7, 11.8], [3.7, 11.8], [3.8, 11.7], [3.8, 11.7], [3.9, 11.7], [3.9, 11.6], [3.9, 11.6], [4, 11.6], [4, 11.5], [4.1, 11.5], [4.1, 11.5], [4.2, 11.4], [4.2, 11.4], [4.3, 11.4], [4.3, 11.3], [4.3, 11.3], [4.4, 11.3], [4.4, 11.2], [4.5, 11.2], [4.5, 11.2], [4.6, 11.2], [4.6, 11.1], [4.7, 11.1], [4.7, 11.1], [4.8, 11.1], [4.8, 11.1], [4.9, 11], [4.9, 11], [5, 11], [5, 11], [5.1, 11], [5.2, 11], [5.2, 10.9], [5.3, 10.9], [5.3, 10.9], [5.4, 10.9], [5.4, 10.9], [5.5, 10.9], [5.5, 10.9], [5.6, 10.9], [5.6, 10.9], [5.7, 10.9], [5.7, 10.8], [5.8, 10.8], [5.9, 10.8], [5.9, 10.8], [6, 10.8], [6, 10.8], [6.1, 10.8], [6.1, 10.8], [6.2, 10.8], [6.2, 10.8], [6.3, 10.9], [6.4, 10.9], [6.4, 10.9], [6.5, 10.9], [6.5, 10.9], [6.6, 10.9], [6.6, 10.9], [6.7, 10.9], [6.7, 10.9], [6.8, 10.9], [6.8, 11], [6.9, 11], [6.9, 11], [7, 11]], [ [27, 37], [27.1, 36.9], [27.2, 36.8], [27.3, 36.6], [27.4, 36.5], [27.5, 36.4], [27.6, 36.3], [27.7, 36.2], [27.8, 36], [27.9, 35.9], [28, 35.8], [28.1, 35.7], [28.2, 35.6], [28.3, 35.5], [28.4, 35.4], [28.5, 35.3], [28.7, 35.2], [28.8, 35.1], [28.9, 35], [29, 34.9], [29.1, 34.8], [29.3, 34.7], [29.4, 34.6], [29.5, 34.6], [29.7, 34.5], [29.8, 34.4], [29.9, 34.3], [30.1, 34.3], [30.2, 34.2], [30.3, 34.1], [30.5, 34], [30.6, 34], [30.7, 33.9], [30.9, 33.9], [31, 33.8], [31.2, 33.7], [31.3, 33.7], [31.5, 33.6], [31.6, 33.6], [31.8, 33.6], [31.9, 33.5], [32.1, 33.5], [32.2, 33.4], [32.4, 33.4], [32.5, 33.4], [32.7, 33.3], [32.8, 33.3], [33, 33.3], [33.1, 33.3], [33.3, 33.2], [33.4, 33.2], [33.6, 33.2], [33.7, 33.2], [33.9, 33.2], [34, 33.2], [34.2, 33.2], [34.3, 33.2], [34.5, 33.2], [34.6, 33.2], [34.8, 33.2], [34.9, 33.2], [35.1, 33.2], [35.3, 33.3], [35.4, 33.3], [35.6, 33.3], [35.7, 33.3], [35.9, 33.3], [36, 33.4], [36.2, 33.4], [36.3, 33.4], [36.5, 33.5], [36.6, 33.5], [36.8, 33.6], [36.9, 33.6], [37.1, 33.7], [37.2, 33.7], [37.3, 33.8], [37.5, 33.8], [37.6, 33.9], [37.8, 33.9], [37.9, 34], [38, 34.1], [38.2, 34.1], [38.3, 34.2], [38.5, 34.3], [38.6, 34.3], [38.7, 34.4], [38.9, 34.5], [39, 34.6], [39.1, 34.7], [39.2, 34.7], [39.4, 34.8], [39.5, 34.9], [39.6, 35], [39.7, 35.1], [39.9, 35.2], [40, 35.3], [40.1, 35.4], [40.2, 35.5], [40.3, 35.6], [40.4, 35.7], [40.5, 35.8], [40.6, 35.9], [40.7, 36.1], [40.8, 36.2], [40.9, 36.3], [41, 36.4], [41.1, 36.5], [41.2, 36.6], [41.3, 36.8], [41.4, 36.9], [41.5, 37], [41.6, 37.1], [41.7, 37.3], [41.8, 37.4], [41.8, 37.5], [41.9, 37.7], [42, 37.8], [42.1, 37.9], [42.1, 38.1], [42.2, 38.2], [42.3, 38.3], [42.3, 38.5], [42.4, 38.6], [42.4, 38.8], [42.5, 38.9], [42.6, 39.1], [42.6, 39.2], [42.6, 39.4], [42.7, 39.5], [42.7, 39.6], [42.8, 39.8], [42.8, 39.9], [42.8, 40.1], [42.9, 40.2], [42.9, 40.4], [42.9, 40.5], [43, 40.7], [43, 40.9], [43, 41], [43, 41.2], [43, 41.3], [43, 41.5], [43, 41.6], [43.1, 41.8], [43.1, 41.9], [43.1, 42.1], [43.1, 42.2], [43, 42.4], [43, 42.5], [43, 42.7], [43, 42.8], [43, 43], [43, 43.1], [43, 43.3], [42.9, 43.5], [42.9, 43.6], [42.9, 43.8], [42.8, 43.9], [42.8, 44.1], [42.8, 44.2], [42.7, 44.4], [42.7, 44.5], [42.6, 44.6], [42.6, 44.8], [42.6, 44.9], [42.5, 45.1], [42.4, 45.2], [42.4, 45.4], [42.3, 45.5], [42.3, 45.7], [42.2, 45.8], [42.1, 45.9], [42.1, 46.1], [42, 46.2], [41.9, 46.3], [41.8, 46.5], [41.8, 46.6], [41.7, 46.7], [41.6, 46.9], [41.5, 47], [41.4, 47.1], [41.3, 47.2], [41.2, 47.4], [41.1, 47.5], [41, 47.6], [40.9, 47.7], [40.8, 47.8], [40.7, 47.9], [40.6, 48.1], [40.5, 48.2], [40.4, 48.3], [40.3, 48.4], [40.2, 48.5], [40.1, 48.6], [40, 48.7], [39.9, 48.8], [39.7, 48.9], [39.6, 49], [39.5, 49.1], [39.4, 49.2], [39.2, 49.3], [39.1, 49.3], [39, 49.4], [38.9, 49.5], [38.7, 49.6], [38.6, 49.7], [38.5, 49.7], [38.3, 49.8], [38.2, 49.9], [38, 49.9], [37.9, 50], [37.8, 50.1], [37.6, 50.1], [37.5, 50.2], [37.3, 50.2], [37.2, 50.3], [37.1, 50.3], [36.9, 50.4], [36.8, 50.4], [36.6, 50.5], [36.5, 50.5], [36.3, 50.6], [36.2, 50.6], [36, 50.6], [35.9, 50.7], [35.7, 50.7], [35.6, 50.7], [35.4, 50.7], [35.3, 50.7], [35.1, 50.8], [34.9, 50.8], [34.8, 50.8], [34.6, 50.8], [34.5, 50.8], [34.3, 50.8], [34.2, 50.8], [34, 50.8], [33.9, 50.8], [33.7, 50.8], [33.6, 50.8], [33.4, 50.8], [33.3, 50.8], [33.1, 50.7], [33, 50.7], [32.8, 50.7], [32.7, 50.7], [32.5, 50.6], [32.4, 50.6], [32.2, 50.6], [32.1, 50.5], [31.9, 50.5], [31.8, 50.4], [31.6, 50.4], [31.5, 50.4], [31.3, 50.3], [31.2, 50.3], [31, 50.2], [30.9, 50.1], [30.7, 50.1], [30.6, 50], [30.5, 50], [30.3, 49.9], [30.2, 49.8], [30.1, 49.7], [29.9, 49.7], [29.8, 49.6], [29.7, 49.5], [29.5, 49.4], [29.4, 49.4], [29.3, 49.3], [29.1, 49.2], [29, 49.1], [28.9, 49], [28.8, 48.9], [28.7, 48.8], [28.5, 48.7], [28.4, 48.6], [28.3, 48.5], [28.2, 48.4], [28.1, 48.3], [28, 48.2], [27.9, 48.1], [27.8, 48], [27.7, 47.8], [27.6, 47.7], [27.5, 47.6], [27.4, 47.5], [27.3, 47.4], [27.2, 47.2], [27.1, 47.1], [27, 47]]] }" ); + res = exportC.asJSON( 1 ); + QCOMPARE( res, expectedSimpleJson ); + + QgsMultiCurve exportFloat; + part.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, 11 / 3.0 ) ) ; + exportFloat.addGeometry( part.clone() ); + part.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, 1 / 3.0 ) ) ; + exportFloat.addGeometry( part.clone() ); + + QString expectedJsonPrec3( QStringLiteral( "{\"type\": \"MultiLineString\", \"coordinates\": [[ [2.333, 5.667], [2.316, 5.677], [2.298, 5.687], [2.28, 5.697], [2.262, 5.707], [2.244, 5.716], [2.226, 5.725], [2.207, 5.734], [2.188, 5.742], [2.17, 5.75], [2.151, 5.757], [2.131, 5.765], [2.112, 5.772], [2.093, 5.778], [2.074, 5.785], [2.054, 5.79], [2.034, 5.796], [2.015, 5.801], [1.995, 5.806], [1.975, 5.811], [1.955, 5.815], [1.935, 5.819], [1.915, 5.822], [1.894, 5.826], [1.874, 5.828], [1.854, 5.831], [1.834, 5.833], [1.813, 5.835], [1.793, 5.836], [1.773, 5.837], [1.752, 5.838], [1.732, 5.838], [1.711, 5.838], [1.691, 5.838], [1.67, 5.837], [1.65, 5.836], [1.63, 5.834], [1.609, 5.833], [1.589, 5.83], [1.569, 5.828], [1.548, 5.825], [1.528, 5.822], [1.508, 5.818], [1.488, 5.814], [1.468, 5.81], [1.448, 5.805], [1.428, 5.801], [1.409, 5.795], [1.389, 5.79], [1.37, 5.784], [1.35, 5.777], [1.331, 5.771], [1.312, 5.764], [1.293, 5.756], [1.274, 5.749], [1.255, 5.741], [1.236, 5.732], [1.218, 5.724], [1.199, 5.715], [1.181, 5.705], [1.163, 5.696], [1.145, 5.686], [1.128, 5.676], [1.11, 5.665], [1.093, 5.654], [1.076, 5.643], [1.059, 5.632], [1.042, 5.62], [1.025, 5.608], [1.009, 5.596], [0.993, 5.583], [0.977, 5.57], [0.962, 5.557], [0.946, 5.543], [0.931, 5.53], [0.916, 5.516], [0.901, 5.502], [0.887, 5.487], [0.873, 5.473], [0.859, 5.458], [0.845, 5.442], [0.832, 5.427], [0.819, 5.411], [0.806, 5.395], [0.793, 5.379], [0.781, 5.363], [0.769, 5.346], [0.757, 5.33], [0.746, 5.313], [0.735, 5.296], [0.724, 5.278], [0.713, 5.261], [0.703, 5.243], [0.693, 5.225], [0.684, 5.207], [0.674, 5.189], [0.666, 5.171], [0.657, 5.152], [0.649, 5.133], [0.641, 5.115], [0.633, 5.096], [0.626, 5.077], [0.619, 5.057], [0.612, 5.038], [0.606, 5.019], [0.6, 4.999], [0.594, 4.979], [0.589, 4.96], [0.584, 4.94], [0.579, 4.92], [0.575, 4.9], [0.571, 4.88], [0.568, 4.86], [0.564, 4.84], [0.562, 4.819], [0.559, 4.799], [0.557, 4.779], [0.555, 4.759], [0.554, 4.738], [0.553, 4.718], [0.552, 4.697], [0.552, 4.677], [0.552, 4.656], [0.552, 4.636], [0.553, 4.616], [0.554, 4.595], [0.555, 4.575], [0.557, 4.554], [0.559, 4.534], [0.562, 4.514], [0.564, 4.494], [0.568, 4.473], [0.571, 4.453], [0.575, 4.433], [0.579, 4.413], [0.584, 4.393], [0.589, 4.374], [0.594, 4.354], [0.6, 4.334], [0.606, 4.315], [0.612, 4.295], [0.619, 4.276], [0.626, 4.257], [0.633, 4.238], [0.641, 4.219], [0.649, 4.2], [0.657, 4.181], [0.666, 4.163], [0.674, 4.144], [0.684, 4.126], [0.693, 4.108], [0.703, 4.09], [0.713, 4.073], [0.724, 4.055], [0.735, 4.038], [0.746, 4.021], [0.757, 4.004], [0.769, 3.987], [0.781, 3.97], [0.793, 3.954], [0.806, 3.938], [0.819, 3.922], [0.832, 3.906], [0.845, 3.891], [0.859, 3.876], [0.873, 3.861], [0.887, 3.846], [0.901, 3.832], [0.916, 3.817], [0.931, 3.804], [0.946, 3.79], [0.962, 3.776], [0.977, 3.763], [0.993, 3.75], [1.009, 3.738], [1.025, 3.726], [1.042, 3.713], [1.059, 3.702], [1.076, 3.69], [1.093, 3.679], [1.11, 3.668], [1.128, 3.658], [1.145, 3.648], [1.163, 3.638], [1.181, 3.628], [1.199, 3.619], [1.218, 3.61], [1.236, 3.601], [1.255, 3.593], [1.274, 3.585], [1.293, 3.577], [1.312, 3.57], [1.331, 3.563], [1.35, 3.556], [1.37, 3.55], [1.389, 3.544], [1.409, 3.538], [1.428, 3.533], [1.448, 3.528], [1.468, 3.523], [1.488, 3.519], [1.508, 3.515], [1.528, 3.511], [1.548, 3.508], [1.569, 3.505], [1.589, 3.503], [1.609, 3.501], [1.63, 3.499], [1.65, 3.497], [1.67, 3.496], [1.691, 3.496], [1.711, 3.495], [1.732, 3.495], [1.752, 3.496], [1.773, 3.496], [1.793, 3.497], [1.813, 3.499], [1.834, 3.5], [1.854, 3.503], [1.874, 3.505], [1.894, 3.508], [1.915, 3.511], [1.935, 3.514], [1.955, 3.518], [1.975, 3.523], [1.995, 3.527], [2.015, 3.532], [2.034, 3.537], [2.054, 3.543], [2.074, 3.549], [2.093, 3.555], [2.112, 3.562], [2.131, 3.569], [2.151, 3.576], [2.17, 3.584], [2.188, 3.592], [2.207, 3.6], [2.226, 3.608], [2.244, 3.617], [2.262, 3.627], [2.28, 3.636], [2.298, 3.646], [2.316, 3.656], [2.333, 3.667]], [ [9, 4.111], [8.966, 4.178], [8.932, 4.244], [8.896, 4.309], [8.859, 4.374], [8.821, 4.438], [8.782, 4.502], [8.742, 4.565], [8.7, 4.627], [8.658, 4.688], [8.614, 4.749], [8.57, 4.809], [8.524, 4.868], [8.477, 4.926], [8.43, 4.983], [8.381, 5.04], [8.331, 5.096], [8.281, 5.151], [8.229, 5.205], [8.177, 5.258], [8.124, 5.31], [8.069, 5.361], [8.014, 5.411], [7.958, 5.461], [7.901, 5.509], [7.844, 5.556], [7.785, 5.603], [7.726, 5.648], [7.666, 5.692], [7.605, 5.735], [7.543, 5.777], [7.481, 5.818], [7.418, 5.858], [7.354, 5.897], [7.29, 5.935], [7.225, 5.971], [7.159, 6.007], [7.093, 6.041], [7.026, 6.074], [6.958, 6.106], [6.89, 6.137], [6.822, 6.167], [6.753, 6.195], [6.683, 6.222], [6.613, 6.248], [6.543, 6.273], [6.472, 6.296], [6.401, 6.319], [6.329, 6.34], [6.257, 6.36], [6.185, 6.378], [6.112, 6.395], [6.04, 6.411], [5.966, 6.426], [5.893, 6.44], [5.819, 6.452], [5.746, 6.463], [5.672, 6.472], [5.597, 6.48], [5.523, 6.487], [5.449, 6.493], [5.374, 6.498], [5.3, 6.501], [5.225, 6.503], [5.15, 6.503], [5.076, 6.502], [5.001, 6.5], [4.927, 6.497], [4.852, 6.492], [4.778, 6.486], [4.704, 6.479], [4.629, 6.47], [4.555, 6.46], [4.482, 6.449], [4.408, 6.437], [4.335, 6.423], [4.262, 6.408], [4.189, 6.392], [4.116, 6.374], [4.044, 6.355], [3.972, 6.335], [3.901, 6.314], [3.83, 6.292], [3.759, 6.268], [3.688, 6.243], [3.619, 6.217], [3.549, 6.189], [3.48, 6.16], [3.412, 6.131], [3.344, 6.1], [3.277, 6.067], [3.21, 6.034], [3.144, 5.999], [3.078, 5.964], [3.013, 5.927], [2.949, 5.889], [2.886, 5.85], [2.823, 5.81], [2.761, 5.768], [2.699, 5.726], [2.638, 5.683], [2.578, 5.638], [2.519, 5.593], [2.461, 5.546], [2.403, 5.499], [2.347, 5.45], [2.291, 5.401], [2.236, 5.35], [2.182, 5.299], [2.129, 5.246], [2.076, 5.193], [2.025, 5.139], [1.975, 5.084], [1.925, 5.028], [1.877, 4.971], [1.829, 4.914], [1.783, 4.855], [1.738, 4.796], [1.693, 4.736], [1.65, 4.675], [1.608, 4.614], [1.567, 4.551], [1.527, 4.488], [1.488, 4.425], [1.45, 4.36], [1.413, 4.295], [1.378, 4.23], [1.343, 4.164], [1.31, 4.097], [1.278, 4.029], [1.247, 3.961], [1.217, 3.893], [1.189, 3.824], [1.161, 3.755], [1.135, 3.685], [1.11, 3.614], [1.087, 3.544], [1.064, 3.472], [1.043, 3.401], [1.023, 3.329], [1.004, 3.257], [0.987, 3.184], [0.971, 3.111], [0.956, 3.038], [0.942, 2.965], [0.93, 2.891], [0.919, 2.817], [0.909, 2.743], [0.901, 2.669], [0.894, 2.595], [0.888, 2.52], [0.883, 2.446], [0.88, 2.371], [0.878, 2.297], [0.878, 2.222], [0.878, 2.148], [0.88, 2.073], [0.883, 1.998], [0.888, 1.924], [0.894, 1.85], [0.901, 1.775], [0.909, 1.701], [0.919, 1.627], [0.93, 1.553], [0.942, 1.48], [0.956, 1.406], [0.971, 1.333], [0.987, 1.26], [1.004, 1.188], [1.023, 1.116], [1.043, 1.044], [1.064, 0.972], [1.087, 0.901], [1.11, 0.83], [1.135, 0.76], [1.161, 0.69], [1.189, 0.62], [1.217, 0.551], [1.247, 0.483], [1.278, 0.415], [1.31, 0.348], [1.343, 0.281], [1.378, 0.215], [1.413, 0.149], [1.45, 0.084], [1.488, 0.02], [1.527, -0.044], [1.567, -0.107], [1.608, -0.169], [1.65, -0.231], [1.693, -0.291], [1.738, -0.351], [1.783, -0.411], [1.829, -0.469], [1.877, -0.527], [1.925, -0.584], [1.975, -0.639], [2.025, -0.694], [2.076, -0.749], [2.129, -0.802], [2.182, -0.854], [2.236, -0.906], [2.291, -0.956], [2.347, -1.006], [2.403, -1.054], [2.461, -1.102], [2.519, -1.148], [2.578, -1.194], [2.638, -1.238], [2.699, -1.282], [2.761, -1.324], [2.823, -1.365], [2.886, -1.405], [2.949, -1.444], [3.013, -1.482], [3.078, -1.519], [3.144, -1.555], [3.21, -1.589], [3.277, -1.623], [3.344, -1.655], [3.412, -1.686], [3.48, -1.716], [3.549, -1.745], [3.619, -1.772], [3.688, -1.798], [3.759, -1.823], [3.83, -1.847], [3.901, -1.87], [3.972, -1.891], [4.044, -1.911], [4.116, -1.93], [4.189, -1.947], [4.262, -1.964], [4.335, -1.979], [4.408, -1.992], [4.482, -2.005], [4.555, -2.016], [4.629, -2.026], [4.704, -2.034], [4.778, -2.042], [4.852, -2.048], [4.927, -2.052], [5.001, -2.056], [5.076, -2.058], [5.15, -2.059], [5.225, -2.058], [5.3, -2.056], [5.374, -2.053], [5.449, -2.049], [5.523, -2.043], [5.597, -2.036], [5.672, -2.028], [5.746, -2.018], [5.819, -2.007], [5.893, -1.995], [5.966, -1.982], [6.04, -1.967], [6.112, -1.951], [6.185, -1.934], [6.257, -1.915], [6.329, -1.895], [6.401, -1.874], [6.472, -1.852], [6.543, -1.829], [6.613, -1.804], [6.683, -1.778], [6.753, -1.751], [6.822, -1.722], [6.89, -1.693], [6.958, -1.662], [7.026, -1.63], [7.093, -1.597], [7.159, -1.562], [7.225, -1.527], [7.29, -1.49], [7.354, -1.453], [7.418, -1.414], [7.481, -1.374], [7.543, -1.333], [7.605, -1.291], [7.666, -1.248], [7.726, -1.203], [7.785, -1.158], [7.844, -1.112], [7.901, -1.065], [7.958, -1.016], [8.014, -0.967], [8.069, -0.917], [8.124, -0.865], [8.177, -0.813], [8.229, -0.76], [8.281, -0.706], [8.331, -0.651], [8.381, -0.596], [8.43, -0.539], [8.477, -0.482], [8.524, -0.423], [8.57, -0.364], [8.614, -0.304], [8.658, -0.244], [8.7, -0.182], [8.742, -0.12], [8.782, -0.057], [8.821, 0.006], [8.859, 0.07], [8.896, 0.135], [8.932, 0.201], [8.966, 0.267], [9, 0.333]]] }" ) ); + res = exportFloat.asJSON( 3 ); + QCOMPARE( res, expectedJsonPrec3 ); + + // as GML2 + QString expectedGML2prec3( QStringLiteral( "2.333,5.667 2.316,5.677 2.298,5.687 2.28,5.697 2.262,5.707 2.244,5.716 2.226,5.725 2.207,5.734 2.188,5.742 2.17,5.75 2.151,5.757 2.131,5.765 2.112,5.772 2.093,5.778 2.074,5.785 2.054,5.79 2.034,5.796 2.015,5.801 1.995,5.806 1.975,5.811 1.955,5.815 1.935,5.819 1.915,5.822 1.894,5.826 1.874,5.828 1.854,5.831 1.834,5.833 1.813,5.835 1.793,5.836 1.773,5.837 1.752,5.838 1.732,5.838 1.711,5.838 1.691,5.838 1.67,5.837 1.65,5.836 1.63,5.834 1.609,5.833 1.589,5.83 1.569,5.828 1.548,5.825 1.528,5.822 1.508,5.818 1.488,5.814 1.468,5.81 1.448,5.805 1.428,5.801 1.409,5.795 1.389,5.79 1.37,5.784 1.35,5.777 1.331,5.771 1.312,5.764 1.293,5.756 1.274,5.749 1.255,5.741 1.236,5.732 1.218,5.724 1.199,5.715 1.181,5.705 1.163,5.696 1.145,5.686 1.128,5.676 1.11,5.665 1.093,5.654 1.076,5.643 1.059,5.632 1.042,5.62 1.025,5.608 1.009,5.596 0.993,5.583 0.977,5.57 0.962,5.557 0.946,5.543 0.931,5.53 0.916,5.516 0.901,5.502 0.887,5.487 0.873,5.473 0.859,5.458 0.845,5.442 0.832,5.427 0.819,5.411 0.806,5.395 0.793,5.379 0.781,5.363 0.769,5.346 0.757,5.33 0.746,5.313 0.735,5.296 0.724,5.278 0.713,5.261 0.703,5.243 0.693,5.225 0.684,5.207 0.674,5.189 0.666,5.171 0.657,5.152 0.649,5.133 0.641,5.115 0.633,5.096 0.626,5.077 0.619,5.057 0.612,5.038 0.606,5.019 0.6,4.999 0.594,4.979 0.589,4.96 0.584,4.94 0.579,4.92 0.575,4.9 0.571,4.88 0.568,4.86 0.564,4.84 0.562,4.819 0.559,4.799 0.557,4.779 0.555,4.759 0.554,4.738 0.553,4.718 0.552,4.697 0.552,4.677 0.552,4.656 0.552,4.636 0.553,4.616 0.554,4.595 0.555,4.575 0.557,4.554 0.559,4.534 0.562,4.514 0.564,4.494 0.568,4.473 0.571,4.453 0.575,4.433 0.579,4.413 0.584,4.393 0.589,4.374 0.594,4.354 0.6,4.334 0.606,4.315 0.612,4.295 0.619,4.276 0.626,4.257 0.633,4.238 0.641,4.219 0.649,4.2 0.657,4.181 0.666,4.163 0.674,4.144 0.684,4.126 0.693,4.108 0.703,4.09 0.713,4.073 0.724,4.055 0.735,4.038 0.746,4.021 0.757,4.004 0.769,3.987 0.781,3.97 0.793,3.954 0.806,3.938 0.819,3.922 0.832,3.906 0.845,3.891 0.859,3.876 0.873,3.861 0.887,3.846 0.901,3.832 0.916,3.817 0.931,3.804 0.946,3.79 0.962,3.776 0.977,3.763 0.993,3.75 1.009,3.738 1.025,3.726 1.042,3.713 1.059,3.702 1.076,3.69 1.093,3.679 1.11,3.668 1.128,3.658 1.145,3.648 1.163,3.638 1.181,3.628 1.199,3.619 1.218,3.61 1.236,3.601 1.255,3.593 1.274,3.585 1.293,3.577 1.312,3.57 1.331,3.563 1.35,3.556 1.37,3.55 1.389,3.544 1.409,3.538 1.428,3.533 1.448,3.528 1.468,3.523 1.488,3.519 1.508,3.515 1.528,3.511 1.548,3.508 1.569,3.505 1.589,3.503 1.609,3.501 1.63,3.499 1.65,3.497 1.67,3.496 1.691,3.496 1.711,3.495 1.732,3.495 1.752,3.496 1.773,3.496 1.793,3.497 1.813,3.499 1.834,3.5 1.854,3.503 1.874,3.505 1.894,3.508 1.915,3.511 1.935,3.514 1.955,3.518 1.975,3.523 1.995,3.527 2.015,3.532 2.034,3.537 2.054,3.543 2.074,3.549 2.093,3.555 2.112,3.562 2.131,3.569 2.151,3.576 2.17,3.584 2.188,3.592 2.207,3.6 2.226,3.608 2.244,3.617 2.262,3.627 2.28,3.636 2.298,3.646 2.316,3.656 2.333,3.6679,4.111 8.966,4.178 8.932,4.244 8.896,4.309 8.859,4.374 8.821,4.438 8.782,4.502 8.742,4.565 8.7,4.627 8.658,4.688 8.614,4.749 8.57,4.809 8.524,4.868 8.477,4.926 8.43,4.983 8.381,5.04 8.331,5.096 8.281,5.151 8.229,5.205 8.177,5.258 8.124,5.31 8.069,5.361 8.014,5.411 7.958,5.461 7.901,5.509 7.844,5.556 7.785,5.603 7.726,5.648 7.666,5.692 7.605,5.735 7.543,5.777 7.481,5.818 7.418,5.858 7.354,5.897 7.29,5.935 7.225,5.971 7.159,6.007 7.093,6.041 7.026,6.074 6.958,6.106 6.89,6.137 6.822,6.167 6.753,6.195 6.683,6.222 6.613,6.248 6.543,6.273 6.472,6.296 6.401,6.319 6.329,6.34 6.257,6.36 6.185,6.378 6.112,6.395 6.04,6.411 5.966,6.426 5.893,6.44 5.819,6.452 5.746,6.463 5.672,6.472 5.597,6.48 5.523,6.487 5.449,6.493 5.374,6.498 5.3,6.501 5.225,6.503 5.15,6.503 5.076,6.502 5.001,6.5 4.927,6.497 4.852,6.492 4.778,6.486 4.704,6.479 4.629,6.47 4.555,6.46 4.482,6.449 4.408,6.437 4.335,6.423 4.262,6.408 4.189,6.392 4.116,6.374 4.044,6.355 3.972,6.335 3.901,6.314 3.83,6.292 3.759,6.268 3.688,6.243 3.619,6.217 3.549,6.189 3.48,6.16 3.412,6.131 3.344,6.1 3.277,6.067 3.21,6.034 3.144,5.999 3.078,5.964 3.013,5.927 2.949,5.889 2.886,5.85 2.823,5.81 2.761,5.768 2.699,5.726 2.638,5.683 2.578,5.638 2.519,5.593 2.461,5.546 2.403,5.499 2.347,5.45 2.291,5.401 2.236,5.35 2.182,5.299 2.129,5.246 2.076,5.193 2.025,5.139 1.975,5.084 1.925,5.028 1.877,4.971 1.829,4.914 1.783,4.855 1.738,4.796 1.693,4.736 1.65,4.675 1.608,4.614 1.567,4.551 1.527,4.488 1.488,4.425 1.45,4.36 1.413,4.295 1.378,4.23 1.343,4.164 1.31,4.097 1.278,4.029 1.247,3.961 1.217,3.893 1.189,3.824 1.161,3.755 1.135,3.685 1.11,3.614 1.087,3.544 1.064,3.472 1.043,3.401 1.023,3.329 1.004,3.257 0.987,3.184 0.971,3.111 0.956,3.038 0.942,2.965 0.93,2.891 0.919,2.817 0.909,2.743 0.901,2.669 0.894,2.595 0.888,2.52 0.883,2.446 0.88,2.371 0.878,2.297 0.878,2.222 0.878,2.148 0.88,2.073 0.883,1.998 0.888,1.924 0.894,1.85 0.901,1.775 0.909,1.701 0.919,1.627 0.93,1.553 0.942,1.48 0.956,1.406 0.971,1.333 0.987,1.26 1.004,1.188 1.023,1.116 1.043,1.044 1.064,0.972 1.087,0.901 1.11,0.83 1.135,0.76 1.161,0.69 1.189,0.62 1.217,0.551 1.247,0.483 1.278,0.415 1.31,0.348 1.343,0.281 1.378,0.215 1.413,0.149 1.45,0.084 1.488,0.02 1.527,-0.044 1.567,-0.107 1.608,-0.169 1.65,-0.231 1.693,-0.291 1.738,-0.351 1.783,-0.411 1.829,-0.469 1.877,-0.527 1.925,-0.584 1.975,-0.639 2.025,-0.694 2.076,-0.749 2.129,-0.802 2.182,-0.854 2.236,-0.906 2.291,-0.956 2.347,-1.006 2.403,-1.054 2.461,-1.102 2.519,-1.148 2.578,-1.194 2.638,-1.238 2.699,-1.282 2.761,-1.324 2.823,-1.365 2.886,-1.405 2.949,-1.444 3.013,-1.482 3.078,-1.519 3.144,-1.555 3.21,-1.589 3.277,-1.623 3.344,-1.655 3.412,-1.686 3.48,-1.716 3.549,-1.745 3.619,-1.772 3.688,-1.798 3.759,-1.823 3.83,-1.847 3.901,-1.87 3.972,-1.891 4.044,-1.911 4.116,-1.93 4.189,-1.947 4.262,-1.964 4.335,-1.979 4.408,-1.992 4.482,-2.005 4.555,-2.016 4.629,-2.026 4.704,-2.034 4.778,-2.042 4.852,-2.048 4.927,-2.052 5.001,-2.056 5.076,-2.058 5.15,-2.059 5.225,-2.058 5.3,-2.056 5.374,-2.053 5.449,-2.049 5.523,-2.043 5.597,-2.036 5.672,-2.028 5.746,-2.018 5.819,-2.007 5.893,-1.995 5.966,-1.982 6.04,-1.967 6.112,-1.951 6.185,-1.934 6.257,-1.915 6.329,-1.895 6.401,-1.874 6.472,-1.852 6.543,-1.829 6.613,-1.804 6.683,-1.778 6.753,-1.751 6.822,-1.722 6.89,-1.693 6.958,-1.662 7.026,-1.63 7.093,-1.597 7.159,-1.562 7.225,-1.527 7.29,-1.49 7.354,-1.453 7.418,-1.414 7.481,-1.374 7.543,-1.333 7.605,-1.291 7.666,-1.248 7.726,-1.203 7.785,-1.158 7.844,-1.112 7.901,-1.065 7.958,-1.016 8.014,-0.967 8.069,-0.917 8.124,-0.865 8.177,-0.813 8.229,-0.76 8.281,-0.706 8.331,-0.651 8.381,-0.596 8.43,-0.539 8.477,-0.482 8.524,-0.423 8.57,-0.364 8.614,-0.304 8.658,-0.244 8.7,-0.182 8.742,-0.12 8.782,-0.057 8.821,0.006 8.859,0.07 8.896,0.135 8.932,0.201 8.966,0.267 9,0.333" ) ); + res = elemToString( exportFloat.asGML2( doc, 3 ) ); + QGSCOMPAREGML( res, expectedGML2prec3 ); + + //as GML3 + QString expectedGML3prec3( QStringLiteral( "2.333 5.667 0.6 4.333 2.333 3.6679 4.111 1.049 1.024 9 0.333" ) ); + res = elemToString( exportFloat.asGML3( doc, 3 ) ); + QCOMPARE( res, expectedGML3prec3 ); + + // insert geometry + QgsMultiCurve rc; + rc.clear(); + rc.insertGeometry( nullptr, 0 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + rc.insertGeometry( nullptr, -1 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + rc.insertGeometry( nullptr, 100 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + + rc.insertGeometry( new QgsPoint(), 0 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + + rc.insertGeometry( part.clone(), 0 ); + QVERIFY( !rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 1 ); + + // cast + QVERIFY( !QgsMultiCurve().cast( nullptr ) ); + QgsMultiCurve pCast; + QVERIFY( QgsMultiCurve().cast( &pCast ) ); + QgsMultiCurve pCast2; + pCast2.fromWkt( QStringLiteral( "MultiCurveZ()" ) ); + QVERIFY( QgsMultiCurve().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "MultiCurveM()" ) ); + QVERIFY( QgsMultiCurve().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "MultiCurveZM()" ) ); + QVERIFY( QgsMultiCurve().cast( &pCast2 ) ); + + //boundary + QgsMultiCurve multiLine1; + QVERIFY( !multiLine1.boundary() ); + QgsCircularString boundaryLine1; + boundaryLine1.setPoints( QList() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) << QgsPoint( 1, 1 ) ); + multiLine1.addGeometry( boundaryLine1.clone() ); + QgsAbstractGeometry *boundary = multiLine1.boundary(); + QgsMultiPointV2 *mpBoundary = dynamic_cast< QgsMultiPointV2 * >( boundary ); + QVERIFY( mpBoundary ); + QCOMPARE( mpBoundary->numGeometries(), 2 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->x(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->y(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->x(), 1.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->y(), 1.0 ); + delete boundary; + // add another QgsCircularString + QgsCircularString boundaryLine2; + boundaryLine2.setPoints( QList() << QgsPoint( 10, 10 ) << QgsPoint( 11, 10 ) << QgsPoint( 11, 11 ) ); + multiLine1.addGeometry( boundaryLine2.clone() ); + boundary = multiLine1.boundary(); + mpBoundary = dynamic_cast< QgsMultiPointV2 * >( boundary ); + QVERIFY( mpBoundary ); + QCOMPARE( mpBoundary->numGeometries(), 4 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->x(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->y(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->x(), 1.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->y(), 1.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 2 ) )->x(), 10.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 2 ) )->y(), 10.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 3 ) )->x(), 11.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 3 ) )->y(), 11.0 ); + delete boundary; + + // add a closed string = no boundary + QgsCircularString boundaryLine3; + boundaryLine3.setPoints( QList() << QgsPoint( 20, 20 ) << QgsPoint( 21, 20 ) << QgsPoint( 20, 20 ) ); + multiLine1.addGeometry( boundaryLine3.clone() ); + boundary = multiLine1.boundary(); + mpBoundary = dynamic_cast< QgsMultiPointV2 * >( boundary ); + QVERIFY( mpBoundary ); + QCOMPARE( mpBoundary->numGeometries(), 4 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->x(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->y(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->x(), 1.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->y(), 1.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 2 ) )->x(), 10.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 2 ) )->y(), 10.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 3 ) )->x(), 11.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 3 ) )->y(), 11.0 ); + delete boundary; + + //boundary with z + QgsCircularString boundaryLine4; + boundaryLine4.setPoints( QList() << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 10 ) << QgsPoint( QgsWkbTypes::PointZ, 1, 0, 15 ) << QgsPoint( QgsWkbTypes::PointZ, 1, 1, 20 ) ); + QgsCircularString boundaryLine5; + boundaryLine5.setPoints( QList() << QgsPoint( QgsWkbTypes::PointZ, 10, 10, 100 ) << QgsPoint( QgsWkbTypes::PointZ, 10, 20, 150 ) << QgsPoint( QgsWkbTypes::PointZ, 20, 20, 200 ) ); + QgsMultiCurve multiLine2; + multiLine2.addGeometry( boundaryLine4.clone() ); + multiLine2.addGeometry( boundaryLine5.clone() ); + + boundary = multiLine2.boundary(); + mpBoundary = dynamic_cast< QgsMultiPointV2 * >( boundary ); + QVERIFY( mpBoundary ); + QCOMPARE( mpBoundary->numGeometries(), 4 ); + QCOMPARE( mpBoundary->geometryN( 0 )->wkbType(), QgsWkbTypes::PointZ ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->x(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->y(), 0.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 0 ) )->z(), 10.0 ); + QCOMPARE( mpBoundary->geometryN( 1 )->wkbType(), QgsWkbTypes::PointZ ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->x(), 1.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->y(), 1.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 1 ) )->z(), 20.0 ); + QCOMPARE( mpBoundary->geometryN( 2 )->wkbType(), QgsWkbTypes::PointZ ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 2 ) )->x(), 10.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 2 ) )->y(), 10.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 2 ) )->z(), 100.0 ); + QCOMPARE( mpBoundary->geometryN( 3 )->wkbType(), QgsWkbTypes::PointZ ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 3 ) )->x(), 20.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 3 ) )->y(), 20.0 ); + QCOMPARE( static_cast< QgsPoint *>( mpBoundary->geometryN( 3 ) )->z(), 200.0 ); + delete boundary; + + //reversed + QgsMultiCurve cR; + std::unique_ptr< QgsMultiCurve > reversed( cR.reversed() ); + QVERIFY( reversed->isEmpty() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 7, 17, 4, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 3, 13, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 7, 11, 2, 8 ) ) ; + cR.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 27, 37, 6, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 43, 43, 11, 5 ) << QgsPoint( QgsWkbTypes::PointZM, 27, 53, 21, 52 ) ) ; + cR.addGeometry( part.clone() ); + reversed.reset( cR.reversed() ); + QVERIFY( !reversed->isEmpty() ); + ls = static_cast< const QgsCircularString * >( reversed->geometryN( 0 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 7, 11, 2, 8 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 3, 13, 1, 4 ) ); + QCOMPARE( ls->pointN( 2 ), QgsPoint( QgsWkbTypes::PointZM, 7, 17, 4, 1 ) ); + ls = static_cast< const QgsCircularString * >( reversed->geometryN( 1 ) ); + QCOMPARE( ls->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 27, 53, 21, 52 ) ); + QCOMPARE( ls->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 43, 43, 11, 5 ) ); + QCOMPARE( ls->pointN( 2 ), QgsPoint( QgsWkbTypes::PointZM, 27, 37, 6, 2 ) ); +} + +void TestQgsGeometry::multiSurface() +{ + //test constructor + QgsMultiSurface c1; + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QVERIFY( !c1.is3D() ); + QVERIFY( !c1.isMeasure() ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiSurface ); + QCOMPARE( c1.wktTypeStr(), QString( "MultiSurface" ) ); + QCOMPARE( c1.geometryType(), QString( "MultiSurface" ) ); + QCOMPARE( c1.dimension(), 0 ); + QVERIFY( !c1.hasCurvedSegments() ); + QCOMPARE( c1.area(), 0.0 ); + QCOMPARE( c1.perimeter(), 0.0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + QCOMPARE( c1.vertexCount( 0, 0 ), 0 ); + QCOMPARE( c1.vertexCount( 0, 1 ), 0 ); + QCOMPARE( c1.vertexCount( 1, 0 ), 0 ); + + //addGeometry + //try with nullptr + c1.addGeometry( nullptr ); + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiSurface ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + + // not a surface + QVERIFY( !c1.addGeometry( new QgsPoint() ) ); + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiSurface ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + + //valid geometry + QgsCurvePolygon part; + QgsCircularString ring; + ring.setPoints( QgsPointSequence() << QgsPoint( 1, 10 ) << QgsPoint( 2, 11 ) << QgsPoint( 1, 10 ) ); + part.setExteriorRing( ring.clone() ); + c1.addGeometry( part.clone() ); + QVERIFY( !c1.isEmpty() ); + QCOMPARE( c1.numGeometries(), 1 ); + QCOMPARE( c1.nCoordinates(), 3 ); + QCOMPARE( c1.ringCount(), 1 ); + QCOMPARE( c1.partCount(), 1 ); + QVERIFY( !c1.is3D() ); + QVERIFY( !c1.isMeasure() ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiSurface ); + QCOMPARE( c1.wktTypeStr(), QString( "MultiSurface" ) ); + QCOMPARE( c1.geometryType(), QString( "MultiSurface" ) ); + QCOMPARE( c1.dimension(), 2 ); + QVERIFY( c1.hasCurvedSegments() ); + QVERIFY( c1.geometryN( 0 ) ); + QCOMPARE( *static_cast< const QgsCurvePolygon * >( c1.geometryN( 0 ) ), part ); + QVERIFY( !c1.geometryN( 100 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + QCOMPARE( c1.vertexCount( 0, 0 ), 3 ); + QCOMPARE( c1.vertexCount( 1, 0 ), 0 ); + + //initial adding of geometry should set z/m type + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 10, 11, 1 ) << QgsPoint( QgsWkbTypes::PointZ, 20, 21, 2 ) + << QgsPoint( QgsWkbTypes::PointZ, 10, 11, 1 ) ); + part.clear(); + part.setExteriorRing( ring.clone() ); + QgsMultiSurface c2; + c2.addGeometry( part.clone() ); + QVERIFY( c2.is3D() ); + QVERIFY( !c2.isMeasure() ); + QCOMPARE( c2.wkbType(), QgsWkbTypes::MultiSurfaceZ ); + QCOMPARE( c2.wktTypeStr(), QString( "MultiSurfaceZ" ) ); + QCOMPARE( c2.geometryType(), QString( "MultiSurface" ) ); + QCOMPARE( *( static_cast< const QgsCurvePolygon * >( c2.geometryN( 0 ) ) ), part ); + QgsMultiSurface c3; + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 10, 11, 0, 1 ) << QgsPoint( QgsWkbTypes::PointM, 20, 21, 0, 2 ) + << QgsPoint( QgsWkbTypes::PointM, 10, 11, 0, 1 ) ); + part.clear(); + part.setExteriorRing( ring.clone() ); + c3.addGeometry( part.clone() ); + QVERIFY( !c3.is3D() ); + QVERIFY( c3.isMeasure() ); + QCOMPARE( c3.wkbType(), QgsWkbTypes::MultiSurfaceM ); + QCOMPARE( c3.wktTypeStr(), QString( "MultiSurfaceM" ) ); + QCOMPARE( *( static_cast< const QgsCurvePolygon * >( c3.geometryN( 0 ) ) ), part ); + QgsMultiSurface c4; + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 10, 11, 2, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 20, 21, 3, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 10, 11, 2, 1 ) ); + part.clear(); + part.setExteriorRing( ring.clone() ); + c4.addGeometry( part.clone() ); + QVERIFY( c4.is3D() ); + QVERIFY( c4.isMeasure() ); + QCOMPARE( c4.wkbType(), QgsWkbTypes::MultiSurfaceZM ); + QCOMPARE( c4.wktTypeStr(), QString( "MultiSurfaceZM" ) ); + QCOMPARE( *( static_cast< const QgsCurvePolygon * >( c4.geometryN( 0 ) ) ), part ); + + //add another part + QgsMultiSurface c6; + ring.setPoints( QgsPointSequence() << QgsPoint( 1, 10 ) << QgsPoint( 2, 11 ) << QgsPoint( 1, 10 ) ); + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.vertexCount( 0, 0 ), 3 ); + ring.setPoints( QgsPointSequence() << QgsPoint( 9, 12 ) << QgsPoint( 3, 13 ) << QgsPoint( 9, 12 ) ); + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.vertexCount( 1, 0 ), 3 ); + QCOMPARE( c6.numGeometries(), 2 ); + QVERIFY( c6.geometryN( 0 ) ); + QCOMPARE( *static_cast< const QgsCurvePolygon * >( c6.geometryN( 1 ) ), part ); + + //adding subsequent points should not alter z/m type, regardless of parts type + c6.clear(); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiSurface ); + ring.setPoints( QgsPointSequence() << QgsPoint( 1, 10, 2 ) << QgsPoint( 2, 11, 3 ) << QgsPoint( 1, 10, 2 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiSurface ); + QCOMPARE( c6.vertexCount( 0, 0 ), 3 ); + QCOMPARE( c6.vertexCount( 1, 0 ), 3 ); + QCOMPARE( c6.vertexCount( 2, 0 ), 0 ); + QCOMPARE( c6.vertexCount( -1, 0 ), 0 ); + QCOMPARE( c6.nCoordinates(), 6 ); + QCOMPARE( c6.ringCount(), 1 ); + QCOMPARE( c6.partCount(), 2 ); + QVERIFY( !c6.is3D() ); + const QgsCurvePolygon *ls = static_cast< const QgsCurvePolygon * >( c6.geometryN( 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( 9, 12 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( 3, 13 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( 9, 12 ) ); + ls = static_cast< const QgsCurvePolygon * >( c6.geometryN( 1 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( 1, 10 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( 2, 11 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( 1, 10 ) ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 21, 30, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 32, 41, 0, 3 ) + << QgsPoint( QgsWkbTypes::PointM, 21, 30, 0, 2 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiSurface ); + QCOMPARE( c6.vertexCount( 0, 0 ), 3 ); + QCOMPARE( c6.vertexCount( 1, 0 ), 3 ); + QCOMPARE( c6.vertexCount( 2, 0 ), 3 ); + QCOMPARE( c6.nCoordinates(), 9 ); + QCOMPARE( c6.ringCount(), 1 ); + QCOMPARE( c6.partCount(), 3 ); + QVERIFY( !c6.is3D() ); + QVERIFY( !c6.isMeasure() ); + ls = static_cast< const QgsCurvePolygon * >( c6.geometryN( 2 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( 21, 30 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( 32, 41 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( 21, 30 ) ); + + c6.clear(); + ring.setPoints( QgsPointSequence() << QgsPoint( 1, 10, 2 ) << QgsPoint( 2, 11, 3 ) << QgsPoint( 1, 10, 2 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiSurfaceZ ); + ring.setPoints( QgsPointSequence() << QgsPoint( 2, 20 ) << QgsPoint( 3, 31 ) << QgsPoint( 2, 20 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiSurfaceZ ); + QVERIFY( c6.is3D() ); + ls = static_cast< const QgsCurvePolygon * >( c6.geometryN( 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( 1, 10, 2 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( 2, 11, 3 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( 1, 10, 2 ) ); + ls = static_cast< const QgsCurvePolygon * >( c6.geometryN( 1 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( 2, 20, 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( 3, 31, 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( 2, 20, 0 ) ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 6, 61, 0, 5 ) + << QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiSurfaceZ ); + QVERIFY( c6.is3D() ); + QVERIFY( !c6.isMeasure() ); + ls = static_cast< const QgsCurvePolygon * >( c6.geometryN( 2 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( 5, 50, 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( 6, 61, 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( 5, 50, 0 ) ); + + c6.clear(); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiSurface ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 6, 61, 0, 5 ) + << QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiSurfaceM ); + ring.setPoints( QgsPointSequence() << QgsPoint( 2, 20 ) << QgsPoint( 3, 31 ) << QgsPoint( 2, 20 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiSurfaceM ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsCurvePolygon * >( c6.geometryN( 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( QgsWkbTypes::PointM, 6, 61, 0, 5 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) ); + ls = static_cast< const QgsCurvePolygon * >( c6.geometryN( 1 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 2, 20, 0, 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( QgsWkbTypes::PointM, 3, 31, 0, 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( QgsWkbTypes::PointM, 2, 20, 0, 0 ) ); + ring.setPoints( QgsPointSequence() << QgsPoint( 11, 12, 13 ) << QgsPoint( 14, 15, 16 ) << QgsPoint( 11, 12, 13 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiSurfaceM ); + QVERIFY( !c6.is3D() ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsCurvePolygon * >( c6.geometryN( 2 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( QgsWkbTypes::PointM, 14, 15, 0, 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 0 ) ); + + c6.clear(); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 6, 61, 3, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiSurfaceZM ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7, 17 ) << QgsPoint( QgsWkbTypes::Point, 3, 13 ) + << QgsPoint( QgsWkbTypes::Point, 7, 17 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiSurfaceZM ); + QVERIFY( c6.isMeasure() ); + QVERIFY( c6.is3D() ); + ls = static_cast< const QgsCurvePolygon * >( c6.geometryN( 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 6, 61, 3, 5 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) ); + ls = static_cast< const QgsCurvePolygon * >( c6.geometryN( 1 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 7, 17, 0, 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 3, 13, 0, 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( QgsWkbTypes::PointZM, 7, 17, 0, 0 ) ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 77, 87, 7 ) << QgsPoint( QgsWkbTypes::PointZ, 83, 83, 8 ) + << QgsPoint( QgsWkbTypes::PointZ, 77, 87, 7 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiSurfaceZM ); + QVERIFY( c6.is3D() ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsCurvePolygon * >( c6.geometryN( 2 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 77, 87, 7, 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 83, 83, 8, 0 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( QgsWkbTypes::PointZM, 77, 87, 7, 0 ) ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 177, 187, 0, 9 ) << QgsPoint( QgsWkbTypes::PointM, 183, 183, 0, 11 ) + << QgsPoint( QgsWkbTypes::PointM, 177, 187, 0, 9 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiSurfaceZM ); + QVERIFY( c6.is3D() ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsCurvePolygon * >( c6.geometryN( 3 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 177, 187, 0, 9 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 183, 183, 0, 11 ) ); + QCOMPARE( static_cast< const QgsCircularString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( QgsWkbTypes::PointZM, 177, 187, 0, 9 ) ); + + //clear + QgsMultiSurface c7; + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 6, 61, 3, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 5, 71, 4, 6 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c7.addGeometry( part.clone() ); + c7.addGeometry( part.clone() ); + QCOMPARE( c7.numGeometries(), 2 ); + c7.clear(); + QVERIFY( c7.isEmpty() ); + QCOMPARE( c7.numGeometries(), 0 ); + QCOMPARE( c7.nCoordinates(), 0 ); + QCOMPARE( c7.ringCount(), 0 ); + QCOMPARE( c7.partCount(), 0 ); + QVERIFY( !c7.is3D() ); + QVERIFY( !c7.isMeasure() ); + QCOMPARE( c7.wkbType(), QgsWkbTypes::MultiSurface ); + + //clone + QgsMultiSurface c11; + std::unique_ptr< QgsMultiSurface >cloned( c11.clone() ); + QVERIFY( cloned->isEmpty() ); + c11.addGeometry( part.clone() ); + c11.addGeometry( part.clone() ); + cloned.reset( c11.clone() ); + QCOMPARE( cloned->numGeometries(), 2 ); + ls = static_cast< const QgsCurvePolygon * >( cloned->geometryN( 0 ) ); + QCOMPARE( *ls, part ); + ls = static_cast< const QgsCurvePolygon * >( cloned->geometryN( 1 ) ); + QCOMPARE( *ls, part ); + + //copy constructor + QgsMultiSurface c12; + QgsMultiSurface c13( c12 ); + QVERIFY( c13.isEmpty() ); + c12.addGeometry( part.clone() ); + c12.addGeometry( part.clone() ); + QgsMultiSurface c14( c12 ); + QCOMPARE( c14.numGeometries(), 2 ); + QCOMPARE( c14.wkbType(), QgsWkbTypes::MultiSurfaceZM ); + ls = static_cast< const QgsCurvePolygon * >( c14.geometryN( 0 ) ); + QCOMPARE( *ls, part ); + ls = static_cast< const QgsCurvePolygon * >( c14.geometryN( 1 ) ); + QCOMPARE( *ls, part ); + + //assignment operator + QgsMultiSurface c15; + c15 = c13; + QCOMPARE( c15.numGeometries(), 0 ); + c15 = c14; + QCOMPARE( c15.numGeometries(), 2 ); + ls = static_cast< const QgsCurvePolygon * >( c15.geometryN( 0 ) ); + QCOMPARE( *ls, part ); + ls = static_cast< const QgsCurvePolygon * >( c15.geometryN( 1 ) ); + QCOMPARE( *ls, part ); + + //toCurveType + std::unique_ptr< QgsMultiSurface > curveType( c12.toCurveType() ); + QCOMPARE( curveType->wkbType(), QgsWkbTypes::MultiSurfaceZM ); + QCOMPARE( curveType->numGeometries(), 2 ); + const QgsCurvePolygon *curve = static_cast< const QgsCurvePolygon * >( curveType->geometryN( 0 ) ); + QCOMPARE( *curve, *static_cast< const QgsCurvePolygon * >( c12.geometryN( 0 ) ) ); + curve = static_cast< const QgsCurvePolygon * >( curveType->geometryN( 1 ) ); + QCOMPARE( *curve, *static_cast< const QgsCurvePolygon * >( c12.geometryN( 1 ) ) ); + + //to/fromWKB + QgsMultiSurface c16; + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7, 17 ) << QgsPoint( QgsWkbTypes::Point, 3, 13 ) << QgsPoint( QgsWkbTypes::Point, 7, 17 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 27, 37 ) << QgsPoint( QgsWkbTypes::Point, 43, 43 ) << QgsPoint( QgsWkbTypes::Point, 27, 37 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + QByteArray wkb16 = c16.asWkb(); + QgsMultiSurface c17; + QgsConstWkbPtr wkb16ptr( wkb16 ); + c17.fromWkb( wkb16ptr ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsCurvePolygon * >( c17.geometryN( 0 ) ), *static_cast< const QgsCurvePolygon * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsCurvePolygon * >( c17.geometryN( 1 ) ), *static_cast< const QgsCurvePolygon * >( c16.geometryN( 1 ) ) ); + + //parts with Z + c16.clear(); + c17.clear(); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 7, 17, 1 ) << QgsPoint( QgsWkbTypes::PointZ, 3, 13, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 7, 17, 1 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 27, 37, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 43, 43, 5 ) << QgsPoint( QgsWkbTypes::PointZ, 27, 37, 2 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr2( wkb16 ); + c17.fromWkb( wkb16ptr2 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiSurfaceZ ); + QCOMPARE( *static_cast< const QgsCurvePolygon * >( c17.geometryN( 0 ) ), *static_cast< const QgsCurvePolygon * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsCurvePolygon * >( c17.geometryN( 1 ) ), *static_cast< const QgsCurvePolygon * >( c16.geometryN( 1 ) ) ); + + //parts with m + c16.clear(); + c17.clear(); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 7, 17, 0, 1 ) << QgsPoint( QgsWkbTypes::PointM, 3, 13, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 7, 17, 0, 1 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 27, 37, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 43, 43, 0, 5 ) << QgsPoint( QgsWkbTypes::PointM, 27, 37, 0, 2 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr3( wkb16 ); + c17.fromWkb( wkb16ptr3 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiSurfaceM ); + QCOMPARE( *static_cast< const QgsCurvePolygon * >( c17.geometryN( 0 ) ), *static_cast< const QgsCurvePolygon * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsCurvePolygon * >( c17.geometryN( 1 ) ), *static_cast< const QgsCurvePolygon * >( c16.geometryN( 1 ) ) ); + + // parts with ZM + c16.clear(); + c17.clear(); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 7, 17, 4, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 3, 13, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 7, 17, 4, 1 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 27, 37, 6, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 43, 43, 11, 5 ) << QgsPoint( QgsWkbTypes::PointZM, 27, 37, 6, 2 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr4( wkb16 ); + c17.fromWkb( wkb16ptr4 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiSurfaceZM ); + QCOMPARE( *static_cast< const QgsCurvePolygon * >( c17.geometryN( 0 ) ), *static_cast< const QgsCurvePolygon * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsCurvePolygon * >( c17.geometryN( 1 ) ), *static_cast< const QgsCurvePolygon * >( c16.geometryN( 1 ) ) ); + + //bad WKB - check for no crash + c17.clear(); + QgsConstWkbPtr nullPtr( nullptr, 0 ); + QVERIFY( !c17.fromWkb( nullPtr ) ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiSurface ); + QgsPoint point( 1, 2 ); + QByteArray wkbPoint = point.asWkb(); + QgsConstWkbPtr wkbPointPtr( wkbPoint ); + QVERIFY( !c17.fromWkb( wkbPointPtr ) ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiSurface ); + + //to/from WKT + QgsMultiSurface c18; + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 7, 17, 4, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 3, 13, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 7, 11, 2, 8 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c18.addGeometry( part.clone() ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 27, 37, 6, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 43, 43, 11, 5 ) << QgsPoint( QgsWkbTypes::PointZM, 27, 53, 21, 52 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c18.addGeometry( part.clone() ); + + QString wkt = c18.asWkt(); + QVERIFY( !wkt.isEmpty() ); + QgsMultiSurface c19; + QVERIFY( c19.fromWkt( wkt ) ); + QCOMPARE( c19.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsCurvePolygon * >( c19.geometryN( 0 ) ), *static_cast< const QgsCurvePolygon * >( c18.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsCurvePolygon * >( c19.geometryN( 1 ) ), *static_cast< const QgsCurvePolygon * >( c18.geometryN( 1 ) ) ); + + //bad WKT + QgsMultiSurface c20; + QVERIFY( !c20.fromWkt( "Point()" ) ); + QVERIFY( c20.isEmpty() ); + QCOMPARE( c20.numGeometries(), 0 ); + QCOMPARE( c20.wkbType(), QgsWkbTypes::MultiSurface ); + + //as JSON + QgsMultiSurface exportC; + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7, 17 ) << QgsPoint( QgsWkbTypes::Point, 3, 13 ) << QgsPoint( QgsWkbTypes::Point, 7, 17 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + exportC.addGeometry( part.clone() ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 27, 37 ) << QgsPoint( QgsWkbTypes::Point, 43, 43 ) << QgsPoint( QgsWkbTypes::Point, 27, 37 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + exportC.addGeometry( part.clone() ); + + // GML document for compare + QDomDocument doc( "gml" ); + + // as GML2 + QString expectedSimpleGML2( QStringLiteral( "7,17 7,1727,37 27,37" ) ); + QString res = elemToString( exportC.asGML2( doc, 1 ) ); + QGSCOMPAREGML( res, expectedSimpleGML2 ); + + //as GML3 + QString expectedSimpleGML3( QStringLiteral( "7 17 3 13 7 1727 37 43 43 27 37" ) ); + res = elemToString( exportC.asGML3( doc ) ); + QCOMPARE( res, expectedSimpleGML3 ); + + // as JSON + QString expectedSimpleJson( "{\"type\": \"MultiPolygon\", \"coordinates\": [[[ [7, 17], [7, 17]]], [[ [27, 37], [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() ); + 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]]]] }" ); + 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 ) ) ; + 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() ); + exportFloat.addGeometry( part.clone() ); + + QString expectedJsonPrec3( QStringLiteral( "{\"type\": \"MultiPolygon\", \"coordinates\": [[[ [2.333, 5.667], [2.333, 5.667]]], [[ [9, 4.111], [9, 4.111]]]] }" ) ); + res = exportFloat.asJSON( 3 ); + QCOMPARE( res, expectedJsonPrec3 ); + + // as GML2 + QString expectedGML2prec3( QStringLiteral( "2.333,5.667 2.333,5.6679,4.111 9,4.111" ) ); + res = elemToString( exportFloat.asGML2( doc, 3 ) ); + QGSCOMPAREGML( res, expectedGML2prec3 ); + + //as GML3 + QString expectedGML3prec3( QStringLiteral( "2.333 5.667 0.6 4.333 2.333 5.6679 4.111 1.049 1.024 9 4.111" ) ); + res = elemToString( exportFloat.asGML3( doc, 3 ) ); + QCOMPARE( res, expectedGML3prec3 ); + + // insert geometry + QgsMultiSurface rc; + rc.clear(); + rc.insertGeometry( nullptr, 0 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + rc.insertGeometry( nullptr, -1 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + rc.insertGeometry( nullptr, 100 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + + rc.insertGeometry( new QgsPoint(), 0 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + + rc.insertGeometry( part.clone(), 0 ); + QVERIFY( !rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 1 ); + + // cast + QVERIFY( !QgsMultiSurface().cast( nullptr ) ); + QgsMultiSurface pCast; + QVERIFY( QgsMultiSurface().cast( &pCast ) ); + QgsMultiSurface pCast2; + pCast2.fromWkt( QStringLiteral( "MultiSurfaceZ()" ) ); + QVERIFY( QgsMultiSurface().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "MultiSurfaceM()" ) ); + QVERIFY( QgsMultiSurface().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "MultiSurfaceZM()" ) ); + QVERIFY( QgsMultiSurface().cast( &pCast2 ) ); + + //boundary + QgsMultiSurface multiSurface; + QVERIFY( !multiSurface.boundary() ); + QgsCircularString boundaryLine1; + boundaryLine1.setPoints( QList() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) << QgsPoint( 0, 0 ) ); + part.clear(); + part.setExteriorRing( boundaryLine1.clone() ); + multiSurface.addGeometry( part.clone() ); + QgsAbstractGeometry *boundary = multiSurface.boundary(); + QgsMultiCurve *mpBoundary = dynamic_cast< QgsMultiCurve * >( boundary ); + QVERIFY( mpBoundary ); + QCOMPARE( mpBoundary->numGeometries(), 1 ); + QCOMPARE( *static_cast< const QgsCurve *>( mpBoundary->geometryN( 0 ) ), *part.exteriorRing() ); + delete boundary; + // add another QgsCircularString + QgsCircularString boundaryLine2; + boundaryLine2.setPoints( QList() << QgsPoint( 10, 10 ) << QgsPoint( 11, 10 ) << QgsPoint( 10, 10 ) ); + QgsCurvePolygon part2; + part2.setExteriorRing( boundaryLine2.clone() ); + multiSurface.addGeometry( part2.clone() ); + boundary = multiSurface.boundary(); + mpBoundary = dynamic_cast< QgsMultiCurve * >( boundary ); + QVERIFY( mpBoundary ); + QCOMPARE( mpBoundary->numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsCurve *>( mpBoundary->geometryN( 0 ) ), *part.exteriorRing() ); + QCOMPARE( *static_cast< const QgsCurve *>( mpBoundary->geometryN( 1 ) ), *part2.exteriorRing() ); + delete boundary; + + //boundary with z + QgsCircularString boundaryLine4; + boundaryLine4.setPoints( QList() << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 10 ) << QgsPoint( QgsWkbTypes::PointZ, 1, 0, 15 ) << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 10 ) ); + part.clear(); + part.setExteriorRing( boundaryLine4.clone() ); + QgsCircularString boundaryLine5; + boundaryLine5.setPoints( QList() << QgsPoint( QgsWkbTypes::PointZ, 10, 10, 100 ) << QgsPoint( QgsWkbTypes::PointZ, 10, 20, 150 ) << QgsPoint( QgsWkbTypes::PointZ, 10, 10, 100 ) ); + part2.clear(); + part2.setExteriorRing( boundaryLine5.clone() ); + QgsMultiSurface multiSurface2; + multiSurface2.addGeometry( part.clone() ); + multiSurface2.addGeometry( part2.clone() ); + + boundary = multiSurface2.boundary(); + mpBoundary = dynamic_cast< QgsMultiCurve * >( boundary ); + QVERIFY( mpBoundary ); + QCOMPARE( mpBoundary->numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsCurve *>( mpBoundary->geometryN( 0 ) ), *part.exteriorRing() ); + QCOMPARE( *static_cast< const QgsCurve *>( mpBoundary->geometryN( 1 ) ), *part2.exteriorRing() ); + delete boundary; +} + void TestQgsGeometry::multiPolygon() { + //test constructor + QgsMultiPolygonV2 c1; + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QVERIFY( !c1.is3D() ); + QVERIFY( !c1.isMeasure() ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiPolygon ); + QCOMPARE( c1.wktTypeStr(), QString( "MultiPolygon" ) ); + QCOMPARE( c1.geometryType(), QString( "MultiPolygon" ) ); + QCOMPARE( c1.dimension(), 0 ); + QVERIFY( !c1.hasCurvedSegments() ); + QCOMPARE( c1.area(), 0.0 ); + QCOMPARE( c1.perimeter(), 0.0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + QCOMPARE( c1.vertexCount( 0, 0 ), 0 ); + QCOMPARE( c1.vertexCount( 0, 1 ), 0 ); + QCOMPARE( c1.vertexCount( 1, 0 ), 0 ); + + //addGeometry + //try with nullptr + c1.addGeometry( nullptr ); + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiPolygon ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + + // not a surface + QVERIFY( !c1.addGeometry( new QgsPoint() ) ); + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiPolygon ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + + //valid geometry + QgsPolygonV2 part; + QgsLineString ring; + ring.setPoints( QgsPointSequence() << QgsPoint( 1, 10 ) << QgsPoint( 2, 11 ) << QgsPoint( 2, 21 ) << QgsPoint( 1, 10 ) ); + part.setExteriorRing( ring.clone() ); + c1.addGeometry( part.clone() ); + QVERIFY( !c1.isEmpty() ); + QCOMPARE( c1.numGeometries(), 1 ); + QCOMPARE( c1.nCoordinates(), 4 ); + QCOMPARE( c1.ringCount(), 1 ); + QCOMPARE( c1.partCount(), 1 ); + QVERIFY( !c1.is3D() ); + QVERIFY( !c1.isMeasure() ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::MultiPolygon ); + QCOMPARE( c1.wktTypeStr(), QString( "MultiPolygon" ) ); + QCOMPARE( c1.geometryType(), QString( "MultiPolygon" ) ); + QCOMPARE( c1.dimension(), 2 ); + QVERIFY( !c1.hasCurvedSegments() ); + QVERIFY( c1.geometryN( 0 ) ); + QCOMPARE( *static_cast< const QgsPolygonV2 * >( c1.geometryN( 0 ) ), part ); + QVERIFY( !c1.geometryN( 100 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + QCOMPARE( c1.vertexCount( 0, 0 ), 4 ); + QCOMPARE( c1.vertexCount( 1, 0 ), 0 ); + + //initial adding of geometry should set z/m type + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 10, 11, 1 ) << QgsPoint( QgsWkbTypes::PointZ, 20, 21, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 30, 31, 2 ) + << QgsPoint( QgsWkbTypes::PointZ, 10, 11, 1 ) ); + part.clear(); + part.setExteriorRing( ring.clone() ); + QgsMultiPolygonV2 c2; + c2.addGeometry( part.clone() ); + QVERIFY( c2.is3D() ); + QVERIFY( !c2.isMeasure() ); + QCOMPARE( c2.wkbType(), QgsWkbTypes::MultiPolygonZ ); + QCOMPARE( c2.wktTypeStr(), QString( "MultiPolygonZ" ) ); + QCOMPARE( c2.geometryType(), QString( "MultiPolygon" ) ); + QCOMPARE( *( static_cast< const QgsPolygonV2 * >( c2.geometryN( 0 ) ) ), part ); + QgsMultiPolygonV2 c3; + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 10, 11, 0, 1 ) << QgsPoint( QgsWkbTypes::PointM, 20, 21, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 30, 31, 0, 2 ) + << QgsPoint( QgsWkbTypes::PointM, 10, 11, 0, 1 ) ); + part.clear(); + part.setExteriorRing( ring.clone() ); + c3.addGeometry( part.clone() ); + QVERIFY( !c3.is3D() ); + QVERIFY( c3.isMeasure() ); + QCOMPARE( c3.wkbType(), QgsWkbTypes::MultiPolygonM ); + QCOMPARE( c3.wktTypeStr(), QString( "MultiPolygonM" ) ); + QCOMPARE( *( static_cast< const QgsPolygonV2 * >( c3.geometryN( 0 ) ) ), part ); + QgsMultiPolygonV2 c4; + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 10, 11, 2, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 20, 21, 3, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 30, 31, 3, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 10, 11, 2, 1 ) ); + part.clear(); + part.setExteriorRing( ring.clone() ); + c4.addGeometry( part.clone() ); + QVERIFY( c4.is3D() ); + QVERIFY( c4.isMeasure() ); + QCOMPARE( c4.wkbType(), QgsWkbTypes::MultiPolygonZM ); + QCOMPARE( c4.wktTypeStr(), QString( "MultiPolygonZM" ) ); + QCOMPARE( *( static_cast< const QgsPolygonV2 * >( c4.geometryN( 0 ) ) ), part ); + + //add another part + QgsMultiPolygonV2 c6; + ring.setPoints( QgsPointSequence() << QgsPoint( 1, 10 ) << QgsPoint( 2, 11 ) << QgsPoint( 10, 21 ) << QgsPoint( 1, 10 ) ); + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.vertexCount( 0, 0 ), 4 ); + ring.setPoints( QgsPointSequence() << QgsPoint( 9, 12 ) << QgsPoint( 3, 13 ) << QgsPoint( 4, 17 ) << QgsPoint( 9, 12 ) ); + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.vertexCount( 1, 0 ), 4 ); + QCOMPARE( c6.numGeometries(), 2 ); + QVERIFY( c6.geometryN( 0 ) ); + QCOMPARE( *static_cast< const QgsPolygonV2 * >( c6.geometryN( 1 ) ), part ); + + //adding subsequent points should not alter z/m type, regardless of parts type + c6.clear(); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPolygon ); + ring.setPoints( QgsPointSequence() << QgsPoint( 1, 10, 2 ) << QgsPoint( 2, 11, 3 ) << QgsPoint( 10, 13, 3 ) << QgsPoint( 1, 10, 2 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPolygon ); + QCOMPARE( c6.vertexCount( 0, 0 ), 4 ); + QCOMPARE( c6.vertexCount( 1, 0 ), 4 ); + QCOMPARE( c6.vertexCount( 2, 0 ), 0 ); + QCOMPARE( c6.vertexCount( -1, 0 ), 0 ); + QCOMPARE( c6.nCoordinates(), 8 ); + QCOMPARE( c6.ringCount(), 1 ); + QCOMPARE( c6.partCount(), 2 ); + QVERIFY( !c6.is3D() ); + const QgsPolygonV2 *ls = static_cast< const QgsPolygonV2 * >( c6.geometryN( 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( 9, 12 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( 3, 13 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( 4, 17 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 3 ), QgsPoint( 9, 12 ) ); + ls = static_cast< const QgsPolygonV2 * >( c6.geometryN( 1 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( 1, 10 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( 2, 11 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( 10, 13 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 3 ), QgsPoint( 1, 10 ) ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 21, 30, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 32, 41, 0, 3 ) + << QgsPoint( QgsWkbTypes::PointM, 42, 61, 0, 4 ) + << QgsPoint( QgsWkbTypes::PointM, 21, 30, 0, 2 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPolygon ); + QCOMPARE( c6.vertexCount( 0, 0 ), 4 ); + QCOMPARE( c6.vertexCount( 1, 0 ), 4 ); + QCOMPARE( c6.vertexCount( 2, 0 ), 4 ); + QCOMPARE( c6.nCoordinates(), 12 ); + QCOMPARE( c6.ringCount(), 1 ); + QCOMPARE( c6.partCount(), 3 ); + QVERIFY( !c6.is3D() ); + QVERIFY( !c6.isMeasure() ); + ls = static_cast< const QgsPolygonV2 * >( c6.geometryN( 2 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( 21, 30 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( 32, 41 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( 42, 61 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 3 ), QgsPoint( 21, 30 ) ); + + c6.clear(); + ring.setPoints( QgsPointSequence() << QgsPoint( 1, 10, 2 ) << QgsPoint( 2, 11, 3 ) << QgsPoint( 9, 15, 3 ) << QgsPoint( 1, 10, 2 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPolygonZ ); + ring.setPoints( QgsPointSequence() << QgsPoint( 2, 20 ) << QgsPoint( 3, 31 ) << QgsPoint( 7, 34 ) << QgsPoint( 2, 20 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPolygonZ ); + QVERIFY( c6.is3D() ); + ls = static_cast< const QgsPolygonV2 * >( c6.geometryN( 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( 1, 10, 2 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( 2, 11, 3 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( 9, 15, 3 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 3 ), QgsPoint( 1, 10, 2 ) ); + ls = static_cast< const QgsPolygonV2 * >( c6.geometryN( 1 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( 2, 20, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( 3, 31, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( 7, 34, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 3 ), QgsPoint( 2, 20, 0 ) ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 6, 61, 0, 5 ) + << QgsPoint( QgsWkbTypes::PointM, 9, 65, 0, 7 ) << QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPolygonZ ); + QVERIFY( c6.is3D() ); + QVERIFY( !c6.isMeasure() ); + ls = static_cast< const QgsPolygonV2 * >( c6.geometryN( 2 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( 5, 50, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( 6, 61, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( 9, 65, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 3 ), QgsPoint( 5, 50, 0 ) ); + + c6.clear(); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPolygon ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 6, 61, 0, 5 ) + << QgsPoint( QgsWkbTypes::PointM, 9, 76, 0, 8 ) << QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPolygonM ); + ring.setPoints( QgsPointSequence() << QgsPoint( 2, 20 ) << QgsPoint( 3, 31 ) << QgsPoint( 7, 39 ) << QgsPoint( 2, 20 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPolygonM ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsPolygonV2 * >( c6.geometryN( 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( QgsWkbTypes::PointM, 6, 61, 0, 5 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( QgsWkbTypes::PointM, 9, 76, 0, 8 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 3 ), QgsPoint( QgsWkbTypes::PointM, 5, 50, 0, 4 ) ); + ls = static_cast< const QgsPolygonV2 * >( c6.geometryN( 1 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 2, 20, 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( QgsWkbTypes::PointM, 3, 31, 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( QgsWkbTypes::PointM, 7, 39, 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 3 ), QgsPoint( QgsWkbTypes::PointM, 2, 20, 0, 0 ) ); + ring.setPoints( QgsPointSequence() << QgsPoint( 11, 12, 13 ) << QgsPoint( 14, 15, 16 ) << QgsPoint( 24, 21, 5 ) << QgsPoint( 11, 12, 13 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPolygonM ); + QVERIFY( !c6.is3D() ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsPolygonV2 * >( c6.geometryN( 2 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( QgsWkbTypes::PointM, 14, 15, 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( QgsWkbTypes::PointM, 24, 21, 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 3 ), QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 0 ) ); + + c6.clear(); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 6, 61, 3, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 9, 71, 4, 9 ) << QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPolygonZM ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7, 17 ) << QgsPoint( QgsWkbTypes::Point, 3, 13 ) + << QgsPoint( QgsWkbTypes::Point, 13, 27 ) << QgsPoint( QgsWkbTypes::Point, 7, 17 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPolygonZM ); + QVERIFY( c6.isMeasure() ); + QVERIFY( c6.is3D() ); + ls = static_cast< const QgsPolygonV2 * >( c6.geometryN( 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 6, 61, 3, 5 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( QgsWkbTypes::PointZM, 9, 71, 4, 9 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 3 ), QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) ); + ls = static_cast< const QgsPolygonV2 * >( c6.geometryN( 1 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 7, 17, 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 3, 13, 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( QgsWkbTypes::PointZM, 13, 27, 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 3 ), QgsPoint( QgsWkbTypes::PointZM, 7, 17, 0, 0 ) ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 77, 87, 7 ) << QgsPoint( QgsWkbTypes::PointZ, 83, 83, 8 ) + << QgsPoint( QgsWkbTypes::PointZ, 93, 85, 10 ) << QgsPoint( QgsWkbTypes::PointZ, 77, 87, 7 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPolygonZM ); + QVERIFY( c6.is3D() ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsPolygonV2 * >( c6.geometryN( 2 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 77, 87, 7, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 83, 83, 8, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( QgsWkbTypes::PointZM, 93, 85, 10, 0 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 3 ), QgsPoint( QgsWkbTypes::PointZM, 77, 87, 7, 0 ) ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 177, 187, 0, 9 ) << QgsPoint( QgsWkbTypes::PointM, 183, 183, 0, 11 ) + << QgsPoint( QgsWkbTypes::PointM, 185, 193, 0, 13 ) << QgsPoint( QgsWkbTypes::PointM, 177, 187, 0, 9 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.wkbType(), QgsWkbTypes::MultiPolygonZM ); + QVERIFY( c6.is3D() ); + QVERIFY( c6.isMeasure() ); + ls = static_cast< const QgsPolygonV2 * >( c6.geometryN( 3 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZM, 177, 187, 0, 9 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 1 ), QgsPoint( QgsWkbTypes::PointZM, 183, 183, 0, 11 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 2 ), QgsPoint( QgsWkbTypes::PointZM, 185, 193, 0, 13 ) ); + QCOMPARE( static_cast< const QgsLineString *>( ls->exteriorRing() )->pointN( 3 ), QgsPoint( QgsWkbTypes::PointZM, 177, 187, 0, 9 ) ); + + //clear + QgsMultiPolygonV2 c7; + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 5, 50, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 6, 61, 3, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 9, 71, 4, 15 ) << QgsPoint( QgsWkbTypes::PointZM, 5, 71, 4, 6 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c7.addGeometry( part.clone() ); + c7.addGeometry( part.clone() ); + QCOMPARE( c7.numGeometries(), 2 ); + c7.clear(); + QVERIFY( c7.isEmpty() ); + QCOMPARE( c7.numGeometries(), 0 ); + QCOMPARE( c7.nCoordinates(), 0 ); + QCOMPARE( c7.ringCount(), 0 ); + QCOMPARE( c7.partCount(), 0 ); + QVERIFY( !c7.is3D() ); + QVERIFY( !c7.isMeasure() ); + QCOMPARE( c7.wkbType(), QgsWkbTypes::MultiPolygon ); + + //clone + QgsMultiPolygonV2 c11; + std::unique_ptr< QgsMultiPolygonV2 >cloned( c11.clone() ); + QVERIFY( cloned->isEmpty() ); + c11.addGeometry( part.clone() ); + c11.addGeometry( part.clone() ); + cloned.reset( c11.clone() ); + QCOMPARE( cloned->numGeometries(), 2 ); + ls = static_cast< const QgsPolygonV2 * >( cloned->geometryN( 0 ) ); + QCOMPARE( *ls, part ); + ls = static_cast< const QgsPolygonV2 * >( cloned->geometryN( 1 ) ); + QCOMPARE( *ls, part ); + + //copy constructor + QgsMultiPolygonV2 c12; + QgsMultiPolygonV2 c13( c12 ); + QVERIFY( c13.isEmpty() ); + c12.addGeometry( part.clone() ); + c12.addGeometry( part.clone() ); + QgsMultiPolygonV2 c14( c12 ); + QCOMPARE( c14.numGeometries(), 2 ); + QCOMPARE( c14.wkbType(), QgsWkbTypes::MultiPolygonZM ); + ls = static_cast< const QgsPolygonV2 * >( c14.geometryN( 0 ) ); + QCOMPARE( *ls, part ); + ls = static_cast< const QgsPolygonV2 * >( c14.geometryN( 1 ) ); + QCOMPARE( *ls, part ); + + //assignment operator + QgsMultiPolygonV2 c15; + c15 = c13; + QCOMPARE( c15.numGeometries(), 0 ); + c15 = c14; + QCOMPARE( c15.numGeometries(), 2 ); + ls = static_cast< const QgsPolygonV2 * >( c15.geometryN( 0 ) ); + QCOMPARE( *ls, part ); + ls = static_cast< const QgsPolygonV2 * >( c15.geometryN( 1 ) ); + QCOMPARE( *ls, part ); + + //toCurveType + std::unique_ptr< QgsMultiSurface > curveType( c12.toCurveType() ); + QCOMPARE( curveType->wkbType(), QgsWkbTypes::MultiSurfaceZM ); + QCOMPARE( curveType->numGeometries(), 2 ); + const QgsPolygonV2 *curve = static_cast< const QgsPolygonV2 * >( curveType->geometryN( 0 ) ); + QCOMPARE( *curve, *static_cast< const QgsPolygonV2 * >( c12.geometryN( 0 ) ) ); + curve = static_cast< const QgsPolygonV2 * >( curveType->geometryN( 1 ) ); + QCOMPARE( *curve, *static_cast< const QgsPolygonV2 * >( c12.geometryN( 1 ) ) ); + + //to/fromWKB + QgsMultiPolygonV2 c16; + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7, 17 ) << QgsPoint( QgsWkbTypes::Point, 3, 13 ) << QgsPoint( QgsWkbTypes::Point, 9, 27 ) << QgsPoint( QgsWkbTypes::Point, 7, 17 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 27, 37 ) << QgsPoint( QgsWkbTypes::Point, 43, 43 ) << QgsPoint( QgsWkbTypes::Point, 29, 39 ) << QgsPoint( QgsWkbTypes::Point, 27, 37 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + QByteArray wkb16 = c16.asWkb(); + QgsMultiPolygonV2 c17; + QgsConstWkbPtr wkb16ptr( wkb16 ); + c17.fromWkb( wkb16ptr ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsPolygonV2 * >( c17.geometryN( 0 ) ), *static_cast< const QgsPolygonV2 * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsPolygonV2 * >( c17.geometryN( 1 ) ), *static_cast< const QgsPolygonV2 * >( c16.geometryN( 1 ) ) ); + + //parts with Z + c16.clear(); + c17.clear(); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 7, 17, 1 ) << QgsPoint( QgsWkbTypes::PointZ, 3, 13, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 9, 27, 5 ) << QgsPoint( QgsWkbTypes::PointZ, 7, 17, 1 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 27, 37, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 43, 43, 5 ) << QgsPoint( QgsWkbTypes::PointZ, 87, 54, 7 ) << QgsPoint( QgsWkbTypes::PointZ, 27, 37, 2 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr2( wkb16 ); + c17.fromWkb( wkb16ptr2 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiPolygonZ ); + QCOMPARE( *static_cast< const QgsPolygonV2 * >( c17.geometryN( 0 ) ), *static_cast< const QgsPolygonV2 * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsPolygonV2 * >( c17.geometryN( 1 ) ), *static_cast< const QgsPolygonV2 * >( c16.geometryN( 1 ) ) ); + + //parts with m + c16.clear(); + c17.clear(); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 7, 17, 0, 1 ) << QgsPoint( QgsWkbTypes::PointM, 3, 13, 0, 4 ) + << QgsPoint( QgsWkbTypes::PointM, 9, 21, 0, 3 ) << QgsPoint( QgsWkbTypes::PointM, 7, 17, 0, 1 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 27, 37, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 43, 43, 0, 5 ) + << QgsPoint( QgsWkbTypes::PointM, 37, 31, 0, 3 ) << QgsPoint( QgsWkbTypes::PointM, 27, 37, 0, 2 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr3( wkb16 ); + c17.fromWkb( wkb16ptr3 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiPolygonM ); + QCOMPARE( *static_cast< const QgsPolygonV2 * >( c17.geometryN( 0 ) ), *static_cast< const QgsPolygonV2 * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsPolygonV2 * >( c17.geometryN( 1 ) ), *static_cast< const QgsPolygonV2 * >( c16.geometryN( 1 ) ) ); + + // parts with ZM + c16.clear(); + c17.clear(); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 7, 17, 4, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 3, 13, 1, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 19, 13, 5, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 7, 17, 4, 1 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 27, 37, 6, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 43, 43, 11, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 7, 17, 4, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 27, 37, 6, 2 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c16.addGeometry( part.clone() ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr4( wkb16 ); + c17.fromWkb( wkb16ptr4 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiPolygonZM ); + QCOMPARE( *static_cast< const QgsPolygonV2 * >( c17.geometryN( 0 ) ), *static_cast< const QgsPolygonV2 * >( c16.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsPolygonV2 * >( c17.geometryN( 1 ) ), *static_cast< const QgsPolygonV2 * >( c16.geometryN( 1 ) ) ); + + //bad WKB - check for no crash + c17.clear(); + QgsConstWkbPtr nullPtr( nullptr, 0 ); + QVERIFY( !c17.fromWkb( nullPtr ) ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiPolygon ); + QgsPoint point( 1, 2 ); + QByteArray wkbPoint = point.asWkb(); + QgsConstWkbPtr wkbPointPtr( wkbPoint ); + QVERIFY( !c17.fromWkb( wkbPointPtr ) ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::MultiPolygon ); + + //to/from WKT + QgsMultiPolygonV2 c18; + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 7, 17, 4, 1 ) << QgsPoint( QgsWkbTypes::PointZM, 3, 13, 1, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 13, 19, 3, 10 ) << QgsPoint( QgsWkbTypes::PointZM, 7, 11, 2, 8 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c18.addGeometry( part.clone() ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 27, 37, 6, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 43, 43, 11, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 17, 49, 31, 53 ) << QgsPoint( QgsWkbTypes::PointZM, 27, 53, 21, 52 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + c18.addGeometry( part.clone() ); + + QString wkt = c18.asWkt(); + QVERIFY( !wkt.isEmpty() ); + QgsMultiPolygonV2 c19; + QVERIFY( c19.fromWkt( wkt ) ); + QCOMPARE( c19.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsPolygonV2 * >( c19.geometryN( 0 ) ), *static_cast< const QgsPolygonV2 * >( c18.geometryN( 0 ) ) ); + QCOMPARE( *static_cast< const QgsPolygonV2 * >( c19.geometryN( 1 ) ), *static_cast< const QgsPolygonV2 * >( c18.geometryN( 1 ) ) ); + + //bad WKT + QgsMultiPolygonV2 c20; + QVERIFY( !c20.fromWkt( "Point()" ) ); + QVERIFY( c20.isEmpty() ); + QCOMPARE( c20.numGeometries(), 0 ); + QCOMPARE( c20.wkbType(), QgsWkbTypes::MultiPolygon ); + + //as JSON + QgsMultiPolygonV2 exportC; + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 7, 17 ) << QgsPoint( QgsWkbTypes::Point, 3, 13 ) + << QgsPoint( QgsWkbTypes::Point, 7, 21 ) << QgsPoint( QgsWkbTypes::Point, 7, 17 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + exportC.addGeometry( part.clone() ); + ring.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 27, 37 ) << QgsPoint( QgsWkbTypes::Point, 43, 43 ) + << QgsPoint( QgsWkbTypes::Point, 41, 39 ) << QgsPoint( QgsWkbTypes::Point, 27, 37 ) ) ; + part.clear(); + part.setExteriorRing( ring.clone() ); + exportC.addGeometry( part.clone() ); + + // GML document for compare + QDomDocument doc( "gml" ); + + // as GML2 + QString expectedSimpleGML2( QStringLiteral( "7,17 3,13 7,21 7,1727,37 43,43 41,39 27,37" ) ); + QString res = elemToString( exportC.asGML2( doc, 1 ) ); + QGSCOMPAREGML( res, expectedSimpleGML2 ); + + //as GML3 + QString expectedSimpleGML3( QStringLiteral( "7 17 3 13 7 21 7 1727 37 43 43 41 39 27 37" ) ); + res = elemToString( exportC.asGML3( doc ) ); + QCOMPARE( res, expectedSimpleGML3 ); + + // as JSON + QString expectedSimpleJson( "{\"type\": \"MultiPolygon\", \"coordinates\": [[[ [7, 17], [3, 13], [7, 21], [7, 17]]], [[ [27, 37], [43, 43], [41, 39], [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, 19, 37 ) << QgsPoint( QgsWkbTypes::Point, 17, 27 ) ) ; + part.addInteriorRing( ring.clone() ); + exportC.addGeometry( part.clone() ); + + QString expectedJsonWithRings( "{\"type\": \"MultiPolygon\", \"coordinates\": [[[ [7, 17], [3, 13], [7, 21], [7, 17]]], [[ [27, 37], [43, 43], [41, 39], [27, 37]]], [[ [27, 37], [43, 43], [41, 39], [27, 37]], [ [17, 27], [18, 28], [19, 37], [17, 27]]]] }" ); + res = exportC.asJSON( 1 ); + QCOMPARE( res, expectedJsonWithRings ); + + QgsMultiPolygonV2 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, 8 / 3.0, 27 / 3.0 ) << QgsPoint( QgsWkbTypes::Point, 7 / 3.0, 17 / 3.0 ) ) ; + 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() ); + exportFloat.addGeometry( part.clone() ); + + QString expectedJsonPrec3( QStringLiteral( "{\"type\": \"MultiPolygon\", \"coordinates\": [[[ [2.333, 5.667], [0.6, 4.333], [2.667, 9], [2.333, 5.667]]], [[ [9, 4.111], [1.049, 1.024], [9, 4.111]]]] }" ) ); + res = exportFloat.asJSON( 3 ); + QCOMPARE( res, expectedJsonPrec3 ); + + // as GML2 + QString expectedGML2prec3( QStringLiteral( "2.333,5.667 0.6,4.333 2.667,9 2.333,5.6679,4.111 1.049,1.024 9,4.111" ) ); + res = elemToString( exportFloat.asGML2( doc, 3 ) ); + QGSCOMPAREGML( res, expectedGML2prec3 ); + + //as GML3 + QString expectedGML3prec3( QStringLiteral( "2.333 5.667 0.6 4.333 2.667 9 2.333 5.6679 4.111 1.049 1.024 9 4.111" ) ); + res = elemToString( exportFloat.asGML3( doc, 3 ) ); + QCOMPARE( res, expectedGML3prec3 ); + + // insert geometry + QgsMultiPolygonV2 rc; + rc.clear(); + rc.insertGeometry( nullptr, 0 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + rc.insertGeometry( nullptr, -1 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + rc.insertGeometry( nullptr, 100 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + + rc.insertGeometry( new QgsPoint(), 0 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + + rc.insertGeometry( part.clone(), 0 ); + QVERIFY( !rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 1 ); + + // cast + QVERIFY( !QgsMultiPolygonV2().cast( nullptr ) ); + QgsMultiPolygonV2 pCast; + QVERIFY( QgsMultiPolygonV2().cast( &pCast ) ); + QgsMultiPolygonV2 pCast2; + pCast2.fromWkt( QStringLiteral( "MultiPolygonZ()" ) ); + QVERIFY( QgsMultiPolygonV2().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "MultiPolygonM()" ) ); + QVERIFY( QgsMultiPolygonV2().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "MultiPolygonZM()" ) ); + QVERIFY( QgsMultiPolygonV2().cast( &pCast2 ) ); + + //boundary QgsMultiPolygonV2 multiPolygon1; QVERIFY( !multiPolygon1.boundary() ); @@ -4706,6 +12756,1124 @@ void TestQgsGeometry::multiPolygon() void TestQgsGeometry::geometryCollection() { + //test constructor + QgsGeometryCollection c1; + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QVERIFY( !c1.is3D() ); + QVERIFY( !c1.isMeasure() ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::GeometryCollection ); + QCOMPARE( c1.wktTypeStr(), QString( "GeometryCollection" ) ); + QCOMPARE( c1.geometryType(), QString( "GeometryCollection" ) ); + QCOMPARE( c1.dimension(), 0 ); + QVERIFY( !c1.hasCurvedSegments() ); + QCOMPARE( c1.area(), 0.0 ); + QCOMPARE( c1.perimeter(), 0.0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + QCOMPARE( c1.vertexCount( 0, 0 ), 0 ); + QCOMPARE( c1.vertexCount( 0, 1 ), 0 ); + QCOMPARE( c1.vertexCount( 1, 0 ), 0 ); + + //addGeometry + + //try with nullptr + c1.addGeometry( nullptr ); + QVERIFY( c1.isEmpty() ); + QCOMPARE( c1.nCoordinates(), 0 ); + QCOMPARE( c1.ringCount(), 0 ); + QCOMPARE( c1.partCount(), 0 ); + QCOMPARE( c1.numGeometries(), 0 ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::GeometryCollection ); + QVERIFY( !c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + + //valid geometry + QgsLineString part; + part.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) + << QgsPoint( 10, 0 ) << QgsPoint( 0, 0 ) ); + c1.addGeometry( part.clone() ); + QVERIFY( !c1.isEmpty() ); + QCOMPARE( c1.numGeometries(), 1 ); + QCOMPARE( c1.nCoordinates(), 5 ); + QCOMPARE( c1.ringCount(), 1 ); + QCOMPARE( c1.partCount(), 1 ); + QVERIFY( !c1.is3D() ); + QVERIFY( !c1.isMeasure() ); + QCOMPARE( c1.wkbType(), QgsWkbTypes::GeometryCollection ); + QCOMPARE( c1.wktTypeStr(), QString( "GeometryCollection" ) ); + QCOMPARE( c1.geometryType(), QString( "GeometryCollection" ) ); + QCOMPARE( c1.dimension(), 1 ); + QVERIFY( !c1.hasCurvedSegments() ); + QCOMPARE( c1.area(), 0.0 ); + QCOMPARE( c1.perimeter(), 0.0 ); + QVERIFY( c1.geometryN( 0 ) ); + QVERIFY( !c1.geometryN( 100 ) ); + QVERIFY( !c1.geometryN( -1 ) ); + QCOMPARE( c1.vertexCount( 0, 0 ), 5 ); + QCOMPARE( c1.vertexCount( 1, 0 ), 0 ); + + //retrieve geometry and check + QCOMPARE( *( static_cast< const QgsLineString * >( c1.geometryN( 0 ) ) ), part ); + + //initial adding of geometry should set z/m type + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) + << QgsPoint( QgsWkbTypes::PointZ, 0, 10, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 10, 10, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 10, 0, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) ); + QgsGeometryCollection c2; + c2.addGeometry( part.clone() ); + //QVERIFY( c2.is3D() ); //no meaning for collections? + //QVERIFY( !c2.isMeasure() ); //no meaning for collections? + QCOMPARE( c2.wkbType(), QgsWkbTypes::GeometryCollection ); + QCOMPARE( c2.wktTypeStr(), QString( "GeometryCollection" ) ); + QCOMPARE( c2.geometryType(), QString( "GeometryCollection" ) ); + QCOMPARE( *( static_cast< const QgsLineString * >( c2.geometryN( 0 ) ) ), part ); + QgsGeometryCollection c3; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 0, 0, 0, 1 ) + << QgsPoint( QgsWkbTypes::PointM, 0, 10, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 10, 10, 0, 3 ) + << QgsPoint( QgsWkbTypes::PointM, 10, 0, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 0, 0, 0, 1 ) ); + c3.addGeometry( part.clone() ); + //QVERIFY( !c3.is3D() ); //no meaning for collections? + //QVERIFY( c3.isMeasure() ); //no meaning for collections? + QCOMPARE( c3.wkbType(), QgsWkbTypes::GeometryCollection ); + QCOMPARE( c3.wktTypeStr(), QString( "GeometryCollection" ) ); + QCOMPARE( *( static_cast< const QgsLineString * >( c3.geometryN( 0 ) ) ), part ); + QgsGeometryCollection c4; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 2, 1 ) + << QgsPoint( QgsWkbTypes::PointZM, 0, 10, 3, 2 ) << QgsPoint( QgsWkbTypes::PointZM, 10, 10, 5, 3 ) + << QgsPoint( QgsWkbTypes::PointZM, 10, 0, 0, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 2, 1 ) ); + c4.addGeometry( part.clone() ); + //QVERIFY( c4.is3D() ); //no meaning for collections? + //QVERIFY( c4.isMeasure() ); //no meaning for collections? + QCOMPARE( c4.wkbType(), QgsWkbTypes::GeometryCollection ); + QCOMPARE( c4.wktTypeStr(), QString( "GeometryCollection" ) ); + QCOMPARE( *( static_cast< const QgsLineString * >( c4.geometryN( 0 ) ) ), part ); + + //add another part + QgsGeometryCollection c6; + part.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) + << QgsPoint( 10, 0 ) << QgsPoint( 0, 0 ) ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.vertexCount( 0, 0 ), 5 ); + part.setPoints( QgsPointSequence() << QgsPoint( 1, 1 ) << QgsPoint( 1, 9 ) << QgsPoint( 9, 9 ) + << QgsPoint( 9, 1 ) << QgsPoint( 1, 1 ) ); + c6.addGeometry( part.clone() ); + QCOMPARE( c6.vertexCount( 1, 0 ), 5 ); + QCOMPARE( c6.numGeometries(), 2 ); + QVERIFY( c6.geometryN( 0 ) ); + QCOMPARE( *static_cast< const QgsLineString * >( c6.geometryN( 1 ) ), part ); + + QgsCoordinateSequence seq = c6.coordinateSequence(); + QCOMPARE( seq, QgsCoordinateSequence() << ( QgsRingSequence() << ( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) + << QgsPoint( 10, 0 ) << QgsPoint( 0, 0 ) ) ) + << ( QgsRingSequence() << ( QgsPointSequence() << QgsPoint( 1, 1 ) << QgsPoint( 1, 9 ) << QgsPoint( 9, 9 ) + << QgsPoint( 9, 1 ) << QgsPoint( 1, 1 ) ) ) ); + QCOMPARE( c6.nCoordinates(), 10 ); + + //clear + QgsGeometryCollection c7; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) + << QgsPoint( QgsWkbTypes::PointZ, 0, 10, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 10, 10, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 10, 0, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) ); + c7.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 1, 1 ) + << QgsPoint( QgsWkbTypes::PointZ, 1, 9, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 9, 9, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 9, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 1, 1, 1 ) ); + c7.addGeometry( part.clone() ); + QCOMPARE( c7.numGeometries(), 2 ); + c7.clear(); + QVERIFY( c7.isEmpty() ); + QCOMPARE( c7.numGeometries(), 0 ); + QCOMPARE( c7.nCoordinates(), 0 ); + QCOMPARE( c7.ringCount(), 0 ); + QCOMPARE( c7.partCount(), 0 ); + QVERIFY( !c7.is3D() ); + QVERIFY( !c7.isMeasure() ); + QCOMPARE( c7.wkbType(), QgsWkbTypes::GeometryCollection ); + + //clone + QgsGeometryCollection c11; + std::unique_ptr< QgsGeometryCollection >cloned( c11.clone() ); + QVERIFY( cloned->isEmpty() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 0, 10, 2, 6 ) << QgsPoint( QgsWkbTypes::PointZM, 10, 10, 3, 7 ) + << QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4, 8 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 9 ) ); + c11.addGeometry( part.clone() ); + QgsLineString part2; + part2.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 9, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZM, 9, 9, 3, 6 ) + << QgsPoint( QgsWkbTypes::PointZM, 9, 1, 4, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 7 ) ); + c11.addGeometry( part2.clone() ); + cloned.reset( c11.clone() ); + QCOMPARE( cloned->numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsLineString * >( cloned->geometryN( 0 ) ), part ); + QCOMPARE( *static_cast< const QgsLineString * >( cloned->geometryN( 1 ) ), part2 ); + + //copy constructor + QgsGeometryCollection c12; + QgsGeometryCollection c13( c12 ); + QVERIFY( c13.isEmpty() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 0, 10, 2, 6 ) << QgsPoint( QgsWkbTypes::PointZM, 10, 10, 3, 7 ) + << QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4, 8 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 9 ) ); + c12.addGeometry( part.clone() ); + part2.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 9, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZM, 9, 9, 3, 6 ) + << QgsPoint( QgsWkbTypes::PointZM, 9, 1, 4, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 7 ) ); + c12.addGeometry( part2.clone() ); + QgsGeometryCollection c14( c12 ); + QCOMPARE( c14.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsLineString * >( c14.geometryN( 0 ) ), part ); + QCOMPARE( *static_cast< const QgsLineString * >( c14.geometryN( 1 ) ), part2 ); + + //assignment operator + QgsGeometryCollection c15; + c15 = c13; + QCOMPARE( c15.numGeometries(), 0 ); + c15 = c14; + QCOMPARE( c15.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsLineString * >( c15.geometryN( 0 ) ), part ); + QCOMPARE( *static_cast< const QgsLineString * >( c15.geometryN( 1 ) ), part2 ); + + //toCurveType + std::unique_ptr< QgsGeometryCollection > curveType( c12.toCurveType() ); + QCOMPARE( curveType->wkbType(), QgsWkbTypes::GeometryCollection ); + QCOMPARE( curveType->numGeometries(), 2 ); + const QgsCompoundCurve *curve = static_cast< const QgsCompoundCurve * >( curveType->geometryN( 0 ) ); + QCOMPARE( curve->numPoints(), 5 ); + QCOMPARE( curve->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 5 ) ); + QCOMPARE( curve->vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( QgsWkbTypes::PointZM, 0, 10, 2, 6 ) ); + QCOMPARE( curve->vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( QgsWkbTypes::PointZM, 10, 10, 3, 7 ) ); + QCOMPARE( curve->vertexAt( QgsVertexId( 0, 0, 3 ) ), QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4, 8 ) ); + QCOMPARE( curve->vertexAt( QgsVertexId( 0, 0, 4 ) ), QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 9 ) ); + curve = static_cast< const QgsCompoundCurve * >( curveType->geometryN( 1 ) ); + QCOMPARE( curve->numPoints(), 5 ); + QCOMPARE( curve->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 2 ) ); + QCOMPARE( curve->vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( QgsWkbTypes::PointZM, 1, 9, 2, 3 ) ); + QCOMPARE( curve->vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( QgsWkbTypes::PointZM, 9, 9, 3, 6 ) ); + QCOMPARE( curve->vertexAt( QgsVertexId( 0, 0, 3 ) ), QgsPoint( QgsWkbTypes::PointZM, 9, 1, 4, 4 ) ); + QCOMPARE( curve->vertexAt( QgsVertexId( 0, 0, 4 ) ), QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 7 ) ); + + //to/fromWKB + QgsGeometryCollection c16; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 0, 0 ) + << QgsPoint( QgsWkbTypes::Point, 0, 10 ) << QgsPoint( QgsWkbTypes::Point, 10, 10 ) + << QgsPoint( QgsWkbTypes::Point, 10, 0 ) << QgsPoint( QgsWkbTypes::Point, 0, 0 ) ); + c16.addGeometry( part.clone() ); + part2.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 1, 1 ) + << QgsPoint( QgsWkbTypes::Point, 1, 9 ) << QgsPoint( QgsWkbTypes::Point, 9, 9 ) + << QgsPoint( QgsWkbTypes::Point, 9, 1 ) << QgsPoint( QgsWkbTypes::Point, 1, 1 ) ); + c16.addGeometry( part2.clone() ); + QByteArray wkb16 = c16.asWkb(); + QgsGeometryCollection c17; + QgsConstWkbPtr wkb16ptr( wkb16 ); + c17.fromWkb( wkb16ptr ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 0 ) ), part ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 1 ) ), part2 ); + + //parts with Z + c16.clear(); + c17.clear(); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) + << QgsPoint( QgsWkbTypes::PointZ, 0, 10, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 10, 10, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 10, 0, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 0, 0, 1 ) ); + c16.addGeometry( part.clone() ); + part2.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZ, 1, 1, 1 ) + << QgsPoint( QgsWkbTypes::PointZ, 1, 9, 2 ) << QgsPoint( QgsWkbTypes::PointZ, 9, 9, 3 ) + << QgsPoint( QgsWkbTypes::PointZ, 9, 1, 4 ) << QgsPoint( QgsWkbTypes::PointZ, 1, 1, 1 ) ); + c16.addGeometry( part2.clone() ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr2( wkb16 ); + c17.fromWkb( wkb16ptr2 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 0 ) ), part ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 1 ) ), part2 ); + + + //parts with m + c16.clear(); + c17.clear(); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 0, 0, 0, 1 ) + << QgsPoint( QgsWkbTypes::PointM, 0, 10, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 10, 10, 0, 3 ) + << QgsPoint( QgsWkbTypes::PointM, 10, 0, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 0, 0, 0, 1 ) ); + c16.addGeometry( part.clone() ); + part2.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 1, 0, 1 ) + << QgsPoint( QgsWkbTypes::PointM, 1, 9, 0, 2 ) << QgsPoint( QgsWkbTypes::PointM, 9, 9, 0, 3 ) + << QgsPoint( QgsWkbTypes::PointM, 9, 1, 0, 4 ) << QgsPoint( QgsWkbTypes::PointM, 1, 1, 0, 1 ) ); + c16.addGeometry( part2.clone() ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr3( wkb16 ); + c17.fromWkb( wkb16ptr3 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 0 ) ), part ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 1 ) ), part2 ); + + // parts with ZM + c16.clear(); + c17.clear(); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 0, 10, 2, 6 ) << QgsPoint( QgsWkbTypes::PointZM, 10, 10, 3, 7 ) + << QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4, 8 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 9 ) ); + c16.addGeometry( part.clone() ); + part2.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 9, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZM, 9, 9, 3, 6 ) + << QgsPoint( QgsWkbTypes::PointZM, 9, 1, 4, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 7 ) ); + c16.addGeometry( part2.clone() ); + wkb16 = c16.asWkb(); + QgsConstWkbPtr wkb16ptr4( wkb16 ); + c17.fromWkb( wkb16ptr4 ); + QCOMPARE( c17.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 0 ) ), part ); + QCOMPARE( *static_cast< const QgsLineString * >( c17.geometryN( 1 ) ), part2 ); + + + //bad WKB - check for no crash + c17.clear(); + QgsConstWkbPtr nullPtr( nullptr, 0 ); + QVERIFY( !c17.fromWkb( nullPtr ) ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::GeometryCollection ); + QgsPoint point( 1, 2 ); + QByteArray wkbPoint = point.asWkb(); + QgsConstWkbPtr wkbPointPtr( wkbPoint ); + QVERIFY( !c17.fromWkb( wkbPointPtr ) ); + QCOMPARE( c17.wkbType(), QgsWkbTypes::GeometryCollection ); + + //to/from WKT + QgsGeometryCollection c18; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 0, 10, 2, 6 ) << QgsPoint( QgsWkbTypes::PointZM, 10, 10, 3, 7 ) + << QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4, 8 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 9 ) ); + c18.addGeometry( part.clone() ); + part2.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 9, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZM, 9, 9, 3, 6 ) + << QgsPoint( QgsWkbTypes::PointZM, 9, 1, 4, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 7 ) ); + c18.addGeometry( part2.clone() ); + + QString wkt = c18.asWkt(); + QVERIFY( !wkt.isEmpty() ); + QgsGeometryCollection c19; + QVERIFY( c19.fromWkt( wkt ) ); + QCOMPARE( c19.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsLineString * >( c19.geometryN( 0 ) ), part ); + QCOMPARE( *static_cast< const QgsLineString * >( c19.geometryN( 1 ) ), part2 ); + + //bad WKT + QgsGeometryCollection c20; + QVERIFY( !c20.fromWkt( "Point()" ) ); + QVERIFY( c20.isEmpty() ); + QCOMPARE( c20.numGeometries(), 0 ); + QCOMPARE( c20.wkbType(), QgsWkbTypes::GeometryCollection ); + + //as JSON + QgsGeometryCollection exportC; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 0, 0 ) + << QgsPoint( QgsWkbTypes::Point, 0, 10 ) << QgsPoint( QgsWkbTypes::Point, 10, 10 ) + << QgsPoint( QgsWkbTypes::Point, 10, 0 ) << QgsPoint( QgsWkbTypes::Point, 0, 0 ) ); + exportC.addGeometry( part.clone() ); + + // GML document for compare + QDomDocument doc( "gml" ); + + + // as GML2 + QString expectedSimpleGML2( QStringLiteral( "0,0 0,10 10,10 10,0 0,0" ) ); + QString res = elemToString( exportC.asGML2( doc ) ); + QGSCOMPAREGML( res, expectedSimpleGML2 ); + + //as GML3 + QString expectedSimpleGML3( QStringLiteral( "0 0 0 10 10 10 10 0 0 0" ) ); + res = elemToString( exportC.asGML3( doc ) ); + QCOMPARE( res, expectedSimpleGML3 ); + + // as JSON + QString expectedSimpleJson( "{\"type\": \"GeometryCollection\", \"geometries\": [{\"type\": \"LineString\", \"coordinates\": [ [0, 0], [0, 10], [10, 10], [10, 0], [0, 0]]}] }" ); + res = exportC.asJSON(); + QCOMPARE( res, expectedSimpleJson ); + + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 1, 1 ) + << QgsPoint( QgsWkbTypes::Point, 1, 9 ) << QgsPoint( QgsWkbTypes::Point, 9, 9 ) + << QgsPoint( QgsWkbTypes::Point, 9, 1 ) << QgsPoint( QgsWkbTypes::Point, 1, 1 ) ); + exportC.addGeometry( part.clone() ); + + QString expectedJson( QStringLiteral( "{\"type\": \"GeometryCollection\", \"geometries\": [{\"type\": \"LineString\", \"coordinates\": [ [0, 0], [0, 10], [10, 10], [10, 0], [0, 0]]}, {\"type\": \"LineString\", \"coordinates\": [ [1, 1], [1, 9], [9, 9], [9, 1], [1, 1]]}] }" ) ); + res = exportC.asJSON(); + QCOMPARE( res, expectedJson ); + + QgsGeometryCollection exportFloat; + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 10 / 9.0, 10 / 9.0 ) + << QgsPoint( QgsWkbTypes::Point, 10 / 9.0, 100 / 9.0 ) << QgsPoint( QgsWkbTypes::Point, 100 / 9.0, 100 / 9.0 ) + << QgsPoint( QgsWkbTypes::Point, 100 / 9.0, 10 / 9.0 ) << QgsPoint( QgsWkbTypes::Point, 10 / 9.0, 10 / 9.0 ) ); + exportFloat.addGeometry( part.clone() ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::Point, 2 / 3.0, 2 / 3.0 ) + << QgsPoint( QgsWkbTypes::Point, 2 / 3.0, 4 / 3.0 ) << QgsPoint( QgsWkbTypes::Point, 4 / 3.0, 4 / 3.0 ) + << QgsPoint( QgsWkbTypes::Point, 4 / 3.0, 2 / 3.0 ) << QgsPoint( QgsWkbTypes::Point, 2 / 3.0, 2 / 3.0 ) ); + exportFloat.addGeometry( part.clone() ); + + QString expectedJsonPrec3( QStringLiteral( "{\"type\": \"GeometryCollection\", \"geometries\": [{\"type\": \"LineString\", \"coordinates\": [ [1.111, 1.111], [1.111, 11.111], [11.111, 11.111], [11.111, 1.111], [1.111, 1.111]]}, {\"type\": \"LineString\", \"coordinates\": [ [0.667, 0.667], [0.667, 1.333], [1.333, 1.333], [1.333, 0.667], [0.667, 0.667]]}] }" ) ); + res = exportFloat.asJSON( 3 ); + QCOMPARE( res, expectedJsonPrec3 ); + + // as GML2 + QString expectedGML2( QStringLiteral( "0,0 0,10 10,10 10,0 0,01,1 1,9 9,9 9,1 1,1" ) ); + res = elemToString( exportC.asGML2( doc ) ); + QGSCOMPAREGML( res, expectedGML2 ); + QString expectedGML2prec3( QStringLiteral( "1.111,1.111 1.111,11.111 11.111,11.111 11.111,1.111 1.111,1.1110.667,0.667 0.667,1.333 1.333,1.333 1.333,0.667 0.667,0.667" ) ); + res = elemToString( exportFloat.asGML2( doc, 3 ) ); + QGSCOMPAREGML( res, expectedGML2prec3 ); + + //as GML3 + QString expectedGML3( QStringLiteral( "0 0 0 10 10 10 10 0 0 01 1 1 9 9 9 9 1 1 1" ) ); + res = elemToString( exportC.asGML3( doc ) ); + QCOMPARE( res, expectedGML3 ); + QString expectedGML3prec3( QStringLiteral( "1.111 1.111 1.111 11.111 11.111 11.111 11.111 1.111 1.111 1.1110.667 0.667 0.667 1.333 1.333 1.333 1.333 0.667 0.667 0.667" ) ); + res = elemToString( exportFloat.asGML3( doc, 3 ) ); + QCOMPARE( res, expectedGML3prec3 ); + + // remove geometry + QgsGeometryCollection rc; + // no crash! + rc.removeGeometry( -1 ); + rc.removeGeometry( 0 ); + rc.removeGeometry( 100 ); + part.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 0, 10, 2, 6 ) << QgsPoint( QgsWkbTypes::PointZM, 10, 10, 3, 7 ) + << QgsPoint( QgsWkbTypes::PointZM, 10, 0, 4, 8 ) << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 9 ) ); + rc.addGeometry( part.clone() ); + part2.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 9, 2, 3 ) << QgsPoint( QgsWkbTypes::PointZM, 9, 9, 3, 6 ) + << QgsPoint( QgsWkbTypes::PointZM, 9, 1, 4, 4 ) << QgsPoint( QgsWkbTypes::PointZM, 1, 1, 1, 7 ) ); + rc.addGeometry( part2.clone() ); + // no crash + rc.removeGeometry( -1 ); + rc.removeGeometry( 100 ); + + rc.removeGeometry( 0 ); + QCOMPARE( rc.numGeometries(), 1 ); + QCOMPARE( *static_cast< const QgsLineString * >( rc.geometryN( 0 ) ), part2 ); + + rc.addGeometry( part.clone() ); + rc.removeGeometry( 1 ); + QCOMPARE( rc.numGeometries(), 1 ); + QCOMPARE( *static_cast< const QgsLineString * >( rc.geometryN( 0 ) ), part2 ); + rc.removeGeometry( 0 ); + QCOMPARE( rc.numGeometries(), 0 ); + + + // insert geometry + rc.clear(); + rc.insertGeometry( nullptr, 0 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + rc.insertGeometry( nullptr, -1 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + rc.insertGeometry( nullptr, 100 ); + QVERIFY( rc.isEmpty() ); + QCOMPARE( rc.numGeometries(), 0 ); + + rc.insertGeometry( part.clone(), 0 ); + QCOMPARE( rc.numGeometries(), 1 ); + QCOMPARE( *static_cast< const QgsLineString * >( rc.geometryN( 0 ) ), part ); + rc.insertGeometry( part2.clone(), 0 ); + QCOMPARE( rc.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsLineString * >( rc.geometryN( 0 ) ), part2 ); + QCOMPARE( *static_cast< const QgsLineString * >( rc.geometryN( 1 ) ), part ); + rc.removeGeometry( 0 ); + rc.insertGeometry( part2.clone(), 1 ); + QCOMPARE( rc.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsLineString * >( rc.geometryN( 0 ) ), part ); + QCOMPARE( *static_cast< const QgsLineString * >( rc.geometryN( 1 ) ), part2 ); + rc.removeGeometry( 1 ); + rc.insertGeometry( part2.clone(), 2 ); + QCOMPARE( rc.numGeometries(), 2 ); + QCOMPARE( *static_cast< const QgsLineString * >( rc.geometryN( 0 ) ), part ); + QCOMPARE( *static_cast< const QgsLineString * >( rc.geometryN( 1 ) ), part2 ); + + // cast + QVERIFY( !QgsGeometryCollection().cast( nullptr ) ); + QgsGeometryCollection pCast; + QVERIFY( QgsGeometryCollection().cast( &pCast ) ); + QgsGeometryCollection pCast2; + pCast2.fromWkt( QStringLiteral( "GeometryCollectionZ(PolygonZ((0 0 0, 0 1 1, 1 0 2, 0 0 0)))" ) ); + QVERIFY( QgsGeometryCollection().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "GeometryCollectionM(PolygonM((0 0 1, 0 1 2, 1 0 3, 0 0 1)))" ) ); + QVERIFY( QgsGeometryCollection().cast( &pCast2 ) ); + pCast2.fromWkt( QStringLiteral( "GeometryCollectionZM(PolygonZM((0 0 0 1, 0 1 1 2, 1 0 2 3, 0 0 0 1)))" ) ); + QVERIFY( QgsGeometryCollection().cast( &pCast2 ) ); + + //transform + //CRS transform + QgsCoordinateReferenceSystem sourceSrs; + sourceSrs.createFromSrid( 3994 ); + QgsCoordinateReferenceSystem destSrs; + destSrs.createFromSrid( 4202 ); // want a transform with ellipsoid change + QgsCoordinateTransform tr( sourceSrs, destSrs ); + + // 2d CRS transform + QgsGeometryCollection pTransform; + QgsLineString l21; + l21.setPoints( QgsPointSequence() << QgsPoint( 6374985, -3626584 ) + << QgsPoint( 6274985, -3526584 ) + << QgsPoint( 6474985, -3526584 ) + << QgsPoint( 6374985, -3626584 ) ); + pTransform.addGeometry( l21.clone() ); + pTransform.addGeometry( l21.clone() ); + pTransform.transform( tr, QgsCoordinateTransform::ForwardTransform ); + const QgsLineString *extR = static_cast< const QgsLineString * >( pTransform.geometryN( 0 ) ); + QGSCOMPARENEAR( extR->pointN( 0 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 0 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).x(), 174.581448, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).x(), 176.958633, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( extR->boundingBox().xMinimum(), 174.581448, 0.001 ); + QGSCOMPARENEAR( extR->boundingBox().yMinimum(), -39.724, 0.001 ); + QGSCOMPARENEAR( extR->boundingBox().xMaximum(), 176.959, 0.001 ); + QGSCOMPARENEAR( extR->boundingBox().yMaximum(), -38.7999, 0.001 ); + const QgsLineString *intR = static_cast< const QgsLineString * >( pTransform.geometryN( 1 ) ); + QGSCOMPARENEAR( intR->pointN( 0 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 0 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).x(), 174.581448, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).x(), 176.958633, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().xMinimum(), 174.581448, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().yMinimum(), -39.724, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().xMaximum(), 176.959, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().yMaximum(), -38.7999, 0.001 ); + + //3d CRS transform + QgsLineString l22; + l22.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 6374985, -3626584, 1, 2 ) + << QgsPoint( QgsWkbTypes::PointZM, 6274985, -3526584, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 6474985, -3526584, 5, 6 ) + << QgsPoint( QgsWkbTypes::PointZM, 6374985, -3626584, 1, 2 ) ); + pTransform.clear(); + pTransform.addGeometry( l22.clone() ); + pTransform.addGeometry( l22.clone() ); + pTransform.transform( tr, QgsCoordinateTransform::ForwardTransform ); + extR = static_cast< const QgsLineString * >( pTransform.geometryN( 0 ) ); + QGSCOMPARENEAR( extR->pointN( 0 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 0 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 0 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 0 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).x(), 174.581448, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).x(), 176.958633, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).z(), 5.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).m(), 6.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( extR->boundingBox().xMinimum(), 174.581448, 0.001 ); + QGSCOMPARENEAR( extR->boundingBox().yMinimum(), -39.724, 0.001 ); + QGSCOMPARENEAR( extR->boundingBox().xMaximum(), 176.959, 0.001 ); + QGSCOMPARENEAR( extR->boundingBox().yMaximum(), -38.7999, 0.001 ); + intR = static_cast< const QgsLineString * >( pTransform.geometryN( 1 ) ); + QGSCOMPARENEAR( intR->pointN( 0 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 0 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 0 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 0 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).x(), 174.581448, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).x(), 176.958633, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).y(), -38.7999, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).z(), 5.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).m(), 6.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).x(), 175.771, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).y(), -39.724, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().xMinimum(), 174.581448, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().yMinimum(), -39.724, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().xMaximum(), 176.959, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().yMaximum(), -38.7999, 0.001 ); + + //reverse transform + pTransform.transform( tr, QgsCoordinateTransform::ReverseTransform ); + extR = static_cast< const QgsLineString * >( pTransform.geometryN( 0 ) ); + QGSCOMPARENEAR( extR->pointN( 0 ).x(), 6374984, 100 ); + QGSCOMPARENEAR( extR->pointN( 0 ).y(), -3626584, 100 ); + QGSCOMPARENEAR( extR->pointN( 0 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 0 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).x(), 6274984, 100 ); + QGSCOMPARENEAR( extR->pointN( 1 ).y(), -3526584, 100 ); + QGSCOMPARENEAR( extR->pointN( 1 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).x(), 6474984, 100 ); + QGSCOMPARENEAR( extR->pointN( 2 ).y(), -3526584, 100 ); + QGSCOMPARENEAR( extR->pointN( 2 ).z(), 5.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).m(), 6.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).x(), 6374984, 100 ); + QGSCOMPARENEAR( extR->pointN( 3 ).y(), -3626584, 100 ); + QGSCOMPARENEAR( extR->pointN( 3 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( extR->boundingBox().xMinimum(), 6274984, 100 ); + QGSCOMPARENEAR( extR->boundingBox().yMinimum(), -3626584, 100 ); + QGSCOMPARENEAR( extR->boundingBox().xMaximum(), 6474984, 100 ); + QGSCOMPARENEAR( extR->boundingBox().yMaximum(), -3526584, 100 ); + intR = static_cast< const QgsLineString * >( pTransform.geometryN( 1 ) ); + QGSCOMPARENEAR( intR->pointN( 0 ).x(), 6374984, 100 ); + QGSCOMPARENEAR( intR->pointN( 0 ).y(), -3626584, 100 ); + QGSCOMPARENEAR( intR->pointN( 0 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 0 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).x(), 6274984, 100 ); + QGSCOMPARENEAR( intR->pointN( 1 ).y(), -3526584, 100 ); + QGSCOMPARENEAR( intR->pointN( 1 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).x(), 6474984, 100 ); + QGSCOMPARENEAR( intR->pointN( 2 ).y(), -3526584, 100 ); + QGSCOMPARENEAR( intR->pointN( 2 ).z(), 5.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).m(), 6.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).x(), 6374984, 100 ); + QGSCOMPARENEAR( intR->pointN( 3 ).y(), -3626584, 100 ); + QGSCOMPARENEAR( intR->pointN( 3 ).z(), 1.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).m(), 2.0, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().xMinimum(), 6274984, 100 ); + QGSCOMPARENEAR( intR->boundingBox().yMinimum(), -3626584, 100 ); + QGSCOMPARENEAR( intR->boundingBox().xMaximum(), 6474984, 100 ); + QGSCOMPARENEAR( intR->boundingBox().yMaximum(), -3526584, 100 ); + + //z value transform + pTransform.transform( tr, QgsCoordinateTransform::ForwardTransform, true ); + extR = static_cast< const QgsLineString * >( pTransform.geometryN( 0 ) ); + QGSCOMPARENEAR( extR->pointN( 0 ).z(), -19.249066, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).z(), -19.148357, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).z(), -19.092128, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).z(), -19.249066, 0.001 ); + intR = static_cast< const QgsLineString * >( pTransform.geometryN( 1 ) ); + QGSCOMPARENEAR( intR->pointN( 0 ).z(), -19.249066, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).z(), -19.148357, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).z(), -19.092128, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).z(), -19.249066, 0.001 ); + pTransform.transform( tr, QgsCoordinateTransform::ReverseTransform, true ); + extR = static_cast< const QgsLineString * >( pTransform.geometryN( 0 ) ); + QGSCOMPARENEAR( extR->pointN( 0 ).z(), 1, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).z(), 3, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).z(), 5, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).z(), 1, 0.001 ); + intR = static_cast< const QgsLineString * >( pTransform.geometryN( 1 ) ); + QGSCOMPARENEAR( intR->pointN( 0 ).z(), 1, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).z(), 3, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).z(), 5, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).z(), 1, 0.001 ); + + //QTransform transform + QTransform qtr = QTransform::fromScale( 2, 3 ); + QgsLineString l23; + l23.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) + << QgsPoint( QgsWkbTypes::PointZM, 11, 12, 13, 14 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 12, 23, 24 ) + << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 ) ); + QgsGeometryCollection pTransform2; + pTransform2.addGeometry( l23.clone() ); + pTransform2.addGeometry( l23.clone() ); + pTransform2.transform( qtr ); + + extR = static_cast< const QgsLineString * >( pTransform2.geometryN( 0 ) ); + QGSCOMPARENEAR( extR->pointN( 0 ).x(), 2, 100 ); + QGSCOMPARENEAR( extR->pointN( 0 ).y(), 6, 100 ); + QGSCOMPARENEAR( extR->pointN( 0 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 0 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).x(), 22, 100 ); + QGSCOMPARENEAR( extR->pointN( 1 ).y(), 36, 100 ); + QGSCOMPARENEAR( extR->pointN( 1 ).z(), 13.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 1 ).m(), 14.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).x(), 2, 100 ); + QGSCOMPARENEAR( extR->pointN( 2 ).y(), 36, 100 ); + QGSCOMPARENEAR( extR->pointN( 2 ).z(), 23.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 2 ).m(), 24.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).x(), 2, 100 ); + QGSCOMPARENEAR( extR->pointN( 3 ).y(), 6, 100 ); + QGSCOMPARENEAR( extR->pointN( 3 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( extR->pointN( 3 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( extR->boundingBox().xMinimum(), 2, 0.001 ); + QGSCOMPARENEAR( extR->boundingBox().yMinimum(), 6, 0.001 ); + QGSCOMPARENEAR( extR->boundingBox().xMaximum(), 22, 0.001 ); + QGSCOMPARENEAR( extR->boundingBox().yMaximum(), 36, 0.001 ); + intR = static_cast< const QgsLineString * >( pTransform2.geometryN( 1 ) ); + QGSCOMPARENEAR( intR->pointN( 0 ).x(), 2, 100 ); + QGSCOMPARENEAR( intR->pointN( 0 ).y(), 6, 100 ); + QGSCOMPARENEAR( intR->pointN( 0 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 0 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).x(), 22, 100 ); + QGSCOMPARENEAR( intR->pointN( 1 ).y(), 36, 100 ); + QGSCOMPARENEAR( intR->pointN( 1 ).z(), 13.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 1 ).m(), 14.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).x(), 2, 100 ); + QGSCOMPARENEAR( intR->pointN( 2 ).y(), 36, 100 ); + QGSCOMPARENEAR( intR->pointN( 2 ).z(), 23.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 2 ).m(), 24.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).x(), 2, 100 ); + QGSCOMPARENEAR( intR->pointN( 3 ).y(), 6, 100 ); + QGSCOMPARENEAR( intR->pointN( 3 ).z(), 3.0, 0.001 ); + QGSCOMPARENEAR( intR->pointN( 3 ).m(), 4.0, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().xMinimum(), 2, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().yMinimum(), 6, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().xMaximum(), 22, 0.001 ); + QGSCOMPARENEAR( intR->boundingBox().yMaximum(), 36, 0.001 ); + + + // closestSegment + QgsPoint pt; + QgsVertexId v; + bool leftOf = false; + QgsGeometryCollection empty; + ( void )empty.closestSegment( QgsPoint( 1, 2 ), pt, v ); // empty collection, just want no crash + + QgsGeometryCollection p21; + QgsLineString p21ls; + p21ls.setPoints( QgsPointSequence() << QgsPoint( 5, 10 ) << QgsPoint( 7, 12 ) << QgsPoint( 5, 15 ) << QgsPoint( 5, 10 ) ); + p21.addGeometry( p21ls.clone() ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 4, 11 ), pt, v, &leftOf ), 1.0, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5, 0.01 ); + QGSCOMPARENEAR( pt.y(), 11, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 3 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 8, 11 ), pt, v, &leftOf ), 2.0, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 7, 0.01 ); + QGSCOMPARENEAR( pt.y(), 12, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 6, 11.5 ), pt, v, &leftOf ), 0.125000, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 6.25, 0.01 ); + QGSCOMPARENEAR( pt.y(), 11.25, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, true ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 7, 16 ), pt, v, &leftOf ), 4.923077, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5.153846, 0.01 ); + QGSCOMPARENEAR( pt.y(), 14.769231, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 5.5, 13.5 ), pt, v, &leftOf ), 0.173077, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5.846154, 0.01 ); + QGSCOMPARENEAR( pt.y(), 13.730769, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, true ); + // point directly on segment + QCOMPARE( p21.closestSegment( QgsPoint( 5, 15 ), pt, v, &leftOf ), 0.0 ); + QCOMPARE( pt, QgsPoint( 5, 15 ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + // with interior ring + p21ls.setPoints( QgsPointSequence() << QgsPoint( 6, 11.5 ) << QgsPoint( 6.5, 12 ) << QgsPoint( 6, 13 ) << QgsPoint( 6, 11.5 ) ); + p21.addGeometry( p21ls.clone() ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 4, 11 ), pt, v, &leftOf ), 1.0, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5, 0.01 ); + QGSCOMPARENEAR( pt.y(), 11, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 3 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 8, 11 ), pt, v, &leftOf ), 2.0, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 7, 0.01 ); + QGSCOMPARENEAR( pt.y(), 12, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 6, 11.4 ), pt, v, &leftOf ), 0.01, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 6.0, 0.01 ); + QGSCOMPARENEAR( pt.y(), 11.5, 0.01 ); + QCOMPARE( v, QgsVertexId( 1, 0, 1 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 7, 16 ), pt, v, &leftOf ), 4.923077, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5.153846, 0.01 ); + QGSCOMPARENEAR( pt.y(), 14.769231, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, false ); + QGSCOMPARENEAR( p21.closestSegment( QgsPoint( 5.5, 13.5 ), pt, v, &leftOf ), 0.173077, 0.0001 ); + QGSCOMPARENEAR( pt.x(), 5.846154, 0.01 ); + QGSCOMPARENEAR( pt.y(), 13.730769, 0.01 ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( leftOf, true ); + // point directly on segment + QCOMPARE( p21.closestSegment( QgsPoint( 6, 13 ), pt, v, &leftOf ), 0.0 ); + QCOMPARE( pt, QgsPoint( 6, 13 ) ); + QCOMPARE( v, QgsVertexId( 1, 0, 2 ) ); + + //nextVertex + QgsGeometryCollection p22; + QVERIFY( !p22.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 0, -2 ); + QVERIFY( !p22.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 0, 10 ); + QVERIFY( !p22.nextVertex( v, pt ) ); + QgsLineString lp22; + lp22.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 12 ) << QgsPoint( 1, 2 ) ); + p22.addGeometry( lp22.clone() ); + v = QgsVertexId( 0, 0, 4 ); //out of range + QVERIFY( !p22.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 0, -5 ); + QVERIFY( p22.nextVertex( v, pt ) ); + v = QgsVertexId( 0, 0, -1 ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 0 ) ); + QCOMPARE( pt, QgsPoint( 1, 2 ) ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 1 ) ); + QCOMPARE( pt, QgsPoint( 11, 12 ) ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 2 ) ); + QCOMPARE( pt, QgsPoint( 1, 12 ) ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 0, 0, 3 ) ); + QCOMPARE( pt, QgsPoint( 1, 2 ) ); + v = QgsVertexId( 1, 0, 0 ); + QVERIFY( !p22.nextVertex( v, pt ) ); + v = QgsVertexId( 1, 0, 0 ); + // add another part + lp22.setPoints( QgsPointSequence() << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) << QgsPoint( 11, 22 ) << QgsPoint( 11, 12 ) ); + p22.addGeometry( lp22.clone() ); + v = QgsVertexId( 1, 0, 4 ); //out of range + QVERIFY( !p22.nextVertex( v, pt ) ); + v = QgsVertexId( 1, 0, -5 ); + QVERIFY( p22.nextVertex( v, pt ) ); + v = QgsVertexId( 1, 0, -1 ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 1, 0, 0 ) ); + QCOMPARE( pt, QgsPoint( 11, 12 ) ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 1, 0, 1 ) ); + QCOMPARE( pt, QgsPoint( 21, 22 ) ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 1, 0, 2 ) ); + QCOMPARE( pt, QgsPoint( 11, 22 ) ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 1, 0, 3 ) ); + QCOMPARE( pt, QgsPoint( 11, 12 ) ); + v = QgsVertexId( 2, 0, 0 ); + QVERIFY( !p22.nextVertex( v, pt ) ); + v = QgsVertexId( 1, 1, 0 ); + QVERIFY( p22.nextVertex( v, pt ) ); + QCOMPARE( v, QgsVertexId( 1, 1, 1 ) ); //test that part number is maintained + QCOMPARE( pt, QgsPoint( 21, 22 ) ); + + + // dropZValue + QgsGeometryCollection p23; + p23.dropZValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::GeometryCollection ); + QgsLineString lp23; + lp23.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 12 ) << QgsPoint( 1, 2 ) ); + p23.addGeometry( lp23.clone() ); + p23.addGeometry( lp23.clone() ); + QCOMPARE( p23.wkbType(), QgsWkbTypes::GeometryCollection ); + p23.dropZValue(); // not z + QCOMPARE( p23.wkbType(), QgsWkbTypes::GeometryCollection ); + QCOMPARE( p23.geometryN( 0 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.geometryN( 0 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + QCOMPARE( p23.geometryN( 1 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.geometryN( 1 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + // with z + lp23.setPoints( QgsPointSequence() << QgsPoint( 1, 2, 3 ) << QgsPoint( 11, 12, 13 ) << QgsPoint( 1, 12, 23 ) << QgsPoint( 1, 2, 3 ) ); + p23.clear(); + p23.addGeometry( lp23.clone() ); + p23.addGeometry( lp23.clone() ); + p23.dropZValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::GeometryCollection ); + QCOMPARE( p23.geometryN( 0 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.geometryN( 0 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + QCOMPARE( p23.geometryN( 1 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.geometryN( 1 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + // with zm + lp23.setPoints( QgsPointSequence() << QgsPoint( 1, 2, 3, 4 ) << QgsPoint( 11, 12, 13, 14 ) << QgsPoint( 1, 12, 23, 24 ) << QgsPoint( 1, 2, 3, 4 ) ); + p23.clear(); + p23.addGeometry( lp23.clone() ); + p23.addGeometry( lp23.clone() ); + p23.dropZValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::GeometryCollection ); + QCOMPARE( p23.geometryN( 0 )->wkbType(), QgsWkbTypes::LineStringM ); + QCOMPARE( static_cast< const QgsLineString *>( p23.geometryN( 0 ) )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) ); + QCOMPARE( p23.geometryN( 1 )->wkbType(), QgsWkbTypes::LineStringM ); + QCOMPARE( static_cast< const QgsLineString *>( p23.geometryN( 1 ) )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 4 ) ); + + + // dropMValue + p23.clear(); + p23.dropMValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::GeometryCollection ); + lp23.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 1, 12 ) << QgsPoint( 1, 2 ) ); + p23.addGeometry( lp23.clone() ); + p23.addGeometry( lp23.clone() ); + QCOMPARE( p23.wkbType(), QgsWkbTypes::GeometryCollection ); + p23.dropMValue(); // not zm + QCOMPARE( p23.wkbType(), QgsWkbTypes::GeometryCollection ); + QCOMPARE( p23.geometryN( 0 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.geometryN( 0 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + QCOMPARE( p23.geometryN( 1 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.geometryN( 1 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + // with m + lp23.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) << QgsPoint( QgsWkbTypes::PointM, 11, 12, 0, 13 ) << QgsPoint( QgsWkbTypes::PointM, 1, 12, 0, 23 ) << QgsPoint( QgsWkbTypes::PointM, 1, 2, 0, 3 ) ); + p23.clear(); + p23.addGeometry( lp23.clone() ); + p23.addGeometry( lp23.clone() ); + p23.dropMValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::GeometryCollection ); + QCOMPARE( p23.geometryN( 0 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.geometryN( 0 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + QCOMPARE( p23.geometryN( 1 )->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( static_cast< const QgsLineString *>( p23.geometryN( 1 ) )->pointN( 0 ), QgsPoint( 1, 2 ) ); + // with zm + lp23.setPoints( QgsPointSequence() << QgsPoint( 1, 2, 3, 4 ) << QgsPoint( 11, 12, 13, 14 ) << QgsPoint( 1, 12, 23, 24 ) << QgsPoint( 1, 2, 3, 4 ) ); + p23.clear(); + p23.addGeometry( lp23.clone() ); + p23.addGeometry( lp23.clone() ); + p23.dropMValue(); + QCOMPARE( p23.wkbType(), QgsWkbTypes::GeometryCollection ); + QCOMPARE( p23.geometryN( 0 )->wkbType(), QgsWkbTypes::LineStringZ ); + QCOMPARE( static_cast< const QgsLineString *>( p23.geometryN( 0 ) )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) ); + QCOMPARE( p23.geometryN( 1 )->wkbType(), QgsWkbTypes::LineStringZ ); + QCOMPARE( static_cast< const QgsLineString *>( p23.geometryN( 1 ) )->pointN( 0 ), QgsPoint( QgsWkbTypes::PointZ, 1, 2, 3 ) ); + + //vertexAngle + QgsGeometryCollection p24; + ( void )p24.vertexAngle( QgsVertexId() ); //just want no crash + ( void )p24.vertexAngle( QgsVertexId( 0, 0, 0 ) ); //just want no crash + ( void )p24.vertexAngle( QgsVertexId( 0, 1, 0 ) ); //just want no crash + ( void )p24.vertexAngle( QgsVertexId( 1, 0, 0 ) ); //just want no crash + ( void )p24.vertexAngle( QgsVertexId( -1, 0, 0 ) ); //just want no crash + QgsLineString l38; + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0.5, 0 ) << QgsPoint( 1, 0 ) + << QgsPoint( 2, 1 ) << QgsPoint( 1, 2 ) << QgsPoint( 0, 2 ) << QgsPoint( 0, 0 ) ); + p24.addGeometry( l38.clone() ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 0, 0 ) ), 2.35619, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 0, 1 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 0, 2 ) ), 1.17809, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 0, 3 ) ), 0.0, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 0, 4 ) ), 5.10509, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 0, 5 ) ), 3.92699, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 0, 0, 6 ) ), 2.35619, 0.00001 ); + p24.addGeometry( l38.clone() ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 1, 0, 0 ) ), 2.35619, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 1, 0, 1 ) ), 1.5708, 0.0001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 1, 0, 2 ) ), 1.17809, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 1, 0, 3 ) ), 0.0, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 1, 0, 4 ) ), 5.10509, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 1, 0, 5 ) ), 3.92699, 0.00001 ); + QGSCOMPARENEAR( p24.vertexAngle( QgsVertexId( 1, 0, 6 ) ), 2.35619, 0.00001 ); + + //insert vertex + + //insert vertex in empty collection + QgsGeometryCollection p25; + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 1, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 1, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( p25.isEmpty() ); + l38.setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0.5, 0 ) << QgsPoint( 1, 0 ) + << QgsPoint( 2, 1 ) << QgsPoint( 1, 2 ) << QgsPoint( 0, 2 ) << QgsPoint( 0, 0 ) ); + p25.addGeometry( l38.clone() ); + QVERIFY( p25.insertVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 0.3, 0 ) ) ); + QCOMPARE( p25.nCoordinates(), 8 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 0 ) )->pointN( 0 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 0 ) )->pointN( 1 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 0 ) )->pointN( 2 ), QgsPoint( 0.5, 0 ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 0, -1 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 0, 0, 100 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 1, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + // first vertex + QVERIFY( p25.insertVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 0, 0.1 ) ) ); + QCOMPARE( p25.nCoordinates(), 9 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 0 ) )->pointN( 0 ), QgsPoint( 0, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 0 ) )->pointN( 1 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 0 ) )->pointN( 2 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 0 ) )->pointN( 3 ), QgsPoint( 0.5, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 0 ) )->pointN( 7 ), QgsPoint( 0, 2 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 0 ) )->pointN( 8 ), QgsPoint( 0, 0 ) ); + // last vertex + QVERIFY( p25.insertVertex( QgsVertexId( 0, 0, 9 ), QgsPoint( 0.1, 0.1 ) ) ); + QCOMPARE( p25.nCoordinates(), 10 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 0 ) )->pointN( 0 ), QgsPoint( 0, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 0 ) )->pointN( 1 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 0 ) )->pointN( 2 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 0 ) )->pointN( 3 ), QgsPoint( 0.5, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 0 ) )->pointN( 8 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 0 ) )->pointN( 9 ), QgsPoint( 0.1, 0.1 ) ); + // with second part + p25.addGeometry( l38.clone() ); + QCOMPARE( p25.nCoordinates(), 17 ); + QVERIFY( p25.insertVertex( QgsVertexId( 1, 0, 1 ), QgsPoint( 0.3, 0 ) ) ); + QCOMPARE( p25.nCoordinates(), 18 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 1 ) )->pointN( 0 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 1 ) )->pointN( 1 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 1 ) )->pointN( 2 ), QgsPoint( 0.5, 0 ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 1, 0, -1 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 1, 0, 100 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p25.insertVertex( QgsVertexId( 2, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + // first vertex in second part + QVERIFY( p25.insertVertex( QgsVertexId( 1, 0, 0 ), QgsPoint( 0, 0.1 ) ) ); + QCOMPARE( p25.nCoordinates(), 19 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 1 ) )->pointN( 0 ), QgsPoint( 0, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 1 ) )->pointN( 1 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 1 ) )->pointN( 2 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 1 ) )->pointN( 3 ), QgsPoint( 0.5, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 1 ) )->pointN( 7 ), QgsPoint( 0, 2 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 1 ) )->pointN( 8 ), QgsPoint( 0, 0 ) ); + // last vertex in second part + QVERIFY( p25.insertVertex( QgsVertexId( 1, 0, 9 ), QgsPoint( 0.1, 0.1 ) ) ); + QCOMPARE( p25.nCoordinates(), 20 ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 1 ) )->pointN( 0 ), QgsPoint( 0, 0.1 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 1 ) )->pointN( 1 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 1 ) )->pointN( 2 ), QgsPoint( 0.3, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 1 ) )->pointN( 3 ), QgsPoint( 0.5, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 1 ) )->pointN( 8 ), QgsPoint( 0, 0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p25.geometryN( 1 ) )->pointN( 9 ), QgsPoint( 0.1, 0.1 ) ); + + //move vertex + + //empty collection + QgsGeometryCollection p26; + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( -1, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 1, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( p26.isEmpty() ); + + //valid collection + l38.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) << QgsPoint( 1, 2 ) ); + p26.addGeometry( l38.clone() ); + QVERIFY( p26.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( p26.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 16.0, 17.0 ) ) ); + QVERIFY( p26.moveVertex( QgsVertexId( 0, 0, 2 ), QgsPoint( 26.0, 27.0 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.geometryN( 0 ) )->pointN( 0 ), QgsPoint( 6.0, 7.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.geometryN( 0 ) )->pointN( 1 ), QgsPoint( 16.0, 17.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.geometryN( 0 ) )->pointN( 2 ), QgsPoint( 26.0, 27.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.geometryN( 0 ) )->pointN( 3 ), QgsPoint( 1, 2 ) ); + + //out of range + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 0, -1 ), QgsPoint( 3.0, 4.0 ) ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 0, 0, 10 ), QgsPoint( 3.0, 4.0 ) ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 1, 0, 0 ), QgsPoint( 3.0, 4.0 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.geometryN( 0 ) )->pointN( 0 ), QgsPoint( 6.0, 7.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.geometryN( 0 ) )->pointN( 1 ), QgsPoint( 16.0, 17.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.geometryN( 0 ) )->pointN( 2 ), QgsPoint( 26.0, 27.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.geometryN( 0 ) )->pointN( 3 ), QgsPoint( 1.0, 2.0 ) ); + + // with second part + p26.addGeometry( l38.clone() ); + QVERIFY( p26.moveVertex( QgsVertexId( 1, 0, 0 ), QgsPoint( 6.0, 7.0 ) ) ); + QVERIFY( p26.moveVertex( QgsVertexId( 1, 0, 1 ), QgsPoint( 16.0, 17.0 ) ) ); + QVERIFY( p26.moveVertex( QgsVertexId( 1, 0, 2 ), QgsPoint( 26.0, 27.0 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.geometryN( 1 ) )->pointN( 0 ), QgsPoint( 6.0, 7.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.geometryN( 1 ) )->pointN( 1 ), QgsPoint( 16.0, 17.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.geometryN( 1 ) )->pointN( 2 ), QgsPoint( 26.0, 27.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p26.geometryN( 1 ) )->pointN( 3 ), QgsPoint( 1.0, 2.0 ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 1, 0, -1 ), QgsPoint( 3.0, 4.0 ) ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 1, 0, 10 ), QgsPoint( 3.0, 4.0 ) ) ); + QVERIFY( !p26.moveVertex( QgsVertexId( 2, 0, 0 ), QgsPoint( 3.0, 4.0 ) ) ); + + //delete vertex + + //empty collection + QgsGeometryCollection p27; + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 1, 0 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 1, 1, 0 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( -1, 1, 0 ) ) ); + QVERIFY( p27.isEmpty() ); + + //valid collection + l38.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 5, 2 ) << QgsPoint( 6, 2 ) << QgsPoint( 7, 2 ) + << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 ) << QgsPoint( 1, 2 ) ); + + p27.addGeometry( l38.clone() ); + //out of range vertices + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 0, -1 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 0, 0, 100 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 1, 0, 1 ) ) ); + + //valid vertices + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 1 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 0 ) )->pointN( 0 ), QgsPoint( 1.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 0 ) )->pointN( 1 ), QgsPoint( 6.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 0 ) )->pointN( 2 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 0 ) )->pointN( 3 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 0 ) )->pointN( 5 ), QgsPoint( 1.0, 2.0 ) ); + + // delete first vertex + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 0 ) )->pointN( 0 ), QgsPoint( 6.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 0 ) )->pointN( 1 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 0 ) )->pointN( 2 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 0 ) )->pointN( 3 ), QgsPoint( 21.0, 22.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 0 ) )->pointN( 4 ), QgsPoint( 1.0, 2.0 ) ); + + // delete last vertex + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 4 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 0 ) )->pointN( 0 ), QgsPoint( 6.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 0 ) )->pointN( 1 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 0 ) )->pointN( 2 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 0 ) )->pointN( 3 ), QgsPoint( 21.0, 22.0 ) ); + + // delete some more vertices - should remove part + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QVERIFY( !p27.geometryN( 0 ) ); + + // with two parts + p27.addGeometry( l38.clone() ); + p27.addGeometry( l38.clone() ); + + //out of range vertices + QVERIFY( !p27.deleteVertex( QgsVertexId( 1, 0, -1 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 1, 0, 100 ) ) ); + QVERIFY( !p27.deleteVertex( QgsVertexId( 2, 0, 1 ) ) ); + + //valid vertices + QVERIFY( p27.deleteVertex( QgsVertexId( 1, 0, 1 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 1 ) )->pointN( 0 ), QgsPoint( 1.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 1 ) )->pointN( 1 ), QgsPoint( 6.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 1 ) )->pointN( 2 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 1 ) )->pointN( 3 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 1 ) )->pointN( 5 ), QgsPoint( 1.0, 2.0 ) ); + + // delete first vertex + QVERIFY( p27.deleteVertex( QgsVertexId( 1, 0, 0 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 1 ) )->pointN( 0 ), QgsPoint( 6.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 1 ) )->pointN( 1 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 1 ) )->pointN( 2 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 1 ) )->pointN( 3 ), QgsPoint( 21.0, 22.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 1 ) )->pointN( 4 ), QgsPoint( 1.0, 2.0 ) ); + + // delete last vertex + QVERIFY( p27.deleteVertex( QgsVertexId( 1, 0, 4 ) ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 1 ) )->pointN( 0 ), QgsPoint( 6.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 1 ) )->pointN( 1 ), QgsPoint( 7.0, 2.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 1 ) )->pointN( 2 ), QgsPoint( 11.0, 12.0 ) ); + QCOMPARE( static_cast< const QgsLineString * >( p27.geometryN( 1 ) )->pointN( 3 ), QgsPoint( 21.0, 22.0 ) ); + + // delete some more vertices - should remove part + QVERIFY( p27.deleteVertex( QgsVertexId( 1, 0, 1 ) ) ); + QVERIFY( p27.deleteVertex( QgsVertexId( 1, 0, 1 ) ) ); + QVERIFY( p27.deleteVertex( QgsVertexId( 1, 0, 1 ) ) ); + QCOMPARE( p27.numGeometries(), 1 ); + QVERIFY( p27.geometryN( 0 ) ); + + // test that second geometry is "promoted" when first is removed + p27.addGeometry( l38.clone() ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( p27.numGeometries(), 2 ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( p27.numGeometries(), 2 ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( p27.numGeometries(), 2 ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QVERIFY( p27.deleteVertex( QgsVertexId( 0, 0, 0 ) ) ); + QCOMPARE( p27.numGeometries(), 1 ); + QVERIFY( p27.geometryN( 0 ) ); //boundary @@ -4717,6 +13885,514 @@ void TestQgsGeometry::geometryCollection() lineBoundary->setPoints( QList() << QgsPoint( 0, 0 ) << QgsPoint( 1, 0 ) ); boundaryCollection.addGeometry( lineBoundary ); QVERIFY( !boundaryCollection.boundary() ); + + // segmentize + QgsGeometryCollection segmentC; + QgsCircularString toSegment; + toSegment.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) + << QgsPoint( 11, 10 ) << QgsPoint( 21, 2 ) ); + segmentC.addGeometry( toSegment.clone() ); + std::unique_ptr segmentized( static_cast< QgsGeometryCollection * >( segmentC.segmentize() ) ); + const QgsLineString *segmentizedLine = static_cast< const QgsLineString * >( segmentized->geometryN( 0 ) ); + QCOMPARE( segmentizedLine->numPoints(), 156 ); + QCOMPARE( segmentizedLine->vertexCount(), 156 ); + QCOMPARE( segmentizedLine->ringCount(), 1 ); + QCOMPARE( segmentizedLine->partCount(), 1 ); + QCOMPARE( segmentizedLine->wkbType(), QgsWkbTypes::LineString ); + QVERIFY( !segmentizedLine->is3D() ); + QVERIFY( !segmentizedLine->isMeasure() ); + QCOMPARE( segmentizedLine->pointN( 0 ), toSegment.pointN( 0 ) ); + QCOMPARE( segmentizedLine->pointN( segmentizedLine->numPoints() - 1 ), toSegment.pointN( toSegment.numPoints() - 1 ) ); + + // hasCurvedSegments + QgsGeometryCollection c30; + QVERIFY( !c30.hasCurvedSegments() ); + c30.addGeometry( part.clone() ); + QVERIFY( !c30.hasCurvedSegments() ); + c30.addGeometry( toSegment.clone() ); + QVERIFY( c30.hasCurvedSegments() ); +} + +void TestQgsGeometry::triangle2() +{ + //test constructor + QgsTriangle t1; + QVERIFY( t1.isEmpty() ); + QCOMPARE( t1.numInteriorRings(), 0 ); + QCOMPARE( t1.nCoordinates(), 0 ); + QCOMPARE( t1.ringCount(), 0 ); + QCOMPARE( t1.partCount(), 0 ); + QVERIFY( !t1.is3D() ); + QVERIFY( !t1.isMeasure() ); + QCOMPARE( t1.wkbType(), QgsWkbTypes::Triangle ); + QCOMPARE( t1.wktTypeStr(), QString( "Triangle" ) ); + QCOMPARE( t1.geometryType(), QString( "Triangle" ) ); + QCOMPARE( t1.dimension(), 2 ); + QVERIFY( !t1.hasCurvedSegments() ); + QCOMPARE( t1.area(), 0.0 ); + QCOMPARE( t1.perimeter(), 0.0 ); + QVERIFY( !t1.exteriorRing() ); + QVERIFY( !t1.interiorRing( 0 ) ); + + // invalid triangles + QgsTriangle invalid( QgsPointXY( 0, 0 ), QgsPointXY( 0, 0 ), QgsPointXY( 10, 10 ) ); + QVERIFY( invalid.isEmpty() ); + invalid = QgsTriangle( QPointF( 0, 0 ), QPointF( 0, 0 ), QPointF( 10, 10 ) ); + QVERIFY( invalid.isEmpty() ); + //set exterior ring + + //try with no ring + std::unique_ptr< QgsLineString > ext; + t1.setExteriorRing( nullptr ); + QVERIFY( t1.isEmpty() ); + QCOMPARE( t1.numInteriorRings(), 0 ); + QCOMPARE( t1.nCoordinates(), 0 ); + QCOMPARE( t1.ringCount(), 0 ); + QCOMPARE( t1.partCount(), 0 ); + QVERIFY( !t1.exteriorRing() ); + QVERIFY( !t1.interiorRing( 0 ) ); + QCOMPARE( t1.wkbType(), QgsWkbTypes::Triangle ); + + //valid exterior ring + ext.reset( new QgsLineString() ); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) + << QgsPoint( 0, 0 ) ); + QVERIFY( ext->isClosed() ); + t1.setExteriorRing( ext->clone() ); + QVERIFY( !t1.isEmpty() ); + QCOMPARE( t1.numInteriorRings(), 0 ); + QCOMPARE( t1.nCoordinates(), 4 ); + QCOMPARE( t1.ringCount(), 1 ); + QCOMPARE( t1.partCount(), 1 ); + QVERIFY( !t1.is3D() ); + QVERIFY( !t1.isMeasure() ); + QCOMPARE( t1.wkbType(), QgsWkbTypes::Triangle ); + QCOMPARE( t1.wktTypeStr(), QString( "Triangle" ) ); + QCOMPARE( t1.geometryType(), QString( "Triangle" ) ); + QCOMPARE( t1.dimension(), 2 ); + QVERIFY( !t1.hasCurvedSegments() ); + QCOMPARE( t1.area(), 50.0 ); + QGSCOMPARENEAR( t1.perimeter(), 34.1421, 0.001 ); + QVERIFY( t1.exteriorRing() ); + QVERIFY( !t1.interiorRing( 0 ) ); + + //retrieve exterior ring and check + QCOMPARE( *( static_cast< const QgsLineString * >( t1.exteriorRing() ) ), *ext ); + + //set new ExteriorRing + ext.reset( new QgsLineString() ); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 10 ) << QgsPoint( 5, 5 ) << QgsPoint( 10, 10 ) + << QgsPoint( 0, 10 ) ); + QVERIFY( ext->isClosed() ); + t1.setExteriorRing( ext->clone() ); + QVERIFY( !t1.isEmpty() ); + QCOMPARE( t1.numInteriorRings(), 0 ); + QCOMPARE( t1.nCoordinates(), 4 ); + QCOMPARE( t1.ringCount(), 1 ); + QCOMPARE( t1.partCount(), 1 ); + QVERIFY( !t1.is3D() ); + QVERIFY( !t1.isMeasure() ); + QCOMPARE( t1.wkbType(), QgsWkbTypes::Triangle ); + QCOMPARE( t1.wktTypeStr(), QString( "Triangle" ) ); + QCOMPARE( t1.geometryType(), QString( "Triangle" ) ); + QCOMPARE( t1.dimension(), 2 ); + QVERIFY( !t1.hasCurvedSegments() ); + QCOMPARE( t1.area(), 25.0 ); + QGSCOMPARENEAR( t1.perimeter(), 24.1421, 0.001 ); + QVERIFY( t1.exteriorRing() ); + QVERIFY( !t1.interiorRing( 0 ) ); + QCOMPARE( *( static_cast< const QgsLineString * >( t1.exteriorRing() ) ), *ext ); + + //test that a non closed exterior ring will be automatically closed + QgsTriangle t2; + ext.reset( new QgsLineString() ); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) ); + QVERIFY( !ext->isClosed() ); + t2.setExteriorRing( ext.release() ); + QVERIFY( !t2.isEmpty() ); + QVERIFY( t2.exteriorRing()->isClosed() ); + QCOMPARE( t2.nCoordinates(), 4 ); + + // invalid number of points + ext.reset( new QgsLineString() ); + t2.clear(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) ); + t2.setExteriorRing( ext.release() ); + QVERIFY( t2.isEmpty() ); + + ext.reset( new QgsLineString() ); + t2.clear(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) << QgsPoint( 5, 10 ) << QgsPoint( 8, 10 ) ); + t2.setExteriorRing( ext.release() ); + QVERIFY( t2.isEmpty() ); + + // invalid exterior ring + ext.reset( new QgsLineString() ); + t2.clear(); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) << QgsPoint( 5, 10 ) ); + t2.setExteriorRing( ext.release() ); + QVERIFY( t2.isEmpty() ); + + ext.reset( new QgsLineString() ); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 0, 0 ) ); + t2.setExteriorRing( ext.release() ); + QVERIFY( t2.isEmpty() ); + + ext.reset( new QgsLineString() ); + ext->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 0, 0 ) ); + t2.setExteriorRing( ext.release() ); + QVERIFY( t2.isEmpty() ); + + // circular ring + QgsCircularString *circularRing = new QgsCircularString(); + t2.clear(); + circularRing->setPoints( QgsPointSequence() << QgsPoint( 0, 0 ) << QgsPoint( 0, 10 ) << QgsPoint( 10, 10 ) ); + QVERIFY( circularRing->hasCurvedSegments() ); + t2.setExteriorRing( circularRing ); + QVERIFY( t2.isEmpty() ); + + //constructor with 3 points + // double points + QgsTriangle t3( QgsPoint( 0, 0 ), QgsPoint( 0, 0 ), QgsPoint( 10, 10 ) ); + QVERIFY( t3.isEmpty() ); + QCOMPARE( t3.numInteriorRings(), 0 ); + QCOMPARE( t3.nCoordinates(), 0 ); + QCOMPARE( t3.ringCount(), 0 ); + QCOMPARE( t3.partCount(), 0 ); + QVERIFY( !t3.is3D() ); + QVERIFY( !t3.isMeasure() ); + QCOMPARE( t3.wkbType(), QgsWkbTypes::Triangle ); + QCOMPARE( t3.wktTypeStr(), QString( "Triangle" ) ); + QCOMPARE( t3.geometryType(), QString( "Triangle" ) ); + QCOMPARE( t3.dimension(), 2 ); + QVERIFY( !t3.hasCurvedSegments() ); + QCOMPARE( t3.area(), 0.0 ); + QCOMPARE( t3.perimeter(), 0.0 ); + QVERIFY( !t3.exteriorRing() ); + QVERIFY( !t3.interiorRing( 0 ) ); + + // colinear + t3 = QgsTriangle( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 10 ) ); + QVERIFY( t3.isEmpty() ); + QCOMPARE( t3.numInteriorRings(), 0 ); + QCOMPARE( t3.nCoordinates(), 0 ); + QCOMPARE( t3.ringCount(), 0 ); + QCOMPARE( t3.partCount(), 0 ); + QVERIFY( !t3.is3D() ); + QVERIFY( !t3.isMeasure() ); + QCOMPARE( t3.wkbType(), QgsWkbTypes::Triangle ); + QCOMPARE( t3.wktTypeStr(), QString( "Triangle" ) ); + QCOMPARE( t3.geometryType(), QString( "Triangle" ) ); + QCOMPARE( t3.dimension(), 2 ); + QVERIFY( !t3.hasCurvedSegments() ); + QCOMPARE( t3.area(), 0.0 ); + QCOMPARE( t3.perimeter(), 0.0 ); + QVERIFY( !t3.exteriorRing() ); + QVERIFY( !t3.interiorRing( 0 ) ); + + t3 = QgsTriangle( QgsPoint( 0, 0 ), QgsPoint( 0, 10 ), QgsPoint( 10, 10 ) ); + QVERIFY( !t3.isEmpty() ); + QCOMPARE( t3.numInteriorRings(), 0 ); + QCOMPARE( t3.nCoordinates(), 4 ); + QCOMPARE( t3.ringCount(), 1 ); + QCOMPARE( t3.partCount(), 1 ); + QVERIFY( !t3.is3D() ); + QVERIFY( !t3.isMeasure() ); + QCOMPARE( t3.wkbType(), QgsWkbTypes::Triangle ); + QCOMPARE( t3.wktTypeStr(), QString( "Triangle" ) ); + QCOMPARE( t3.geometryType(), QString( "Triangle" ) ); + QCOMPARE( t3.dimension(), 2 ); + QVERIFY( !t3.hasCurvedSegments() ); + QCOMPARE( t3.area(), 50.0 ); + QGSCOMPARENEAR( t3.perimeter(), 34.1421, 0.001 ); + QVERIFY( t3.exteriorRing() ); + QVERIFY( !t3.interiorRing( 0 ) ); + + // equality + QVERIFY( QgsTriangle() == QgsTriangle() ); // empty + QVERIFY( QgsTriangle() == QgsTriangle( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 10 ) ) ); // empty + QVERIFY( QgsTriangle( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 0, 10 ) ) == QgsTriangle() ); // empty + QVERIFY( QgsTriangle() != QgsTriangle( QgsPoint( 0, 0 ), QgsPoint( 5, 5 ), QgsPoint( 0, 10 ) ) ); + QVERIFY( QgsTriangle( QgsPoint( 0, 0 ), QgsPoint( 5, 5 ), QgsPoint( 0, 10 ) ) != QgsTriangle() ); + QVERIFY( QgsTriangle( QgsPoint( 0, 0 ), QgsPoint( 5, 5 ), QgsPoint( 0, 10 ) ) != QgsTriangle( QgsPoint( 0, 10 ), QgsPoint( 5, 5 ), QgsPoint( 0, 0 ) ) ); + + // clone + QgsTriangle *t4 = t3.clone(); + QCOMPARE( t3, *t4 ); + delete t4; + + // constructor from QgsPointXY and QPointF + QgsTriangle t_qgspoint = QgsTriangle( QgsPointXY( 0, 0 ), QgsPointXY( 0, 10 ), QgsPointXY( 10, 10 ) ); + QVERIFY( t3 == t_qgspoint ); + QgsTriangle t_pointf = QgsTriangle( QPointF( 0, 0 ), QPointF( 0, 10 ), QPointF( 10, 10 ) ); + QVERIFY( t3 == t_pointf ); + + // fromWkt + QgsTriangle t5; + ext.reset( new QgsLineString() ); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 0, 10, 2, 6 ) << QgsPoint( QgsWkbTypes::PointZM, 10, 10, 3, 7 ) ); + t5.setExteriorRing( ext.release() ); + QString wkt = t5.asWkt(); + QVERIFY( !wkt.isEmpty() ); + QgsTriangle t6; + QVERIFY( t6.fromWkt( wkt ) ); + QCOMPARE( t5, t6 ); + + // conversion + QgsPolygonV2 p1; + ext.reset( new QgsLineString() ); + ext->setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 0, 0, 1, 5 ) + << QgsPoint( QgsWkbTypes::PointZM, 0, 10, 2, 6 ) << QgsPoint( QgsWkbTypes::PointZM, 10, 10, 3, 7 ) ); + p1.setExteriorRing( ext.release() ); + //toPolygon + std::unique_ptr< QgsPolygonV2 > poly( t5.toPolygon() ); + QCOMPARE( *poly, p1 ); + //surfaceToPolygon + std::unique_ptr< QgsPolygonV2 > surface( t5.surfaceToPolygon() ); + QCOMPARE( *surface, p1 ); + + //bad WKT + QVERIFY( !t6.fromWkt( "Point()" ) ); + QVERIFY( t6.isEmpty() ); + QVERIFY( !t6.exteriorRing() ); + QCOMPARE( t6.numInteriorRings(), 0 ); + QVERIFY( !t6.is3D() ); + QVERIFY( !t6.isMeasure() ); + QCOMPARE( t6.wkbType(), QgsWkbTypes::Triangle ); + + // WKB + QByteArray wkb = t5.asWkb(); + t6.clear(); + QgsConstWkbPtr wkb16ptr5( wkb ); + t6.fromWkb( wkb16ptr5 ); + QCOMPARE( t5.wkbType(), QgsWkbTypes::TriangleZM ); + QCOMPARE( t5, t6 ); + + //bad WKB - check for no crash + t6.clear(); + QgsConstWkbPtr nullPtr( nullptr, 0 ); + QVERIFY( !t6.fromWkb( nullPtr ) ); + QCOMPARE( t6.wkbType(), QgsWkbTypes::Triangle ); + QgsPoint point( 1, 2 ); + QByteArray wkbPoint = point.asWkb(); + QgsConstWkbPtr wkbPointPtr( wkbPoint ); + QVERIFY( !t6.fromWkb( wkbPointPtr ) ); + QCOMPARE( t6.wkbType(), QgsWkbTypes::Triangle ); + + // lengths and angles + QgsTriangle t7( QgsPoint( 0, 0 ), QgsPoint( 0, 5 ), QgsPoint( 5, 5 ) ); + + QVector a_tested, a_t7 = t7.angles(); + a_tested.append( M_PI / 4.0 ); + a_tested.append( M_PI / 2.0 ); + a_tested.append( M_PI / 4.0 ); + QGSCOMPARENEAR( a_tested.at( 0 ), a_t7.at( 0 ), 0.0001 ); + QGSCOMPARENEAR( a_tested.at( 1 ), a_t7.at( 1 ), 0.0001 ); + QGSCOMPARENEAR( a_tested.at( 2 ), a_t7.at( 2 ), 0.0001 ); + + QVector l_tested, l_t7 = t7.lengths(); + l_tested.append( 5 ); + l_tested.append( 5 ); + l_tested.append( std::sqrt( 5 * 5 + 5 * 5 ) ); + QGSCOMPARENEAR( l_tested.at( 0 ), l_t7.at( 0 ), 0.0001 ); + QGSCOMPARENEAR( l_tested.at( 1 ), l_t7.at( 1 ), 0.0001 ); + QGSCOMPARENEAR( l_tested.at( 2 ), l_t7.at( 2 ), 0.0001 ); + + // type of triangle + QVERIFY( t7.isRight() ); + QVERIFY( t7.isIsocele() ); + QVERIFY( !t7.isScalene() ); + QVERIFY( !t7.isEquilateral() ); + + QgsTriangle t8( QgsPoint( 7.2825, 4.2368 ), QgsPoint( 13.0058, 3.3218 ), QgsPoint( 9.2145, 6.5242 ) ); + // angles in radians 58.8978;31.1036;89.9985 + // length 5.79598;4.96279;2.99413 + QVERIFY( t8.isRight() ); + QVERIFY( !t8.isIsocele() ); + QVERIFY( t8.isScalene() ); + QVERIFY( !t8.isEquilateral() ); + + QgsTriangle t9( QgsPoint( 10, 10 ), QgsPoint( 16, 10 ), QgsPoint( 13, 15.1962 ) ); + QVERIFY( !t9.isRight() ); + QVERIFY( t9.isIsocele() ); + QVERIFY( !t9.isScalene() ); + QVERIFY( t9.isEquilateral() ); + + // vertex + QCOMPARE( t9.vertexAt( -1 ), QgsPoint( 0, 0 ) ); + QCOMPARE( t9.vertexAt( 0 ), QgsPoint( 10, 10 ) ); + QCOMPARE( t9.vertexAt( 1 ), QgsPoint( 16, 10 ) ); + QCOMPARE( t9.vertexAt( 2 ), QgsPoint( 13, 15.1962 ) ); + QCOMPARE( t9.vertexAt( 3 ), QgsPoint( 10, 10 ) ); + QCOMPARE( t9.vertexAt( 4 ), QgsPoint( 0, 0 ) ); + + // altitudes + QgsTriangle t10( QgsPoint( 20, 2 ), QgsPoint( 16, 6 ), QgsPoint( 26, 2 ) ); + QVector alt = t10.altitudes(); + QGSCOMPARENEARPOINT( alt.at( 0 ).pointN( 1 ), QgsPoint( 20.8276, 4.0690 ), 0.0001 ); + QGSCOMPARENEARPOINT( alt.at( 1 ).pointN( 1 ), QgsPoint( 16, 2 ), 0.0001 ); + QGSCOMPARENEARPOINT( alt.at( 2 ).pointN( 1 ), QgsPoint( 23, -1 ), 0.0001 ); + + // orthocenter + QCOMPARE( QgsPoint( 16, -8 ), t10.orthocenter() ); + QCOMPARE( QgsPoint( 0, 5 ), t7.orthocenter() ); + QGSCOMPARENEARPOINT( QgsPoint( 13, 11.7321 ), t9.orthocenter(), 0.0001 ); + + // circumscribed circle + QCOMPARE( QgsPoint( 2.5, 2.5 ), t7.circumscribedCenter() ); + QGSCOMPARENEAR( 3.5355, t7.circumscribedRadius(), 0.0001 ); + QCOMPARE( QgsPoint( 23, 9 ), t10.circumscribedCenter() ); + QGSCOMPARENEAR( 7.6158, t10.circumscribedRadius(), 0.0001 ); + QGSCOMPARENEARPOINT( QgsPoint( 13, 11.7321 ), t9.circumscribedCenter(), 0.0001 ); + QGSCOMPARENEAR( 3.4641, t9.circumscribedRadius(), 0.0001 ); + QGSCOMPARENEARPOINT( QgsPoint( 13, 11.7321 ), t9.circumscribedCircle().center(), 0.0001 ); + QGSCOMPARENEAR( 3.4641, t9.circumscribedCircle().radius(), 0.0001 ); + + // inscribed circle + QGSCOMPARENEARPOINT( QgsPoint( 1.4645, 3.5355 ), t7.inscribedCenter(), 0.001 ); + QGSCOMPARENEAR( 1.4645, t7.inscribedRadius(), 0.0001 ); + QGSCOMPARENEARPOINT( QgsPoint( 20.4433, 3.0701 ), t10.inscribedCenter(), 0.001 ); + QGSCOMPARENEAR( 1.0701, t10.inscribedRadius(), 0.0001 ); + QGSCOMPARENEARPOINT( QgsPoint( 13, 11.7321 ), t9.inscribedCenter(), 0.0001 ); + QGSCOMPARENEAR( 1.7321, t9.inscribedRadius(), 0.0001 ); + QGSCOMPARENEARPOINT( QgsPoint( 13, 11.7321 ), t9.inscribedCircle().center(), 0.0001 ); + QGSCOMPARENEAR( 1.7321, t9.inscribedCircle().radius(), 0.0001 ); + + // medians + QVector med = t7.medians(); + QCOMPARE( med.at( 0 ).pointN( 0 ), t7.vertexAt( 0 ) ); + QGSCOMPARENEARPOINT( med.at( 0 ).pointN( 1 ), QgsPoint( 2.5, 5 ), 0.0001 ); + QCOMPARE( med.at( 1 ).pointN( 0 ), t7.vertexAt( 1 ) ); + QGSCOMPARENEARPOINT( med.at( 1 ).pointN( 1 ), QgsPoint( 2.5, 2.5 ), 0.0001 ); + QCOMPARE( med.at( 2 ).pointN( 0 ), t7.vertexAt( 2 ) ); + QGSCOMPARENEARPOINT( med.at( 2 ).pointN( 1 ), QgsPoint( 0, 2.5 ), 0.0001 ); + med.clear(); + + med = t10.medians(); + QCOMPARE( med.at( 0 ).pointN( 0 ), t10.vertexAt( 0 ) ); + QGSCOMPARENEARPOINT( med.at( 0 ).pointN( 1 ), QgsPoint( 21, 4 ), 0.0001 ); + QCOMPARE( med.at( 1 ).pointN( 0 ), t10.vertexAt( 1 ) ); + QGSCOMPARENEARPOINT( med.at( 1 ).pointN( 1 ), QgsPoint( 23, 2 ), 0.0001 ); + QCOMPARE( med.at( 2 ).pointN( 0 ), t10.vertexAt( 2 ) ); + QGSCOMPARENEARPOINT( med.at( 2 ).pointN( 1 ), QgsPoint( 18, 4 ), 0.0001 ); + med.clear(); + alt.clear(); + + med = t9.medians(); + alt = t9.altitudes(); + QGSCOMPARENEARPOINT( med.at( 0 ).pointN( 0 ), alt.at( 0 ).pointN( 0 ), 0.0001 ); + QGSCOMPARENEARPOINT( med.at( 0 ).pointN( 1 ), alt.at( 0 ).pointN( 1 ), 0.0001 ); + QGSCOMPARENEARPOINT( med.at( 1 ).pointN( 0 ), alt.at( 1 ).pointN( 0 ), 0.0001 ); + QGSCOMPARENEARPOINT( med.at( 1 ).pointN( 1 ), alt.at( 1 ).pointN( 1 ), 0.0001 ); + QGSCOMPARENEARPOINT( med.at( 2 ).pointN( 0 ), alt.at( 2 ).pointN( 0 ), 0.0001 ); + QGSCOMPARENEARPOINT( med.at( 2 ).pointN( 1 ), alt.at( 2 ).pointN( 1 ), 0.0001 ); + + // medial + QCOMPARE( t7.medial(), QgsTriangle( QgsPoint( 0, 2.5 ), QgsPoint( 2.5, 5 ), QgsPoint( 2.5, 2.5 ) ) ); + QCOMPARE( t9.medial(), QgsTriangle( QgsGeometryUtils::midpoint( t9.vertexAt( 0 ), t9.vertexAt( 1 ) ), + QgsGeometryUtils::midpoint( t9.vertexAt( 1 ), t9.vertexAt( 2 ) ), + QgsGeometryUtils::midpoint( t9.vertexAt( 2 ), t9.vertexAt( 0 ) ) ) ); + + // bisectors + QVector bis = t7.bisectors(); + QCOMPARE( bis.at( 0 ).pointN( 0 ), t7.vertexAt( 0 ) ); + QGSCOMPARENEARPOINT( bis.at( 0 ).pointN( 1 ), QgsPoint( 2.0711, 5 ), 0.0001 ); + QCOMPARE( bis.at( 1 ).pointN( 0 ), t7.vertexAt( 1 ) ); + QGSCOMPARENEARPOINT( bis.at( 1 ).pointN( 1 ), QgsPoint( 2.5, 2.5 ), 0.0001 ); + QCOMPARE( bis.at( 2 ).pointN( 0 ), t7.vertexAt( 2 ) ); + QGSCOMPARENEARPOINT( bis.at( 2 ).pointN( 1 ), QgsPoint( 0, 2.9289 ), 0.0001 ); + + // "deleted" method + ext.reset( new QgsLineString() ); + QgsTriangle t11( QgsPoint( 0, 0 ), QgsPoint( 100, 100 ), QgsPoint( 0, 200 ) ); + ext->setPoints( QgsPointSequence() << QgsPoint( 5, 5 ) + << QgsPoint( 50, 50 ) << QgsPoint( 0, 25 ) + << QgsPoint( 5, 5 ) ); + t11.addInteriorRing( ext.release() ); + QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 100 100, 0 200, 0 0))" ) ); + + /* QList lc; + lc.append(ext); + t11.setInteriorRings( lc ); + QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 100 100, 0 200, 0 0))" ) );*/ + + QgsVertexId id( 0, 0, 1 ); + QVERIFY( !t11.deleteVertex( id ) ); + QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 100 100, 0 200, 0 0))" ) ); + QVERIFY( !t11.insertVertex( id, QgsPoint( 5, 5 ) ) ); + QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 100 100, 0 200, 0 0))" ) ); + + //move vertex + QgsPoint pt1( 5, 5 ); + // invalid part + id.part = -1; + QVERIFY( !t11.moveVertex( id, pt1 ) ); + id.part = 1; + QVERIFY( !t11.moveVertex( id, pt1 ) ); + // invalid ring + id.part = 0; + id.ring = -1; + QVERIFY( !t11.moveVertex( id, pt1 ) ); + id.ring = 1; + QVERIFY( !t11.moveVertex( id, pt1 ) ); + id.ring = 0; + id.vertex = -1; + QVERIFY( !t11.moveVertex( id, pt1 ) ); + id.vertex = 5; + QVERIFY( !t11.moveVertex( id, pt1 ) ); + + // valid vertex + id.vertex = 0; + QVERIFY( t11.moveVertex( id, pt1 ) ); + QCOMPARE( t11.asWkt(), QString( "Triangle ((5 5, 100 100, 0 200, 5 5))" ) ); + pt1 = QgsPoint(); + QVERIFY( t11.moveVertex( id, pt1 ) ); + QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 100 100, 0 200, 0 0))" ) ); + id.vertex = 4; + pt1 = QgsPoint( 5, 5 ); + QVERIFY( t11.moveVertex( id, pt1 ) ); + QCOMPARE( t11.asWkt(), QString( "Triangle ((5 5, 100 100, 0 200, 5 5))" ) ); + pt1 = QgsPoint(); + QVERIFY( t11.moveVertex( id, pt1 ) ); + QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 100 100, 0 200, 0 0))" ) ); + id.vertex = 1; + pt1 = QgsPoint( 5, 5 ); + QVERIFY( t11.moveVertex( id, pt1 ) ); + QCOMPARE( t11.asWkt(), QString( "Triangle ((0 0, 5 5, 0 200, 0 0))" ) ); + // colinear + pt1 = QgsPoint( 0, 100 ); + QVERIFY( !t11.moveVertex( id, pt1 ) ); + // duplicate point + pt1 = QgsPoint( 0, 0 ); + QVERIFY( !t11.moveVertex( id, pt1 ) ); + + //toCurveType + QgsTriangle t12( QgsPoint( 7, 4 ), QgsPoint( 13, 3 ), QgsPoint( 9, 6 ) ); + std::unique_ptr< QgsCurvePolygon > curveType( t12.toCurveType() ); + QCOMPARE( curveType->wkbType(), QgsWkbTypes::CurvePolygon ); + QCOMPARE( curveType->exteriorRing()->numPoints(), 4 ); + QCOMPARE( curveType->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( 7, 4 ) ); + QCOMPARE( curveType->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( 13, 3 ) ); + QCOMPARE( curveType->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( 9, 6 ) ); + QCOMPARE( curveType->exteriorRing()->vertexAt( QgsVertexId( 0, 0, 3 ) ), QgsPoint( 7, 4 ) ); + QCOMPARE( curveType->numInteriorRings(), 0 ); + + // boundary + QVERIFY( !QgsTriangle().boundary() ); + std::unique_ptr< QgsCurve > boundary( QgsTriangle( QgsPoint( 7, 4 ), QgsPoint( 13, 3 ), QgsPoint( 9, 6 ) ).boundary() ); + QCOMPARE( boundary->wkbType(), QgsWkbTypes::LineString ); + QCOMPARE( boundary->numPoints(), 4 ); + QCOMPARE( boundary->vertexAt( QgsVertexId( 0, 0, 0 ) ), QgsPoint( 7, 4 ) ); + QCOMPARE( boundary->vertexAt( QgsVertexId( 0, 0, 1 ) ), QgsPoint( 13, 3 ) ); + QCOMPARE( boundary->vertexAt( QgsVertexId( 0, 0, 2 ) ), QgsPoint( 9, 6 ) ); + QCOMPARE( boundary->vertexAt( QgsVertexId( 0, 0, 3 ) ), QgsPoint( 7, 4 ) ); + + // cast + QgsTriangle pCast; + QVERIFY( QgsPolygonV2().cast( &pCast ) ); + QgsTriangle pCast2( QgsPoint( 7, 4 ), QgsPoint( 13, 3 ), QgsPoint( 9, 6 ) );; + QVERIFY( QgsPolygonV2().cast( &pCast2 ) ); } void TestQgsGeometry::fromQgsPointXY() @@ -5449,12 +15125,12 @@ void TestQgsGeometry::poleOfInaccessibility() QGSCOMPARENEAR( point.y(), 3263.50, 0.01 ); //test degenerate polygons - QgsGeometry degen1 = QgsGeometry::fromWkt( "Polygon(( 0 0, 1 0, 2 0, 0 0 ))" ); + QgsGeometry degen1 = QgsGeometry::fromWkt( QStringLiteral( "Polygon(( 0 0, 1 0, 2 0, 0 0 ))" ) ); point = degen1.poleOfInaccessibility( 1 ).asPoint(); QGSCOMPARENEAR( point.x(), 0, 0.01 ); QGSCOMPARENEAR( point.y(), 0, 0.01 ); - QgsGeometry degen2 = QgsGeometry::fromWkt( "Polygon(( 0 0, 1 0, 1 1 , 1 0, 0 0 ))" ); + QgsGeometry degen2 = QgsGeometry::fromWkt( QStringLiteral( "Polygon(( 0 0, 1 0, 1 1 , 1 0, 0 0 ))" ) ); point = degen2.poleOfInaccessibility( 1 ).asPoint(); QGSCOMPARENEAR( point.x(), 0, 0.01 ); QGSCOMPARENEAR( point.y(), 0, 0.01 ); @@ -5463,7 +15139,7 @@ void TestQgsGeometry::poleOfInaccessibility() QVERIFY( QgsGeometry().poleOfInaccessibility( 1 ).isNull() ); // not a polygon - QgsGeometry lineString = QgsGeometry::fromWkt( "LineString(1 0, 2 2 )" ); + QgsGeometry lineString = QgsGeometry::fromWkt( QStringLiteral( "LineString(1 0, 2 2 )" ) ); QVERIFY( lineString.poleOfInaccessibility( 1 ).isNull() ); // invalid threshold @@ -5490,20 +15166,20 @@ void TestQgsGeometry::makeValid() typedef QPair InputAndExpectedWktPair; QList geoms; // dimension collapse - geoms << qMakePair( QString( "LINESTRING(0 0)" ), - QString( "POINT(0 0)" ) ); + geoms << qMakePair( QStringLiteral( "LINESTRING(0 0)" ), + QStringLiteral( "POINT(0 0)" ) ); // unclosed ring - geoms << qMakePair( QString( "POLYGON((10 22,10 32,20 32,20 22))" ), - QString( "POLYGON((10 22,10 32,20 32,20 22,10 22))" ) ); + geoms << qMakePair( QStringLiteral( "POLYGON((10 22,10 32,20 32,20 22))" ), + QStringLiteral( "POLYGON((10 22,10 32,20 32,20 22,10 22))" ) ); // butterfly polygon (self-intersecting ring) - geoms << qMakePair( QString( "POLYGON((0 0, 10 10, 10 0, 0 10, 0 0))" ), - QString( "MULTIPOLYGON(((5 5, 0 0, 0 10, 5 5)),((5 5, 10 10, 10 0, 5 5)))" ) ); + geoms << qMakePair( QStringLiteral( "POLYGON((0 0, 10 10, 10 0, 0 10, 0 0))" ), + QStringLiteral( "MULTIPOLYGON(((5 5, 0 0, 0 10, 5 5)),((5 5, 10 10, 10 0, 5 5)))" ) ); // polygon with extra tail (a part of the ring does not form any area) - geoms << qMakePair( QString( "POLYGON((0 0, 1 0, 1 1, 0 1, 0 0, -1 0, 0 0))" ), - QString( "GEOMETRYCOLLECTION(POLYGON((0 0, 0 1, 1 1, 1 0, 0 0)), LINESTRING(0 0, -1 0))" ) ); + geoms << qMakePair( QStringLiteral( "POLYGON((0 0, 1 0, 1 1, 0 1, 0 0, -1 0, 0 0))" ), + QStringLiteral( "GEOMETRYCOLLECTION(POLYGON((0 0, 0 1, 1 1, 1 0, 0 0)), LINESTRING(0 0, -1 0))" ) ); // collection with invalid geometries - geoms << qMakePair( QString( "GEOMETRYCOLLECTION(LINESTRING(0 0, 0 0), POLYGON((0 0, 10 10, 10 0, 0 10, 0 0)), LINESTRING(10 0, 10 10))" ), - QString( "GEOMETRYCOLLECTION(POINT(0 0), MULTIPOLYGON(((5 5, 0 0, 0 10, 5 5)),((5 5, 10 10, 10 0, 5 5))), LINESTRING(10 0, 10 10)))" ) ); + geoms << qMakePair( QStringLiteral( "GEOMETRYCOLLECTION(LINESTRING(0 0, 0 0), POLYGON((0 0, 10 10, 10 0, 0 10, 0 0)), LINESTRING(10 0, 10 10))" ), + QStringLiteral( "GEOMETRYCOLLECTION(POINT(0 0), MULTIPOLYGON(((5 5, 0 0, 0 10, 5 5)),((5 5, 10 10, 10 0, 5 5))), LINESTRING(10 0, 10 10)))" ) ); Q_FOREACH ( const InputAndExpectedWktPair &pair, geoms ) { @@ -5522,18 +15198,18 @@ void TestQgsGeometry::isSimple() { typedef QPair InputWktAndExpectedResult; QList geoms; - geoms << qMakePair( QString( "LINESTRING(0 0, 1 0, 1 1)" ), true ); - geoms << qMakePair( QString( "LINESTRING(0 0, 1 0, 1 1, 0 0)" ), true ); // may be closed (linear ring) - geoms << qMakePair( QString( "LINESTRING(0 0, 1 0, 1 1, 0 -1)" ), false ); // self-intersection - geoms << qMakePair( QString( "LINESTRING(0 0, 1 0, 1 1, 0.5 0, 0 1)" ), false ); // self-tangency - geoms << qMakePair( QString( "POINT(1 1)" ), true ); // points are simple - geoms << qMakePair( QString( "POLYGON((0 0, 1 1, 1 1, 0 0))" ), true ); // polygons are always simple, even if they are invalid - geoms << qMakePair( QString( "MULTIPOINT((1 1), (2 2))" ), true ); - geoms << qMakePair( QString( "MULTIPOINT((1 1), (1 1))" ), false ); // must not contain the same point twice - geoms << qMakePair( QString( "MULTILINESTRING((0 0, 1 0), (0 1, 1 1))" ), true ); - geoms << qMakePair( QString( "MULTILINESTRING((0 0, 1 0), (0 0, 1 0))" ), true ); // may be touching at endpoints - geoms << qMakePair( QString( "MULTILINESTRING((0 0, 1 1), (0 1, 1 0))" ), false ); // must not intersect each other - geoms << qMakePair( QString( "MULTIPOLYGON(((0 0, 1 1, 1 1, 0 0)),((0 0, 1 1, 1 1, 0 0)))" ), true ); // multi-polygons are always simple + geoms << qMakePair( QStringLiteral( "LINESTRING(0 0, 1 0, 1 1)" ), true ); + geoms << qMakePair( QStringLiteral( "LINESTRING(0 0, 1 0, 1 1, 0 0)" ), true ); // may be closed (linear ring) + geoms << qMakePair( QStringLiteral( "LINESTRING(0 0, 1 0, 1 1, 0 -1)" ), false ); // self-intersection + geoms << qMakePair( QStringLiteral( "LINESTRING(0 0, 1 0, 1 1, 0.5 0, 0 1)" ), false ); // self-tangency + geoms << qMakePair( QStringLiteral( "POINT(1 1)" ), true ); // points are simple + geoms << qMakePair( QStringLiteral( "POLYGON((0 0, 1 1, 1 1, 0 0))" ), true ); // polygons are always simple, even if they are invalid + geoms << qMakePair( QStringLiteral( "MULTIPOINT((1 1), (2 2))" ), true ); + geoms << qMakePair( QStringLiteral( "MULTIPOINT((1 1), (1 1))" ), false ); // must not contain the same point twice + geoms << qMakePair( QStringLiteral( "MULTILINESTRING((0 0, 1 0), (0 1, 1 1))" ), true ); + geoms << qMakePair( QStringLiteral( "MULTILINESTRING((0 0, 1 0), (0 0, 1 0))" ), true ); // may be touching at endpoints + geoms << qMakePair( QStringLiteral( "MULTILINESTRING((0 0, 1 1), (0 1, 1 0))" ), false ); // must not intersect each other + geoms << qMakePair( QStringLiteral( "MULTIPOLYGON(((0 0, 1 1, 1 1, 0 0)),((0 0, 1 1, 1 1, 0 0)))" ), true ); // multi-polygons are always simple Q_FOREACH ( const InputWktAndExpectedResult &pair, geoms ) { @@ -5548,8 +15224,8 @@ void TestQgsGeometry::isSimple() void TestQgsGeometry::reshapeGeometryLineMerge() { int res; - QgsGeometry g2D = QgsGeometry::fromWkt( "LINESTRING(10 10, 20 20)" ); - QgsGeometry g3D = QgsGeometry::fromWkt( "LINESTRINGZ(10 10 1, 20 20 2)" ); + QgsGeometry g2D = QgsGeometry::fromWkt( QStringLiteral( "LINESTRING(10 10, 20 20)" ) ); + QgsGeometry g3D = QgsGeometry::fromWkt( QStringLiteral( "LINESTRINGZ(10 10 1, 20 20 2)" ) ); // prepare 2D reshaping line QVector v2D_1, v2D_2; @@ -5643,35 +15319,35 @@ void TestQgsGeometry::minimalEnclosingCircle() QCOMPARE( result, resultTest ); // case 2 - geomTest = QgsGeometry::fromWkt( QString( "MULTIPOINT( 3 8, 7 4 )" ) ); + geomTest = QgsGeometry::fromWkt( QStringLiteral( "MULTIPOINT( 3 8, 7 4 )" ) ); result = geomTest.minimalEnclosingCircle( center, radius ); QGSCOMPARENEARPOINT( center, QgsPointXY( 5, 6 ), 0.0001 ); QGSCOMPARENEAR( radius, sqrt( 2 ) * 2, 0.0001 ); resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) ); QCOMPARE( result, resultTest ); - geomTest = QgsGeometry::fromWkt( QString( "LINESTRING( 0 5, 2 2, 0 -5, -1 -1 )" ) ); + geomTest = QgsGeometry::fromWkt( QStringLiteral( "LINESTRING( 0 5, 2 2, 0 -5, -1 -1 )" ) ); result = geomTest.minimalEnclosingCircle( center, radius ); QGSCOMPARENEARPOINT( center, QgsPointXY( 0, 0 ), 0.0001 ); QGSCOMPARENEAR( radius, 5, 0.0001 ); resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) ); QCOMPARE( result, resultTest ); - geomTest = QgsGeometry::fromWkt( QString( "MULTIPOINT( 0 5, 2 2, 0 -5, -1 -1 )" ) ); + geomTest = QgsGeometry::fromWkt( QStringLiteral( "MULTIPOINT( 0 5, 2 2, 0 -5, -1 -1 )" ) ); result = geomTest.minimalEnclosingCircle( center, radius ); QGSCOMPARENEARPOINT( center, QgsPointXY( 0, 0 ), 0.0001 ); QGSCOMPARENEAR( radius, 5, 0.0001 ); resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) ); QCOMPARE( result, resultTest ); - geomTest = QgsGeometry::fromWkt( QString( "POLYGON(( 0 5, 2 2, 0 -5, -1 -1 ))" ) ); + geomTest = QgsGeometry::fromWkt( QStringLiteral( "POLYGON(( 0 5, 2 2, 0 -5, -1 -1 ))" ) ); result = geomTest.minimalEnclosingCircle( center, radius ); QGSCOMPARENEARPOINT( center, QgsPointXY( 0, 0 ), 0.0001 ); QGSCOMPARENEAR( radius, 5, 0.0001 ); resultTest.setGeometry( QgsCircle( QgsPoint( center ), radius ).toPolygon( 36 ) ); QCOMPARE( result, resultTest ); - geomTest = QgsGeometry::fromWkt( QString( "MULTIPOINT( 0 5, 0 -5, 0 0 )" ) ); + geomTest = QgsGeometry::fromWkt( QStringLiteral( "MULTIPOINT( 0 5, 0 -5, 0 0 )" ) ); result = geomTest.minimalEnclosingCircle( center, radius ); QGSCOMPARENEARPOINT( center, QgsPointXY( 0, 0 ), 0.0001 ); QGSCOMPARENEAR( radius, 5, 0.0001 ); @@ -5679,7 +15355,7 @@ void TestQgsGeometry::minimalEnclosingCircle() QCOMPARE( result, resultTest ); // case 3 - geomTest = QgsGeometry::fromWkt( QString( "MULTIPOINT((0 0), (5 5), (0 -5), (0 5), (-5 0))" ) ); + geomTest = QgsGeometry::fromWkt( QStringLiteral( "MULTIPOINT((0 0), (5 5), (0 -5), (0 5), (-5 0))" ) ); result = geomTest.minimalEnclosingCircle( center, radius ); QGSCOMPARENEARPOINT( center, QgsPointXY( 0.8333, 0.8333 ), 0.0001 ); QGSCOMPARENEAR( radius, 5.8926, 0.0001 );