Add method to filter vertices for geometries in place, by providing a custom filter function

This commit is contained in:
Nyall Dawson 2018-05-29 16:12:39 +10:00
parent 8341b9b19c
commit f092c7edb7
21 changed files with 195 additions and 0 deletions

View File

@ -158,6 +158,7 @@ Sets the circular string's points
virtual double yAt( int index ) const;
virtual QgsCircularString *createEmptyWithSameType() const /Factory/;

View File

@ -162,6 +162,7 @@ Appends first point if not already closed.
virtual double yAt( int index ) const;
virtual QgsCompoundCurve *createEmptyWithSameType() const /Factory/;

View File

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

View File

@ -1588,6 +1588,7 @@ was performed on the geometry.
.. versionadded:: 3.0
%End
static QgsGeometry fromQPointF( QPointF point );
%Docstring
Construct geometry from a QPointF

View File

@ -345,6 +345,8 @@ class QgsShapeburstFillSymbolLayer : QgsFillSymbolLayer
~QgsShapeburstFillSymbolLayer();
static QgsSymbolLayer *create( const QgsStringMap &properties = QgsStringMap() ) /Factory/;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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