Skip to content

Commit

Permalink
Add method to swap x/y coordinates in geometries
Browse files Browse the repository at this point in the history
This can be used to repair geometries which have
accidentally had their latitude and longitude coordinates
reversed.
  • Loading branch information
nyalldawson committed Apr 4, 2018
1 parent b19aef3 commit 1ea20a4
Show file tree
Hide file tree
Showing 22 changed files with 147 additions and 2 deletions.
9 changes: 9 additions & 0 deletions python/core/geometry/qgsabstractgeometry.sip.in
Expand Up @@ -587,6 +587,15 @@ Drops any measure values which exist in the geometry.
.. seealso:: :py:func:`dropZValue`

.. versionadded:: 2.14
%End

virtual void swapXy() = 0;
%Docstring
Swaps the x and y coordinates from the geometry. This can be used
to repair geometries which have accidentally had their latitude and longitude
coordinates reversed.

.. versionadded:: 3.2
%End

virtual bool convertTo( QgsWkbTypes::Type type );
Expand Down
2 changes: 2 additions & 0 deletions python/core/geometry/qgscircularstring.sip.in
Expand Up @@ -122,6 +122,8 @@ Sets the circular string's points

virtual bool dropMValue();

virtual void swapXy();

virtual double xAt( int index ) const;

virtual double yAt( int index ) const;
Expand Down
2 changes: 2 additions & 0 deletions python/core/geometry/qgscompoundcurve.sip.in
Expand Up @@ -154,6 +154,8 @@ Appends first point if not already closed.

virtual bool dropMValue();

virtual void swapXy();


virtual double xAt( int index ) const;

Expand Down
2 changes: 2 additions & 0 deletions python/core/geometry/qgscurvepolygon.sip.in
Expand Up @@ -195,6 +195,8 @@ Returns approximate rotation angle for a vertex. Usually average angle between a

virtual bool dropMValue();

virtual void swapXy();


virtual QgsCurvePolygon *toCurveType() const /Factory/;

Expand Down
2 changes: 2 additions & 0 deletions python/core/geometry/qgsgeometrycollection.sip.in
Expand Up @@ -172,6 +172,8 @@ Returns a geometry without curves. Caller takes ownership

virtual bool dropMValue();

virtual void swapXy();

virtual QgsGeometryCollection *toCurveType() const /Factory/;


Expand Down
2 changes: 2 additions & 0 deletions python/core/geometry/qgslinestring.sip.in
Expand Up @@ -286,6 +286,8 @@ of the curve.

virtual bool dropMValue();

virtual void swapXy();


virtual bool convertTo( QgsWkbTypes::Type type );

Expand Down
4 changes: 2 additions & 2 deletions python/core/geometry/qgsmultilinestring.sip.in
Expand Up @@ -30,9 +30,9 @@ class QgsMultiLineString: QgsMultiCurve

virtual bool fromWkt( const QString &wkt );

virtual QDomElement asGml2( QDomDocument &doc, int precision = 17, const QString &ns = "gml", const QgsAbstractGeometry::AxisOrder &axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const;
virtual QDomElement asGml2( QDomDocument &doc, int precision = 17, const QString &ns = "gml", QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const;

virtual QDomElement asGml3( QDomDocument &doc, int precision = 17, const QString &ns = "gml", const QgsAbstractGeometry::AxisOrder &axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const;
virtual QDomElement asGml3( QDomDocument &doc, int precision = 17, const QString &ns = "gml", QgsAbstractGeometry::AxisOrder axisOrder = QgsAbstractGeometry::AxisOrder::XY ) const;

virtual QString asJson( int precision = 17 ) const;

Expand Down
2 changes: 2 additions & 0 deletions python/core/geometry/qgspoint.sip.in
Expand Up @@ -415,6 +415,8 @@ Angle undefined. Always returns 0.0

virtual bool dropMValue();

virtual void swapXy();

virtual bool convertTo( QgsWkbTypes::Type type );


Expand Down
8 changes: 8 additions & 0 deletions src/core/geometry/qgsabstractgeometry.h
Expand Up @@ -552,6 +552,14 @@ class CORE_EXPORT QgsAbstractGeometry
*/
virtual bool dropMValue() = 0;

/**
* Swaps the x and y coordinates from the geometry. This can be used
* to repair geometries which have accidentally had their latitude and longitude
* coordinates reversed.
* \since QGIS 3.2
*/
virtual void swapXy() = 0;

/**
* Converts the geometry to a specified type.
* \returns true if conversion was successful
Expand Down
6 changes: 6 additions & 0 deletions src/core/geometry/qgscircularstring.cpp
Expand Up @@ -1124,3 +1124,9 @@ bool QgsCircularString::dropMValue()
mM.clear();
return true;
}

void QgsCircularString::swapXy()
{
std::swap( mX, mY );
clearCache();
}
1 change: 1 addition & 0 deletions src/core/geometry/qgscircularstring.h
Expand Up @@ -94,6 +94,7 @@ class CORE_EXPORT QgsCircularString: public QgsCurve
bool addMValue( double mValue = 0 ) override;
bool dropZValue() override;
bool dropMValue() override;
void swapXy() override;
double xAt( int index ) const override;
double yAt( int index ) const override;
#ifndef SIP_RUN
Expand Down
9 changes: 9 additions & 0 deletions src/core/geometry/qgscompoundcurve.cpp
Expand Up @@ -904,3 +904,12 @@ bool QgsCompoundCurve::dropMValue()
return true;
}

void QgsCompoundCurve::swapXy()
{
for ( QgsCurve *curve : qgis::as_const( mCurves ) )
{
curve->swapXy();
}
clearCache();
}

1 change: 1 addition & 0 deletions src/core/geometry/qgscompoundcurve.h
Expand Up @@ -122,6 +122,7 @@ class CORE_EXPORT QgsCompoundCurve: public QgsCurve

bool dropZValue() override;
bool dropMValue() override;
void swapXy() override;

double xAt( int index ) const override;
double yAt( int index ) const override;
Expand Down
11 changes: 11 additions & 0 deletions src/core/geometry/qgscurvepolygon.cpp
Expand Up @@ -1158,6 +1158,17 @@ bool QgsCurvePolygon::dropMValue()
return true;
}

void QgsCurvePolygon::swapXy()
{
if ( mExteriorRing )
mExteriorRing->swapXy();
for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
{
curve->swapXy();
}
clearCache();
}

QgsCurvePolygon *QgsCurvePolygon::toCurveType() const
{
return clone();
Expand Down
1 change: 1 addition & 0 deletions src/core/geometry/qgscurvepolygon.h
Expand Up @@ -151,6 +151,7 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
bool addMValue( double mValue = 0 ) override;
bool dropZValue() override;
bool dropMValue() override;
void swapXy() override;

QgsCurvePolygon *toCurveType() const override SIP_FACTORY;
#ifndef SIP_RUN
Expand Down
10 changes: 10 additions & 0 deletions src/core/geometry/qgsgeometrycollection.cpp
Expand Up @@ -830,6 +830,16 @@ bool QgsGeometryCollection::dropMValue()
return true;
}

void QgsGeometryCollection::swapXy()
{
for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
{
if ( geom )
geom->swapXy();
}
clearCache();
}

QgsGeometryCollection *QgsGeometryCollection::toCurveType() const
{
std::unique_ptr< QgsGeometryCollection > newCollection( new QgsGeometryCollection() );
Expand Down
1 change: 1 addition & 0 deletions src/core/geometry/qgsgeometrycollection.h
Expand Up @@ -140,6 +140,7 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
bool addMValue( double mValue = 0 ) override;
bool dropZValue() override;
bool dropMValue() override;
void swapXy() override;
QgsGeometryCollection *toCurveType() const override SIP_FACTORY;

#ifndef SIP_RUN
Expand Down
6 changes: 6 additions & 0 deletions src/core/geometry/qgslinestring.cpp
Expand Up @@ -1253,6 +1253,12 @@ bool QgsLineString::dropMValue()
return true;
}

void QgsLineString::swapXy()
{
std::swap( mX, mY );
clearCache();
}

bool QgsLineString::convertTo( QgsWkbTypes::Type type )
{
if ( type == mWkbType )
Expand Down
1 change: 1 addition & 0 deletions src/core/geometry/qgslinestring.h
Expand Up @@ -234,6 +234,7 @@ class CORE_EXPORT QgsLineString: public QgsCurve

bool dropZValue() override;
bool dropMValue() override;
void swapXy() override;

bool convertTo( QgsWkbTypes::Type type ) override;

Expand Down
6 changes: 6 additions & 0 deletions src/core/geometry/qgspoint.cpp
Expand Up @@ -553,6 +553,12 @@ bool QgsPoint::dropMValue()
return true;
}

void QgsPoint::swapXy()
{
std::swap( mX, mY );
clearCache();
}

bool QgsPoint::convertTo( QgsWkbTypes::Type type )
{
if ( type == mWkbType )
Expand Down
1 change: 1 addition & 0 deletions src/core/geometry/qgspoint.h
Expand Up @@ -436,6 +436,7 @@ class CORE_EXPORT QgsPoint: public QgsAbstractGeometry
bool addMValue( double mValue = 0 ) override;
bool dropZValue() override;
bool dropMValue() override;
void swapXy() override;
bool convertTo( QgsWkbTypes::Type type ) override;

#ifndef SIP_RUN
Expand Down
62 changes: 62 additions & 0 deletions tests/src/core/testqgsgeometry.cpp
Expand Up @@ -1122,6 +1122,15 @@ void TestQgsGeometry::point()
QVERIFY( !p.removeDuplicateNodes() );
QCOMPARE( p.x(), 1.0 );
QCOMPARE( p.y(), 2.0 );

// swap xy
p = QgsPoint( 1.1, 2.2, 3.3, 4.4, QgsWkbTypes::PointZM );
p.swapXy();
QCOMPARE( p.x(), 2.2 );
QCOMPARE( p.y(), 1.1 );
QCOMPARE( p.z(), 3.3 );
QCOMPARE( p.m(), 4.4 );
QCOMPARE( p.wkbType(), QgsWkbTypes::PointZM );
}

void TestQgsGeometry::circularString()
Expand Down Expand Up @@ -2475,6 +2484,12 @@ void TestQgsGeometry::circularString()
QVERIFY( !nodeLine.removeDuplicateNodes( 0.02, true ) );
QCOMPARE( nodeLine.asWkt( 2 ), QStringLiteral( "CircularStringZ (11 2 1, 11.01 1.99 2, 11.02 2.01 3, 11 12 4, 111 12 5)" ) );

//swap xy
QgsCircularString swapLine;
swapLine.swapXy(); // no crash
swapLine.setPoints( QgsPointSequence() << QgsPoint( 11, 2, 3, 4, QgsWkbTypes::PointZM ) << QgsPoint( 11, 12, 13, 14, QgsWkbTypes::PointZM ) << QgsPoint( 111, 12, 23, 24, QgsWkbTypes::PointZM ) );
swapLine.swapXy();
QCOMPARE( swapLine.asWkt(), QStringLiteral( "CircularStringZM (2 11 3 4, 12 11 13 14, 12 111 23 24)" ) );
}


Expand Down Expand Up @@ -4308,6 +4323,13 @@ void TestQgsGeometry::lineString()
<< QgsPoint( 11, 12, 4 ) << QgsPoint( 111, 12, 5 ) << QgsPoint( 111.01, 11.99, 6 ) );
QVERIFY( !nodeLine.removeDuplicateNodes( 0.02, true ) );
QCOMPARE( nodeLine.asWkt( 2 ), QStringLiteral( "LineStringZ (11 2 1, 11.01 1.99 2, 11.02 2.01 3, 11 12 4, 111 12 5, 111.01 11.99 6)" ) );

// swap xy
QgsLineString swapLine;
swapLine.swapXy(); // no crash
swapLine.setPoints( QgsPointSequence() << QgsPoint( 11, 2, 3, 4, QgsWkbTypes::PointZM ) << QgsPoint( 11, 12, 13, 14, QgsWkbTypes::PointZM ) << QgsPoint( 111, 12, 23, 24, QgsWkbTypes::PointZM ) );
swapLine.swapXy();
QCOMPARE( swapLine.asWkt( 2 ), QStringLiteral( "LineStringZM (2 11 3 4, 12 11 13 14, 12 111 23 24)" ) );
}

void TestQgsGeometry::polygon()
Expand Down Expand Up @@ -6055,6 +6077,19 @@ void TestQgsGeometry::polygon()
nodePolygon.addInteriorRing( nodeLine.clone() );
QVERIFY( nodePolygon.removeDuplicateNodes( 0.02 ) );
QCOMPARE( nodePolygon.asWkt( 2 ), QStringLiteral( "Polygon ((11 2, 11 12, 11 22, 11 2),(11 2, 11.01 2.01, 11 2.01, 11 2))" ) );

// swap XY
QgsPolygon swapPolygon;
swapPolygon.swapXy(); //no crash
QgsLineString swapLine;
swapLine.setPoints( QgsPointSequence() << QgsPoint( 11, 2, 3, 4, QgsWkbTypes::PointZM ) << QgsPoint( 11, 12, 13, 14, QgsWkbTypes::PointZM ) << QgsPoint( 11, 22, 23, 24, QgsWkbTypes::PointZM ) << QgsPoint( 11, 2, 3, 4, QgsWkbTypes::PointZM ) );
swapPolygon.setExteriorRing( swapLine.clone() );
swapPolygon.swapXy();
QCOMPARE( swapPolygon.asWkt(), QStringLiteral( "PolygonZM ((2 11 3 4, 12 11 13 14, 22 11 23 24, 2 11 3 4))" ) );
swapLine.setPoints( QgsPointSequence() << QgsPoint( 1, 2, 5, 6, QgsWkbTypes::PointZM ) << QgsPoint( 11.01, 2.01, 15, 16, QgsWkbTypes::PointZM ) << QgsPoint( 11, 2.01, 25, 26, QgsWkbTypes::PointZM ) << QgsPoint( 11, 2, 5, 6, QgsWkbTypes::PointZM ) );
swapPolygon.addInteriorRing( swapLine.clone() );
swapPolygon.swapXy();
QCOMPARE( swapPolygon.asWkt( 2 ), QStringLiteral( "PolygonZM ((11 2 3 4, 11 12 13 14, 11 22 23 24, 11 2 3 4),(2 1 5 6, 2.01 11.01 15 16, 2.01 11 25 26, 2 11 5 6, 2 1 5 6))" ) );
}

void TestQgsGeometry::triangle()
Expand Down Expand Up @@ -10810,6 +10845,20 @@ void TestQgsGeometry::compoundCurve()
nodeCurve.addCurve( linePart.clone() );
QVERIFY( nodeCurve.removeDuplicateNodes( 0.02 ) );
QCOMPARE( nodeCurve.asWkt( 2 ), QStringLiteral( "CompoundCurve ((1 1, 111.01 11.99),(111.01 11.99, 31 33))" ) );

// swap xy
QgsCompoundCurve swapCurve;
swapCurve.swapXy(); //no crash
nodeLine.setPoints( QgsPointSequence() << QgsPoint( 11, 2, 3, 4, QgsWkbTypes::PointZM ) << QgsPoint( 11, 12, 13, 14, QgsWkbTypes::PointZM ) << QgsPoint( 111, 12, 23, 24, QgsWkbTypes::PointZM ) );
swapCurve.addCurve( nodeLine.clone() );
swapCurve.swapXy();
QCOMPARE( swapCurve.asWkt(), QStringLiteral( "CompoundCurveZM (CircularStringZM (2 11 3 4, 12 11 13 14, 12 111 23 24))" ) );
QgsLineString lsSwap;
lsSwap.setPoints( QgsPointSequence() << QgsPoint( 12, 111, 23, 24, QgsWkbTypes::PointZM ) << QgsPoint( 22, 122, 33, 34, QgsWkbTypes::PointZM ) );
swapCurve.addCurve( lsSwap.clone() );
swapCurve.swapXy();
QCOMPARE( swapCurve.asWkt(), QStringLiteral( "CompoundCurveZM (CircularStringZM (11 2 3 4, 11 12 13 14, 111 12 23 24),(111 12 23 24, 122 22 33 34))" ) );

}

void TestQgsGeometry::multiPoint()
Expand Down Expand Up @@ -15187,6 +15236,19 @@ void TestQgsGeometry::geometryCollection()
gcNodes.addGeometry( nodeLine.clone() );
QVERIFY( gcNodes.removeDuplicateNodes( 0.02 ) );
QCOMPARE( gcNodes.asWkt( 2 ), QStringLiteral( "GeometryCollection (LineString (11 2, 11 12, 111 12),LineString (11 2, 11 12, 111 12))" ) );

//swapXy
QgsGeometryCollection swapCollect;
QgsLineString swapLine;
swapCollect.swapXy(); // no crash
swapLine.setPoints( QgsPointSequence() << QgsPoint( 11, 2, 3, 4, QgsWkbTypes::PointZM ) << QgsPoint( 11, 12, 13, 14, QgsWkbTypes::PointZM ) << QgsPoint( 111, 12, 23, 24, QgsWkbTypes::PointZM ) );
swapCollect.addGeometry( swapLine.clone() );
swapCollect.swapXy();
QCOMPARE( swapCollect.asWkt(), QStringLiteral( "GeometryCollection (LineStringZM (2 11 3 4, 12 11 13 14, 12 111 23 24))" ) );
swapLine.setPoints( QgsPointSequence() << QgsPoint( 11, 2, 5, 6, QgsWkbTypes::PointZM ) << QgsPoint( 11.01, 1.99, 15, 16, QgsWkbTypes::PointZM ) << QgsPoint( 11.02, 2.01, 25, 26, QgsWkbTypes::PointZM ) );
swapCollect.addGeometry( swapLine.clone() );
swapCollect.swapXy();
QCOMPARE( swapCollect.asWkt( 2 ), QStringLiteral( "GeometryCollection (LineStringZM (11 2 3 4, 11 12 13 14, 111 12 23 24),LineStringZM (2 11 5 6, 1.99 11.01 15 16, 2.01 11.02 25 26))" ) );
}

void TestQgsGeometry::fromQgsPointXY()
Expand Down

0 comments on commit 1ea20a4

Please sign in to comment.