Skip to content

Commit

Permalink
Add method to filter vertices for geometries in place, by providing a…
Browse files Browse the repository at this point in the history
… custom filter function
  • Loading branch information
nyalldawson committed May 29, 2018
1 parent 8341b9b commit f092c7e
Show file tree
Hide file tree
Showing 21 changed files with 195 additions and 0 deletions.
Expand Up @@ -158,6 +158,7 @@ Sets the circular string's points
virtual double yAt( int index ) const;



virtual QgsCircularString *createEmptyWithSameType() const /Factory/;


Expand Down
Expand Up @@ -162,6 +162,7 @@ Appends first point if not already closed.
virtual double yAt( int index ) const;



virtual QgsCompoundCurve *createEmptyWithSameType() const /Factory/;


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



virtual QgsCurvePolygon *createEmptyWithSameType() const /Factory/;


Expand Down
1 change: 1 addition & 0 deletions python/core/auto_generated/geometry/qgsgeometry.sip.in
Expand Up @@ -1588,6 +1588,7 @@ was performed on the geometry.
.. versionadded:: 3.0
%End


static QgsGeometry fromQPointF( QPointF point );
%Docstring
Construct geometry from a QPointF
Expand Down
Expand Up @@ -345,6 +345,8 @@ class QgsShapeburstFillSymbolLayer : QgsFillSymbolLayer
~QgsShapeburstFillSymbolLayer();




static QgsSymbolLayer *create( const QgsStringMap &properties = QgsStringMap() ) /Factory/;


Expand Down
5 changes: 5 additions & 0 deletions src/core/geometry/qgsabstractgeometry.cpp
Expand Up @@ -246,6 +246,11 @@ bool QgsAbstractGeometry::convertTo( QgsWkbTypes::Type type )
return true;
}

void QgsAbstractGeometry::filterVertices( const std::function<bool ( const QgsPoint & )> & )
{
// Ideally this would be pure virtual, but SIP has issues with that
}

QgsVertexIterator QgsAbstractGeometry::vertices() const
{
return QgsVertexIterator( this );
Expand Down
12 changes: 12 additions & 0 deletions src/core/geometry/qgsabstractgeometry.h
Expand Up @@ -569,6 +569,18 @@ class CORE_EXPORT QgsAbstractGeometry

#ifndef SIP_RUN

/**
* Filters the vertices from the geometry in place, removing any which do not return true for the \a filter function
* check. Has no meaning when called on a single point geometry.
*
* Depending on the \a filter used, this may result in an invalid geometry. However, CurvePolygon rings which are no longer
* valid rings will be automatically removed after filtering.
*
* \since QGIS 3.2
* \note Not available in Python bindings
*/
virtual void filterVertices( const std::function< bool( const QgsPoint & ) > &filter );

/**
* \ingroup core
* The vertex_iterator class provides STL-style iterator for vertices.
Expand Down
46 changes: 46 additions & 0 deletions src/core/geometry/qgscircularstring.cpp
Expand Up @@ -546,6 +546,52 @@ double QgsCircularString::yAt( int index ) const
return 0.0;
}

void QgsCircularString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
{
bool hasZ = is3D();
bool hasM = isMeasure();
int size = mX.size();

double *srcX = mX.data(); // clazy:exclude=detaching-member
double *srcY = mY.data(); // clazy:exclude=detaching-member
double *srcM = hasM ? mM.data() : nullptr; // clazy:exclude=detaching-member
double *srcZ = hasZ ? mZ.data() : nullptr; // clazy:exclude=detaching-member

double *destX = srcX;
double *destY = srcY;
double *destM = srcM;
double *destZ = srcZ;

int filteredPoints = 0;
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 ( filter( QgsPoint( x, y, z, m ) ) )
{
filteredPoints++;
*destX++ = x;
*destY++ = y;
if ( hasM )
*destM++ = m;
if ( hasZ )
*destZ++ = z;
}
}

mX.resize( filteredPoints );
mY.resize( filteredPoints );
if ( hasZ )
mZ.resize( filteredPoints );
if ( hasM )
mM.resize( filteredPoints );

clearCache();
}

void QgsCircularString::points( QgsPointSequence &pts ) const
{
pts.clear();
Expand Down
2 changes: 2 additions & 0 deletions src/core/geometry/qgscircularstring.h
Expand Up @@ -125,7 +125,9 @@ class CORE_EXPORT QgsCircularString: public QgsCurve
void swapXy() override;
double xAt( int index ) const override;
double yAt( int index ) const override;

#ifndef SIP_RUN
void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) override;

/**
* Cast the \a geom to a QgsCircularString.
Expand Down
9 changes: 9 additions & 0 deletions src/core/geometry/qgscompoundcurve.cpp
Expand Up @@ -773,6 +773,15 @@ double QgsCompoundCurve::yAt( int index ) const
return 0.0;
}

void QgsCompoundCurve::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
{
for ( QgsCurve *curve : qgis::as_const( mCurves ) )
{
curve->filterVertices( filter );
}
clearCache();
}

void QgsCompoundCurve::sumUpArea( double &sum ) const
{
for ( const QgsCurve *curve : mCurves )
Expand Down
2 changes: 2 additions & 0 deletions src/core/geometry/qgscompoundcurve.h
Expand Up @@ -125,7 +125,9 @@ class CORE_EXPORT QgsCompoundCurve: public QgsCurve

double xAt( int index ) const override;
double yAt( int index ) const override;

#ifndef SIP_RUN
void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) override;

/**
* Cast the \a geom to a QgsCompoundCurve.
Expand Down
25 changes: 25 additions & 0 deletions src/core/geometry/qgscurvepolygon.cpp
Expand Up @@ -1174,6 +1174,31 @@ QgsCurvePolygon *QgsCurvePolygon::toCurveType() const
return clone();
}

void QgsCurvePolygon::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
{
if ( mExteriorRing )
mExteriorRing->filterVertices( filter );

QVector<QgsCurve *> filteredRings;
filteredRings.reserve( mInteriorRings.size() );
for ( QgsCurve *curve : qgis::as_const( mInteriorRings ) )
{
curve->filterVertices( filter );

if ( !curve->isRing() )
{
// remove invalid rings
delete curve;
}
else
{
filteredRings << curve;
}
}
mInteriorRings = filteredRings;
clearCache();
}

int QgsCurvePolygon::childCount() const
{
return 1 + mInteriorRings.count();
Expand Down
2 changes: 2 additions & 0 deletions src/core/geometry/qgscurvepolygon.h
Expand Up @@ -153,7 +153,9 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
void swapXy() override;

QgsCurvePolygon *toCurveType() const override SIP_FACTORY;

#ifndef SIP_RUN
void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) override;

/**
* Cast the \a geom to a QgsCurvePolygon.
Expand Down
10 changes: 10 additions & 0 deletions src/core/geometry/qgsgeometry.cpp
Expand Up @@ -2574,6 +2574,16 @@ QString QgsGeometry::lastError() const
return mLastError;
}

void QgsGeometry::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
{
if ( !d->geometry )
return;

detach();

d->geometry->filterVertices( filter );
}

void QgsGeometry::convertPointList( const QVector<QgsPointXY> &input, QgsPointSequence &output )
{
output.clear();
Expand Down
12 changes: 12 additions & 0 deletions src/core/geometry/qgsgeometry.h
Expand Up @@ -1529,6 +1529,18 @@ class CORE_EXPORT QgsGeometry
*/
QString lastError() const;

/**
* Filters the vertices from the geometry in place, removing any which do not return true for the \a filter function
* check. Has no effect when called on a single point geometry.
*
* Depending on the \a filter used, this may result in an invalid geometry. However, CurvePolygon rings which are no longer
* valid rings will be automatically removed after filtering.
*
* \since QGIS 3.2
* \note Not available in Python bindings
*/
void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) SIP_SKIP;

/**
* Construct geometry from a QPointF
* \param point source QPointF
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::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
{
for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
{
if ( geom )
geom->filterVertices( filter );
}
clearCache();
}

void QgsGeometryCollection::swapXy()
{
for ( QgsAbstractGeometry *geom : qgis::as_const( mGeometries ) )
Expand Down
1 change: 1 addition & 0 deletions src/core/geometry/qgsgeometrycollection.h
Expand Up @@ -143,6 +143,7 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
QgsGeometryCollection *toCurveType() const override SIP_FACTORY;

#ifndef SIP_RUN
void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) override;

/**
* Cast the \a geom to a QgsGeometryCollection.
Expand Down
46 changes: 46 additions & 0 deletions src/core/geometry/qgslinestring.cpp
Expand Up @@ -1329,3 +1329,49 @@ bool QgsLineString::convertTo( QgsWkbTypes::Type type )
return QgsCurve::convertTo( type );
}
}

void QgsLineString::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
{
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;

double *destX = srcX;
double *destY = srcY;
double *destM = srcM;
double *destZ = srcZ;

int filteredPoints = 0;
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 ( filter( QgsPoint( x, y, z, m ) ) )
{
filteredPoints++;
*destX++ = x;
*destY++ = y;
if ( hasM )
*destM++ = m;
if ( hasZ )
*destZ++ = z;
}
}

mX.resize( filteredPoints );
mY.resize( filteredPoints );
if ( hasZ )
mZ.resize( filteredPoints );
if ( hasM )
mM.resize( filteredPoints );

clearCache();
}
1 change: 1 addition & 0 deletions src/core/geometry/qgslinestring.h
Expand Up @@ -268,6 +268,7 @@ class CORE_EXPORT QgsLineString: public QgsCurve
bool convertTo( QgsWkbTypes::Type type ) override;

#ifndef SIP_RUN
void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) override;

/**
* Cast the \a geom to a QgsLineString.
Expand Down
4 changes: 4 additions & 0 deletions src/core/geometry/qgspoint.cpp
Expand Up @@ -592,6 +592,10 @@ bool QgsPoint::convertTo( QgsWkbTypes::Type type )
return false;
}

void QgsPoint::filterVertices( const std::function<bool ( const QgsPoint & )> & )
{
// no meaning for points
}

QPointF QgsPoint::toQPointF() const
{
Expand Down
2 changes: 2 additions & 0 deletions src/core/geometry/qgspoint.h
Expand Up @@ -440,6 +440,8 @@ class CORE_EXPORT QgsPoint: public QgsAbstractGeometry

#ifndef SIP_RUN

void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) override;

/**
* Cast the \a geom to a QgsPoint.
* Should be used by qgsgeometry_cast<QgsPoint *>( geometry ).
Expand Down

0 comments on commit f092c7e

Please sign in to comment.