From e2e47d71c6de99b3b472b2ff37b32ae0a798f02f Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Fri, 9 Jan 2015 12:15:47 +0100 Subject: [PATCH] Add rotate and transform(QTransform) methods to QgsGeometry Reimplement translate in terms of transform(QTransform). Includes new unit test for rotate() and translate() methods. Includes sip bindings update. --- python/core/qgsgeometry.sip | 12 +++++ src/core/qgsgeometry.cpp | 56 ++++++++++++++-------- src/core/qgsgeometry.h | 19 ++++++-- tests/src/core/testqgsgeometry.cpp | 77 ++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 24 deletions(-) diff --git a/python/core/qgsgeometry.sip b/python/core/qgsgeometry.sip index 710b10e2ce5..eef270ee5e6 100644 --- a/python/core/qgsgeometry.sip +++ b/python/core/qgsgeometry.sip @@ -251,6 +251,18 @@ class QgsGeometry @return 0 in case of success*/ int transform( const QgsCoordinateTransform& ct ); + /**Transform this geometry as described by QTransform ct + @note added in 2.8 + @return 0 in case of success*/ + int transform( const QTransform& ct ); + + /**Rotate this geometry around the Z axis + @note added in 2.8 + @param rotation clockwise rotation in degrees + @param center rotation center + @return 0 in case of success*/ + int rotate( double rotation, const QgsPoint& center ); + /**Splits this geometry according to a given line. Note that the geometry is only split once. If there are several intersections between geometry and splitLine, only the first one is considered. @param splitLine the line that splits the geometry diff --git a/src/core/qgsgeometry.cpp b/src/core/qgsgeometry.cpp index 2579638e596..82304fdb4bd 100644 --- a/src/core/qgsgeometry.cpp +++ b/src/core/qgsgeometry.cpp @@ -2706,7 +2706,7 @@ int QgsGeometry::addPart( GEOSGeometry *newPart ) return 0; } -int QgsGeometry::translate( double dx, double dy ) +int QgsGeometry::transform( const QTransform& t ) { if ( mDirtyWkb ) exportGeosToWkb(); @@ -2717,17 +2717,17 @@ int QgsGeometry::translate( double dx, double dy ) return 1; } + bool hasZValue = false; QgsWkbPtr wkbPtr( mGeometry + 1 ); QGis::WkbType wkbType; wkbPtr >> wkbType; - bool hasZValue = false; switch ( wkbType ) { case QGis::WKBPoint25D: case QGis::WKBPoint: { - translateVertex( wkbPtr, dx, dy, hasZValue ); + transformVertex( wkbPtr, t, hasZValue ); } break; @@ -2738,7 +2738,7 @@ int QgsGeometry::translate( double dx, double dy ) int nPoints; wkbPtr >> nPoints; for ( int index = 0; index < nPoints; ++index ) - translateVertex( wkbPtr, dx, dy, hasZValue ); + transformVertex( wkbPtr, t, hasZValue ); break; } @@ -2754,7 +2754,7 @@ int QgsGeometry::translate( double dx, double dy ) int nPoints; wkbPtr >> nPoints; for ( int index2 = 0; index2 < nPoints; ++index2 ) - translateVertex( wkbPtr, dx, dy, hasZValue ); + transformVertex( wkbPtr, t, hasZValue ); } break; @@ -2766,11 +2766,10 @@ int QgsGeometry::translate( double dx, double dy ) { int nPoints; wkbPtr >> nPoints; - for ( int index = 0; index < nPoints; ++index ) { wkbPtr += 1 + sizeof( int ); - translateVertex( wkbPtr, dx, dy, hasZValue ); + transformVertex( wkbPtr, t, hasZValue ); } break; } @@ -2787,7 +2786,8 @@ int QgsGeometry::translate( double dx, double dy ) int nPoints; wkbPtr >> nPoints; for ( int index2 = 0; index2 < nPoints; ++index2 ) - translateVertex( wkbPtr, dx, dy, hasZValue ); + transformVertex( wkbPtr, t, hasZValue ); + } break; } @@ -2801,19 +2801,17 @@ int QgsGeometry::translate( double dx, double dy ) for ( int index = 0; index < nPolys; ++index ) { wkbPtr += 1 + sizeof( int ); //skip endian and polygon type - int nRings; wkbPtr >> nRings; - for ( int index2 = 0; index2 < nRings; ++index2 ) { int nPoints; wkbPtr >> nPoints; for ( int index3 = 0; index3 < nPoints; ++index3 ) - translateVertex( wkbPtr, dx, dy, hasZValue ); + transformVertex( wkbPtr, t, hasZValue ); + } } - break; } default: @@ -2823,6 +2821,19 @@ int QgsGeometry::translate( double dx, double dy ) return 0; } +int QgsGeometry::translate( double dx, double dy ) +{ + return transform( QTransform::fromTranslate( dx, dy ) ); +} + +int QgsGeometry::rotate( double rotation, const QgsPoint& center ) +{ + QTransform t = QTransform::fromTranslate( center.x(), center.y() ); + t.rotate( -rotation ); + t.translate( -center.x(), -center.y() ); + return transform( t ); +} + int QgsGeometry::transform( const QgsCoordinateTransform& ct ) { if ( mDirtyWkb ) @@ -4612,20 +4623,25 @@ bool QgsGeometry::convertToMultiType() return true; } -void QgsGeometry::translateVertex( QgsWkbPtr &wkbPtr, double dx, double dy, bool hasZValue ) +void QgsGeometry::transformVertex( QgsWkbPtr &wkbPtr, const QTransform& trans, bool hasZValue ) { - double x, y, translated_x, translated_y; + double x, y, rotated_x, rotated_y; + + QgsWkbPtr tmp = wkbPtr; + + memcpy( &x, tmp, sizeof( double ) ); + tmp += sizeof( double ); + memcpy( &y, tmp, sizeof( double ) ); + tmp += sizeof( double ); + + trans.map( x, y, &rotated_x, &rotated_y ); //x-coordinate - memcpy( &x, wkbPtr, sizeof( double ) ); - translated_x = x + dx; - memcpy( wkbPtr, &translated_x, sizeof( double ) ); + memcpy( wkbPtr, &rotated_x, sizeof( double ) ); wkbPtr += sizeof( double ); //y-coordinate - memcpy( &y, wkbPtr, sizeof( double ) ); - translated_y = y + dy; - memcpy( wkbPtr, &translated_y, sizeof( double ) ); + memcpy( wkbPtr, &rotated_y, sizeof( double ) ); wkbPtr += sizeof( double ); if ( hasZValue ) diff --git a/src/core/qgsgeometry.h b/src/core/qgsgeometry.h index efa62071d09..3a8e5afafd4 100644 --- a/src/core/qgsgeometry.h +++ b/src/core/qgsgeometry.h @@ -293,6 +293,18 @@ class CORE_EXPORT QgsGeometry @return 0 in case of success*/ int transform( const QgsCoordinateTransform& ct ); + /**Transform this geometry as described by QTransform ct + @note added in 2.8 + @return 0 in case of success*/ + int transform( const QTransform& ct ); + + /**Rotate this geometry around the Z axis + @note added in 2.8 + @param rotation clockwise rotation in degrees + @param center rotation center + @return 0 in case of success*/ + int rotate( double rotation, const QgsPoint& center ); + /**Splits this geometry according to a given line. Note that the geometry is only split once. If there are several intersections between geometry and splitLine, only the first one is considered. @param splitLine the line that splits the geometry @@ -565,12 +577,11 @@ class CORE_EXPORT QgsGeometry const GEOSCoordSequence* old_sequence, GEOSCoordSequence** new_sequence ); - /**Translates a single vertex by dx and dy. + /**Transform a single vertex by QTransform @param wkbPtr pointer to current position in wkb array. Is increased automatically by the function - @param dx translation of x coordinate - @param dy translation of y coordinate + @param trans transform matrix @param hasZValue 25D type?*/ - void translateVertex( QgsWkbPtr &wkbPtr, double dx, double dy, bool hasZValue ); + void transformVertex( QgsWkbPtr &wkbPtr, const QTransform& trans, bool hasZValue ); /**Transforms a single vertex by ct. @param wkbPtr pointer to current position in wkb. Is increased automatically by the function diff --git a/tests/src/core/testqgsgeometry.cpp b/tests/src/core/testqgsgeometry.cpp index 19b11099d90..e5858199d3c 100644 --- a/tests/src/core/testqgsgeometry.cpp +++ b/tests/src/core/testqgsgeometry.cpp @@ -60,6 +60,8 @@ class TestQgsGeometry : public QObject #endif void intersectionCheck1(); void intersectionCheck2(); + void translateCheck1(); + void rotateCheck1(); void unionCheck1(); void unionCheck2(); void differenceCheck1(); @@ -359,6 +361,81 @@ void TestQgsGeometry::intersectionCheck2() QVERIFY( !mpPolygonGeometryA->intersects( mpPolygonGeometryC ) ); } +void TestQgsGeometry::translateCheck1() +{ + QString wkt = "LINESTRING(0 0, 10 0, 10 10)"; + QScopedPointer geom( QgsGeometry::fromWkt( wkt ) ); + geom->translate( 10, -5 ); + QString obtained = geom->exportToWkt(); + QString expected = "LINESTRING(10 -5, 20 -5, 20 5)"; + QCOMPARE( obtained, expected ); + geom->translate( -10, 5 ); + obtained = geom->exportToWkt(); + QCOMPARE( obtained, wkt ); + + wkt = "POLYGON((-2 4,-2 -10,2 3,-2 4),(1 1,-1 1,-1 -1,1 1))"; + geom.reset( QgsGeometry::fromWkt( wkt ) ); + geom->translate( -2, 10 ); + obtained = geom->exportToWkt(); + expected = "POLYGON((-4 14,-4 0,0 13,-4 14),(-1 11,-3 11,-3 9,-1 11))"; + QCOMPARE( obtained, expected ); + geom->translate( 2, -10 ); + obtained = geom->exportToWkt(); + QCOMPARE( obtained, wkt ); + + wkt = "POINT(40 50)"; + geom.reset( QgsGeometry::fromWkt( wkt ) ); + geom->translate( -2, 10 ); + obtained = geom->exportToWkt(); + expected = "POINT(38 60)"; + QCOMPARE( obtained, expected ); + geom->translate( 2, -10 ); + obtained = geom->exportToWkt(); + QCOMPARE( obtained, wkt ); + +} + +void TestQgsGeometry::rotateCheck1() +{ + QString wkt = "LINESTRING(0 0, 10 0, 10 10)"; + QScopedPointer geom( QgsGeometry::fromWkt( wkt ) ); + geom->rotate( 90, QgsPoint( 0, 0 ) ); + QString obtained = geom->exportToWkt(); + QString expected = "LINESTRING(0 0, 0 -10, 10 -10)"; + QCOMPARE( obtained, expected ); + geom->rotate( -90, QgsPoint( 0, 0 ) ); + obtained = geom->exportToWkt(); + QCOMPARE( obtained, wkt ); + + wkt = "POLYGON((-2 4,-2 -10,2 3,-2 4),(1 1,-1 1,-1 -1,1 1))"; + geom.reset( QgsGeometry::fromWkt( wkt ) ); + geom->rotate( 90, QgsPoint( 0, 0 ) ); + obtained = geom->exportToWkt(); + expected = "POLYGON((4 2,-10 2,3 -2,4 2),(1 -1,1 1,-1 1,1 -1))"; + QCOMPARE( obtained, expected ); + geom->rotate( -90, QgsPoint( 0, 0 ) ); + obtained = geom->exportToWkt(); + QCOMPARE( obtained, wkt ); + + wkt = "POINT(40 50)"; + geom.reset( QgsGeometry::fromWkt( wkt ) ); + geom->rotate( 90, QgsPoint( 0, 0 ) ); + obtained = geom->exportToWkt(); + expected = "POINT(50 -40)"; + QCOMPARE( obtained, expected ); + geom->rotate( -90, QgsPoint( 0, 0 ) ); + obtained = geom->exportToWkt(); + QCOMPARE( obtained, wkt ); + geom->rotate( 180, QgsPoint( 40, 0 ) ); + expected = "POINT(40 -50)"; + obtained = geom->exportToWkt(); + QCOMPARE( obtained, expected ); + geom->rotate( 180, QgsPoint( 40, 0 ) ); // round-trip + obtained = geom->exportToWkt(); + QCOMPARE( obtained, wkt ); + +} + void TestQgsGeometry::unionCheck1() { // should be a multipolygon with 2 parts as A does not intersect C