Skip to content
Permalink
Browse files

Add optimised method to QgsMapToPixelSimplifier which avoids clones

in unwanted circumstances and works on QgsAbstractGeometry objects
instead of QgsGeometry

(cherry picked from commit ff730b6)
  • Loading branch information
nyalldawson committed Feb 19, 2021
1 parent fdaf54c commit e61381155959c3d22717c3d579f08791a9d5dc4a
@@ -25,6 +25,17 @@ Abstract base class for simplify geometries using a specific algorithm
virtual QgsGeometry simplify( const QgsGeometry &geometry ) const = 0;
%Docstring
Returns a simplified version the specified geometry
%End

virtual QgsAbstractGeometry *simplify( const QgsAbstractGeometry *geometry ) const = 0 /Factory/;
%Docstring
Returns a simplified version the specified ``geometry``.

Will return ``None`` if no simplification is to be performed to the geometry.

Caller takes ownership of the returned geometry.

.. versionadded:: 3.18
%End

public:
@@ -61,6 +72,8 @@ is specified in layer units.

virtual QgsGeometry simplify( const QgsGeometry &geometry ) const;

virtual QgsAbstractGeometry *simplify( const QgsAbstractGeometry *geometry ) const /Factory/;


protected:

@@ -78,9 +78,8 @@ Sets the local simplification algorithm of the vector layer managed

virtual QgsGeometry simplify( const QgsGeometry &geometry ) const;

%Docstring
Returns a simplified version the specified geometry
%End
virtual QgsAbstractGeometry *simplify( const QgsAbstractGeometry *geometry ) const /Factory/;


void setTolerance( double value );
%Docstring
@@ -18,6 +18,7 @@
#include "qgsgeometrysimplifier.h"
#include "qgsrectangle.h"
#include "qgsgeometry.h"
#include "qgsgeos.h"

bool QgsAbstractGeometrySimplifier::isGeneralizableByDeviceBoundingBox( const QgsRectangle &envelope, float mapToPixelTol )
{
@@ -47,3 +48,15 @@ QgsGeometry QgsTopologyPreservingSimplifier::simplify( const QgsGeometry &geomet
return geometry.simplify( mTolerance );
}

QgsAbstractGeometry *QgsTopologyPreservingSimplifier::simplify( const QgsAbstractGeometry *geometry ) const
{
if ( !geometry )
{
return nullptr;
}

QgsGeos geos( geometry );
std::unique_ptr< QgsAbstractGeometry > simplifiedGeom( geos.simplify( mTolerance ) );
return simplifiedGeom.release();
}

@@ -22,8 +22,10 @@

class QgsGeometry;
class QgsRectangle;
class QgsAbstractGeometry;

#include "qgis_core.h"
#include "qgis_sip.h"

/**
* \ingroup core
@@ -37,6 +39,17 @@ class CORE_EXPORT QgsAbstractGeometrySimplifier
//! Returns a simplified version the specified geometry
virtual QgsGeometry simplify( const QgsGeometry &geometry ) const = 0;

/**
* Returns a simplified version the specified \a geometry.
*
* Will return NULLPTR if no simplification is to be performed to the geometry.
*
* Caller takes ownership of the returned geometry.
*
* \since QGIS 3.18
*/
virtual QgsAbstractGeometry *simplify( const QgsAbstractGeometry *geometry ) const = 0 SIP_FACTORY;

// MapToPixel simplification helper methods
public:
//! Returns whether the device-envelope can be replaced by its BBOX when is applied the specified tolerance
@@ -65,6 +78,7 @@ class CORE_EXPORT QgsTopologyPreservingSimplifier : public QgsAbstractGeometrySi
QgsTopologyPreservingSimplifier( double tolerance );

QgsGeometry simplify( const QgsGeometry &geometry ) const override;
QgsAbstractGeometry *simplify( const QgsAbstractGeometry *geometry ) const override SIP_FACTORY;

protected:
//! Distance tolerance for the simplification
@@ -415,3 +415,47 @@ QgsGeometry QgsMapToPixelSimplifier::simplify( const QgsGeometry &geometry ) con

return QgsGeometry( simplifyGeometry( mSimplifyFlags, mSimplifyAlgorithm, *geometry.constGet(), mTolerance, false ) );
}

QgsAbstractGeometry *QgsMapToPixelSimplifier::simplify( const QgsAbstractGeometry *geometry ) const
{
//
// IMPORTANT!!!!!!!
// We want to avoid any geometry cloning we possibly can here, which is why the
// "fail" paths always return nullptr
//

if ( !geometry )
{
return nullptr;
}
if ( mSimplifyFlags == QgsMapToPixelSimplifier::NoFlags )
{
return nullptr;
}

// Check whether the geometry can be simplified using the map2pixel context
const QgsWkbTypes::Type singleType = QgsWkbTypes::singleType( geometry->wkbType() );
const QgsWkbTypes::Type flatType = QgsWkbTypes::flatType( singleType );
if ( flatType == QgsWkbTypes::Point )
{
return nullptr;
}

const bool isaLinearRing = flatType == QgsWkbTypes::Polygon;
const int numPoints = geometry->nCoordinates();

if ( numPoints <= ( isaLinearRing ? 6 : 3 ) )
{
// No simplify simple geometries
return nullptr;
}

const QgsRectangle envelope = geometry->boundingBox();
if ( std::max( envelope.width(), envelope.height() ) / numPoints > mTolerance * 2.0 )
{
//points are in average too far apart to lead to any significant simplification
return nullptr;
}

return simplifyGeometry( mSimplifyFlags, mSimplifyAlgorithm, *geometry, mTolerance, false ).release();
}
@@ -89,8 +89,8 @@ class CORE_EXPORT QgsMapToPixelSimplifier : public QgsAbstractGeometrySimplifier
//! Sets the local simplification algorithm of the vector layer managed
void setSimplifyAlgorithm( SimplifyAlgorithm simplifyAlgorithm ) { mSimplifyAlgorithm = simplifyAlgorithm; }

//! Returns a simplified version the specified geometry
QgsGeometry simplify( const QgsGeometry &geometry ) const override;
QgsAbstractGeometry *simplify( const QgsAbstractGeometry *geometry ) const override SIP_FACTORY;

//! Sets the tolerance of the vector layer managed
void setTolerance( double value ) { mTolerance = value; }
@@ -77,6 +77,7 @@ class TestQgsMapToPixelGeometrySimplifier : public QObject
void testCircularString();
void testVisvalingam();
void testRingValidity();
void testAbstractGeometrySimplify();

};

@@ -217,5 +218,42 @@ void TestQgsMapToPixelGeometrySimplifier::testRingValidity()

}

void TestQgsMapToPixelGeometrySimplifier::testAbstractGeometrySimplify()
{
// test direct simplification of abstract geometries, especially the "no simplification required" paths
QgsMapToPixelSimplifier simplifier( QgsMapToPixelSimplifier::SimplifyGeometry, 5 );
std::unique_ptr< QgsAbstractGeometry > simplified;

// no input geometry
simplified.reset( simplifier.simplify( nullptr ) );
QVERIFY( !simplified.get() );

// no simplification flag
simplifier.setSimplifyFlags( QgsMapToPixelSimplifier::NoFlags );
simplified.reset( simplifier.simplify( nullptr ) );
QVERIFY( !simplified.get() );

simplifier.setSimplifyFlags( QgsMapToPixelSimplifier::SimplifyGeometry );
// point geometry = no simplification
simplified.reset( simplifier.simplify( QgsGeometry::fromWkt( QStringLiteral( "Point( 1 2 )" ) ).constGet() ) );
QVERIFY( !simplified.get() );
simplified.reset( simplifier.simplify( QgsGeometry::fromWkt( QStringLiteral( "PointZ( 1 2 3 )" ) ).constGet() ) );
QVERIFY( !simplified.get() );
simplified.reset( simplifier.simplify( QgsGeometry::fromWkt( QStringLiteral( "MultiPoint( 1 2, 3 4 )" ) ).constGet() ) );
QVERIFY( !simplified.get() );

// triangle polygon = no simplification
simplified.reset( simplifier.simplify( QgsGeometry::fromWkt( QStringLiteral( "Polygon(( 1 1, 1 2, 2 2, 1 1))" ) ).constGet() ) );
QVERIFY( !simplified.get() );

// too large bounding box vs tolerance
simplified.reset( simplifier.simplify( QgsGeometry::fromWkt( QStringLiteral( "LineString( 1 1, 50 1.5, 100 2, 100 200 )" ) ).constGet() ) );
QVERIFY( !simplified.get() );

// should be simplified
simplified.reset( simplifier.simplify( QgsGeometry::fromWkt( QStringLiteral( "LineString( 1 1, 2 1.1, 2.1 1.09, 3 0.9, 4 1 )" ) ).constGet() ) );
QCOMPARE( simplified->asWkt( 2 ), QStringLiteral( "LineString (1 1, 2 1.1, 3 0.9, 4 1)" ) );
}

QGSTEST_MAIN( TestQgsMapToPixelGeometrySimplifier )
#include "testqgsmaptopixelgeometrysimplifier.moc"

0 comments on commit e613811

Please sign in to comment.