Skip to content
Permalink
Browse files

Add method to swap x/y coordinates in geometries

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 1ea20a4b352c17e37204c0ed0fc3b01a3f2f1ea6
@@ -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 );
@@ -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;
@@ -154,6 +154,8 @@ Appends first point if not already closed.

virtual bool dropMValue();

virtual void swapXy();


virtual double xAt( int index ) const;

@@ -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/;

@@ -172,6 +172,8 @@ Returns a geometry without curves. Caller takes ownership

virtual bool dropMValue();

virtual void swapXy();

virtual QgsGeometryCollection *toCurveType() const /Factory/;


@@ -286,6 +286,8 @@ of the curve.

virtual bool dropMValue();

virtual void swapXy();


virtual bool convertTo( QgsWkbTypes::Type type );

@@ -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;

@@ -415,6 +415,8 @@ Angle undefined. Always returns 0.0

virtual bool dropMValue();

virtual void swapXy();

virtual bool convertTo( QgsWkbTypes::Type type );


@@ -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
@@ -1124,3 +1124,9 @@ bool QgsCircularString::dropMValue()
mM.clear();
return true;
}

void QgsCircularString::swapXy()
{
std::swap( mX, mY );
clearCache();
}
@@ -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
@@ -904,3 +904,12 @@ bool QgsCompoundCurve::dropMValue()
return true;
}

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

@@ -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;
@@ -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();
@@ -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
@@ -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() );
@@ -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
@@ -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 )
@@ -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;

@@ -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 )
@@ -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
@@ -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()
@@ -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)" ) );
}


@@ -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()
@@ -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()
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()
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()

0 comments on commit 1ea20a4

Please sign in to comment.
You can’t perform that action at this time.