Skip to content

Commit

Permalink
Add optimised method to QgsMapToPixelSimplifier which avoids clones
Browse files Browse the repository at this point in the history
in unwanted circumstances and works on QgsAbstractGeometry objects
instead of QgsGeometry
  • Loading branch information
nyalldawson committed Jan 19, 2021
1 parent 24d4d4a commit ff730b6
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 4 deletions.
13 changes: 13 additions & 0 deletions python/core/auto_generated/qgsgeometrysimplifier.sip.in
Expand Up @@ -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:
Expand Down Expand Up @@ -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:

Expand Down
Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions src/core/qgsgeometrysimplifier.cpp
Expand Up @@ -18,6 +18,7 @@
#include "qgsgeometrysimplifier.h"
#include "qgsrectangle.h"
#include "qgsgeometry.h"
#include "qgsgeos.h"

bool QgsAbstractGeometrySimplifier::isGeneralizableByDeviceBoundingBox( const QgsRectangle &envelope, float mapToPixelTol )
{
Expand Down Expand Up @@ -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();
}

14 changes: 14 additions & 0 deletions src/core/qgsgeometrysimplifier.h
Expand Up @@ -22,8 +22,10 @@

class QgsGeometry;
class QgsRectangle;
class QgsAbstractGeometry;

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

/**
* \ingroup core
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
44 changes: 44 additions & 0 deletions src/core/qgsmaptopixelgeometrysimplifier.cpp
Expand Up @@ -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();
}
2 changes: 1 addition & 1 deletion src/core/qgsmaptopixelgeometrysimplifier.h
Expand Up @@ -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; }
Expand Down
38 changes: 38 additions & 0 deletions tests/src/core/testqgsmaptopixelgeometrysimplifier.cpp
Expand Up @@ -77,6 +77,7 @@ class TestQgsMapToPixelGeometrySimplifier : public QObject
void testCircularString();
void testVisvalingam();
void testRingValidity();
void testAbstractGeometrySimplify();

};

Expand Down Expand Up @@ -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 ff730b6

Please sign in to comment.