Skip to content

Commit

Permalink
Create class QgsAbstractGeometryTransformer, which can be used
Browse files Browse the repository at this point in the history
to transform the vertices of a QgsAbstractGeometry

E.g.

        class Transformer(QgsAbstractGeometryTransformer):

            def transformPoint(self, x, y, z, m):
                return True, x * 2, y + 1, z, m

        transformer = Transformer()
        g = QgsGeometry.fromWkt('LineString(3 0, 10 0, 10 10)')
        g.get().transform(transformer)
        print(g.asWkt()) # 'LineString (6 1, 20 1, 20 11)'
  • Loading branch information
nyalldawson committed Feb 8, 2021
1 parent bad13f2 commit 6f4b32f
Show file tree
Hide file tree
Showing 26 changed files with 498 additions and 1 deletion.
15 changes: 15 additions & 0 deletions python/core/auto_generated/geometry/qgsabstractgeometry.sip.in
Expand Up @@ -707,6 +707,21 @@ Checks validity of the geometry, and returns ``True`` if the geometry is valid.
.. versionadded:: 3.8
%End

virtual bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = 0 ) = 0;
%Docstring
Transforms the vertices from the geometry in place, using the specified geometry ``transformer``
object.

Depending on the ``transformer`` used, this may result in an invalid geometry.

The optional ``feedback`` argument can be used to cancel the transformation before it completes.
If this is done, the geometry will be left in a semi-transformed state.

:return: ``True`` if the geometry was successfully transformed.

.. versionadded:: 3.18
%End


QgsGeometryPartIterator parts();
%Docstring
Expand Down
3 changes: 3 additions & 0 deletions python/core/auto_generated/geometry/qgscircularstring.sip.in
Expand Up @@ -163,6 +163,9 @@ Sets the circular string's points
virtual double yAt( int index ) const /HoldGIL/;


virtual bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = 0 );



virtual QgsCircularString *createEmptyWithSameType() const /Factory/;

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


virtual bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = 0 );



virtual QgsCompoundCurve *createEmptyWithSameType() const /Factory/;

Expand Down
3 changes: 3 additions & 0 deletions python/core/auto_generated/geometry/qgscurvepolygon.sip.in
Expand Up @@ -272,6 +272,9 @@ Returns approximate rotation angle for a vertex. Usually average angle between a
virtual QgsCurvePolygon *toCurveType() const /Factory/;


virtual bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = 0 );



virtual QgsCurvePolygon *createEmptyWithSameType() const /Factory/;

Expand Down
Expand Up @@ -243,6 +243,9 @@ Returns a geometry without curves. Caller takes ownership
virtual QgsGeometryCollection *toCurveType() const /Factory/;


virtual bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = 0 );





Expand Down
61 changes: 61 additions & 0 deletions python/core/auto_generated/geometry/qgsgeometrytransformer.sip.in
@@ -0,0 +1,61 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/geometry/qgsgeometrytransformer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/




class QgsAbstractGeometryTransformer
{
%Docstring
An abstract base class for classes which transform geometries by transforming
input points to output points.

.. versionadded:: 3.18
%End

%TypeHeaderCode
#include "qgsgeometrytransformer.h"
%End
public:

virtual ~QgsAbstractGeometryTransformer();

virtual bool transformPoint( double &x /In,Out/, double &y /In,Out/, double &z /In,Out/, double &m /In,Out/ ) = 0;
%Docstring
Transforms the point defined by the coordinates (``x``, ``y``, ``z``) and the specified ``m`` value.

:param x: point x coordinate
:param y: point y coordinate
:param z: point z coordinate, or NaN if the input point is 2D only
:param m: point m value, or NaN if not available

:return: ``True`` if point was transformed (or no transformation was required), or ``False`` if point could not be transformed successfully.

Example
-------

A transformer which multiples the x coordinate by 3 and adds 10 to the y coordinate:

.. code-block:: python

class MyTransformer(QgsAbstractGeometryTransformer):

def transformPoint(self, x, y, z, m):
# returns a tuple of True to indicate success, then the modified x/y/z/m values
return True, x*3, y+10, z, m
%End

};

/************************************************************************
* This file has been generated automatically from *
* *
* src/core/geometry/qgsgeometrytransformer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
3 changes: 3 additions & 0 deletions python/core/auto_generated/geometry/qgslinestring.sip.in
Expand Up @@ -543,6 +543,9 @@ of the curve.
virtual bool convertTo( QgsWkbTypes::Type type );


virtual bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = 0 );



virtual QgsLineString *createEmptyWithSameType() const /Factory/;

Expand Down
3 changes: 3 additions & 0 deletions python/core/auto_generated/geometry/qgspoint.sip.in
Expand Up @@ -450,6 +450,9 @@ Angle undefined. Always returns 0.0
virtual bool convertTo( QgsWkbTypes::Type type );


virtual bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = 0 );



virtual QgsPoint *createEmptyWithSameType() const /Factory/;

Expand Down
1 change: 1 addition & 0 deletions python/core/core_auto.sip
Expand Up @@ -301,6 +301,7 @@
%Include auto_generated/geometry/qgsgeometry.sip
%Include auto_generated/geometry/qgsgeometrycollection.sip
%Include auto_generated/geometry/qgsgeometryengine.sip
%Include auto_generated/geometry/qgsgeometrytransformer.sip
%Include auto_generated/geometry/qgsgeometryutils.sip
%Include auto_generated/geometry/qgslinesegment.sip
%Include auto_generated/geometry/qgslinestring.sip
Expand Down
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -1183,6 +1183,7 @@ set(QGIS_CORE_HDRS
geometry/qgsgeometryeditutils.h
geometry/qgsgeometryengine.h
geometry/qgsgeometryfactory.h
geometry/qgsgeometrytransformer.h
geometry/qgsgeometryutils.h
geometry/qgsgeos.h
geometry/qgsinternalgeometryengine.h
Expand Down
17 changes: 17 additions & 0 deletions src/core/geometry/qgsabstractgeometry.h
Expand Up @@ -45,6 +45,8 @@ class QgsGeometryPartIterator;
class QgsGeometryConstPartIterator;
class QgsConstWkbPtr;
class QPainterPath;
class QgsAbstractGeometryTransformer;
class QgsFeedback;

typedef QVector< QgsPoint > QgsPointSequence;
#ifndef SIP_RUN
Expand Down Expand Up @@ -689,6 +691,21 @@ class CORE_EXPORT QgsAbstractGeometry
*/
virtual bool isValid( QString &error SIP_OUT, int flags = 0 ) const = 0;

/**
* Transforms the vertices from the geometry in place, using the specified geometry \a transformer
* object.
*
* Depending on the \a transformer used, this may result in an invalid geometry.
*
* The optional \a feedback argument can be used to cancel the transformation before it completes.
* If this is done, the geometry will be left in a semi-transformed state.
*
* \returns TRUE if the geometry was successfully transformed.
*
* \since QGIS 3.18
*/
virtual bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = nullptr ) = 0;

#ifndef SIP_RUN

/**
Expand Down
46 changes: 46 additions & 0 deletions src/core/geometry/qgscircularstring.cpp
Expand Up @@ -24,6 +24,8 @@
#include "qgspoint.h"
#include "qgswkbptr.h"
#include "qgslogger.h"
#include "qgsgeometrytransformer.h"
#include "qgsfeedback.h"

#include <QJsonObject>
#include <QPainter>
Expand Down Expand Up @@ -580,6 +582,50 @@ double QgsCircularString::yAt( int index ) const
return 0.0;
}

bool QgsCircularString::transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback )
{
if ( !transformer )
return false;

bool hasZ = is3D();
bool hasM = isMeasure();
int size = mX.size();

double *srcX = mX.data();
double *srcY = mY.data();
double *srcM = hasM ? mM.data() : nullptr;
double *srcZ = hasZ ? mZ.data() : nullptr;

bool res = true;
for ( int i = 0; i < size; ++i )
{
double x = *srcX;
double y = *srcY;
double z = hasZ ? *srcZ : std::numeric_limits<double>::quiet_NaN();
double m = hasM ? *srcM : std::numeric_limits<double>::quiet_NaN();
if ( !transformer->transformPoint( x, y, z, m ) )
{
res = false;
break;
}

*srcX++ = x;
*srcY++ = y;
if ( hasM )
*srcM++ = m;
if ( hasZ )
*srcZ++ = z;

if ( feedback && feedback->isCanceled() )
{
res = false;
break;
}
}
clearCache();
return res;
}

void QgsCircularString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
{
bool hasZ = is3D();
Expand Down
2 changes: 2 additions & 0 deletions src/core/geometry/qgscircularstring.h
Expand Up @@ -129,6 +129,8 @@ class CORE_EXPORT QgsCircularString: public QgsCurve
double xAt( int index ) const override SIP_HOLDGIL;
double yAt( int index ) const override SIP_HOLDGIL;

bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = nullptr ) override;

#ifndef SIP_RUN
void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) override;
void transformVertices( const std::function< QgsPoint( const QgsPoint & ) > &transform ) override;
Expand Down
22 changes: 22 additions & 0 deletions src/core/geometry/qgscompoundcurve.cpp
Expand Up @@ -21,6 +21,7 @@
#include "qgsgeometryutils.h"
#include "qgslinestring.h"
#include "qgswkbptr.h"
#include "qgsfeedback.h"

#include <QJsonObject>
#include <QPainter>
Expand Down Expand Up @@ -815,6 +816,27 @@ double QgsCompoundCurve::yAt( int index ) const
return 0.0;
}

bool QgsCompoundCurve::transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback )
{
bool res = true;
for ( QgsCurve *curve : qgis::as_const( mCurves ) )
{
if ( !curve->transform( transformer ) )
{
res = false;
break;
}

if ( feedback && feedback->isCanceled() )
{
res = false;
break;
}
}
clearCache();
return res;
}

void QgsCompoundCurve::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
{
for ( QgsCurve *curve : qgis::as_const( mCurves ) )
Expand Down
2 changes: 2 additions & 0 deletions src/core/geometry/qgscompoundcurve.h
Expand Up @@ -131,6 +131,8 @@ class CORE_EXPORT QgsCompoundCurve: public QgsCurve
double xAt( int index ) const override SIP_HOLDGIL;
double yAt( int index ) const override SIP_HOLDGIL;

bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = nullptr ) override;

#ifndef SIP_RUN
void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) override;
void transformVertices( const std::function< QgsPoint( const QgsPoint & ) > &transform ) override;
Expand Down
30 changes: 30 additions & 0 deletions src/core/geometry/qgscurvepolygon.cpp
Expand Up @@ -24,6 +24,7 @@
#include "qgspolygon.h"
#include "qgswkbptr.h"
#include "qgsmulticurve.h"
#include "qgsfeedback.h"

#include <QJsonArray>
#include <QJsonObject>
Expand Down Expand Up @@ -1264,6 +1265,35 @@ QgsCurvePolygon *QgsCurvePolygon::toCurveType() const
return clone();
}

bool QgsCurvePolygon::transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback )
{
if ( !transformer )
return false;

bool res = true;
if ( mExteriorRing )
res = mExteriorRing->transform( transformer, feedback );

if ( !res || ( feedback && feedback->isCanceled() ) )
{
clearCache();
return false;
}

for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
{
res = curve->transform( transformer );

if ( feedback && feedback->isCanceled() )
res = false;

if ( !res )
break;
}
clearCache();
return res;
}

void QgsCurvePolygon::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
{
if ( mExteriorRing )
Expand Down
2 changes: 2 additions & 0 deletions src/core/geometry/qgscurvepolygon.h
Expand Up @@ -261,6 +261,8 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface

QgsCurvePolygon *toCurveType() const override SIP_FACTORY;

bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = nullptr ) override;

#ifndef SIP_RUN
void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) override;
void transformVertices( const std::function< QgsPoint( const QgsPoint & ) > &transform ) override;
Expand Down
22 changes: 22 additions & 0 deletions src/core/geometry/qgsgeometrycollection.cpp
Expand Up @@ -27,6 +27,7 @@ email : marco.hugentobler at sourcepole dot com
#include "qgsmultipolygon.h"
#include "qgswkbptr.h"
#include "qgsgeos.h"
#include "qgsfeedback.h"

#include <nlohmann/json.hpp>
#include <memory>
Expand Down Expand Up @@ -978,6 +979,27 @@ QgsGeometryCollection *QgsGeometryCollection::toCurveType() const
return newCollection.release();
}

bool QgsGeometryCollection::transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback )
{
if ( !transformer )
return false;

bool res = true;
for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
{
if ( geom )
res = geom->transform( transformer, feedback );

if ( feedback && feedback->isCanceled() )
res = false;

if ( !res )
break;
}
clearCache();
return res;
}

bool QgsGeometryCollection::wktOmitChildType() const
{
return false;
Expand Down

0 comments on commit 6f4b32f

Please sign in to comment.