Skip to content

Commit

Permalink
Add QgsAbstractGeometry::simplifiedTypeRef
Browse files Browse the repository at this point in the history
Returns a reference to the simplest lossless representation of this geometry,
e.g. if the geometry is a multipart geometry type with a single member geometry,
a reference to that part will be returned.

This method employs the following logic:

- For multipart geometries containing a single part only a direct reference to that part will be returned.
- For compound curve geometries containing a single curve only a direct reference to that curve will be returned.

This method returns a reference only, and does not involve any geometry cloning.
  • Loading branch information
nyalldawson committed May 24, 2021
1 parent 44a2482 commit 0d768fa
Show file tree
Hide file tree
Showing 12 changed files with 111 additions and 12 deletions.
16 changes: 16 additions & 0 deletions python/core/auto_generated/geometry/qgsabstractgeometry.sip.in
Expand Up @@ -719,6 +719,22 @@ Converts the geometry to a specified type.
:return: ``True`` if conversion was successful

.. versionadded:: 2.14
%End

virtual const QgsAbstractGeometry *simplifiedTypeRef() const /HoldGIL/;
%Docstring
Returns a reference to the simplest lossless representation of this geometry,
e.g. if the geometry is a multipart geometry type with a single member geometry,
a reference to that part will be returned.

This method employs the following logic:

- For multipart geometries containing a single part only a direct reference to that part will be returned.
- For compound curve geometries containing a single curve only a direct reference to that curve will be returned.

This method returns a reference only, and does not involve any geometry cloning.

.. versionadded:: 3.20
%End

virtual bool isValid( QString &error /Out/, int flags = 0 ) const = 0;
Expand Down
2 changes: 2 additions & 0 deletions python/core/auto_generated/geometry/qgscompoundcurve.sip.in
Expand Up @@ -85,6 +85,8 @@ of the curve.

virtual bool boundingBoxIntersects( const QgsRectangle &rectangle ) const /HoldGIL/;

virtual const QgsAbstractGeometry *simplifiedTypeRef() const /HoldGIL/;


int nCurves() const /HoldGIL/;
%Docstring
Expand Down
Expand Up @@ -245,6 +245,8 @@ Returns a geometry without curves. Caller takes ownership

virtual QgsGeometryCollection *toCurveType() const /Factory/;

virtual const QgsAbstractGeometry *simplifiedTypeRef() const /HoldGIL/;


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

Expand Down
21 changes: 13 additions & 8 deletions src/3d/symbols/qgsline3dsymbol_p.cpp
Expand Up @@ -105,12 +105,14 @@ void QgsBufferedLine3DSymbolHandler::processFeature( const QgsFeature &f, const
LineData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;

QgsGeometry geom = f.geometry();
const QgsAbstractGeometry *g = geom.constGet()->simplifiedTypeRef();

// segmentize curved geometries if necessary
if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) )
geom = QgsGeometry( geom.constGet()->segmentize() );

const QgsAbstractGeometry *g = geom.constGet();
if ( QgsWkbTypes::isCurvedType( g->wkbType() ) )
{
geom = QgsGeometry( g->segmentize() );
g = geom.constGet()->simplifiedTypeRef();
}

// TODO: configurable
const int nSegments = 4;
Expand Down Expand Up @@ -375,11 +377,14 @@ void QgsThickLine3DSymbolHandler::processFeature( const QgsFeature &f, const Qgs
QgsLineVertexData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;

QgsGeometry geom = f.geometry();
// segmentize curved geometries if necessary
if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) )
geom = QgsGeometry( geom.constGet()->segmentize() );
const QgsAbstractGeometry *g = geom.constGet()->simplifiedTypeRef();

const QgsAbstractGeometry *g = geom.constGet();
// segmentize curved geometries if necessary
if ( QgsWkbTypes::isCurvedType( g->wkbType() ) )
{
geom = QgsGeometry( g->segmentize() );
g = geom.constGet()->simplifiedTypeRef();
}

if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) )
{
Expand Down
10 changes: 6 additions & 4 deletions src/3d/symbols/qgspolygon3dsymbol_p.cpp
Expand Up @@ -163,12 +163,14 @@ void QgsPolygon3DSymbolHandler::processFeature( const QgsFeature &f, const Qgs3D
PolygonData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal;

QgsGeometry geom = f.geometry();
const QgsAbstractGeometry *g = geom.constGet()->simplifiedTypeRef();

// segmentize curved geometries if necessary
if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) )
geom = QgsGeometry( geom.constGet()->segmentize() );

const QgsAbstractGeometry *g = geom.constGet();
if ( QgsWkbTypes::isCurvedType( g->wkbType() ) )
{
geom = QgsGeometry( g->segmentize() );
g = geom.constGet()->simplifiedTypeRef();
}

const QgsPropertyCollection &ddp = mSymbol->dataDefinedProperties();
bool hasDDHeight = ddp.isActive( QgsAbstract3DSymbol::PropertyHeight );
Expand Down
5 changes: 5 additions & 0 deletions src/core/geometry/qgsabstractgeometry.cpp
Expand Up @@ -280,6 +280,11 @@ bool QgsAbstractGeometry::convertTo( QgsWkbTypes::Type type )
return true;
}

const QgsAbstractGeometry *QgsAbstractGeometry::simplifiedTypeRef() const
{
return this;
}

void QgsAbstractGeometry::filterVertices( const std::function<bool ( const QgsPoint & )> & )
{
// Ideally this would be pure virtual, but SIP has issues with that
Expand Down
16 changes: 16 additions & 0 deletions src/core/geometry/qgsabstractgeometry.h
Expand Up @@ -706,6 +706,22 @@ class CORE_EXPORT QgsAbstractGeometry
*/
virtual bool convertTo( QgsWkbTypes::Type type );

/**
* Returns a reference to the simplest lossless representation of this geometry,
* e.g. if the geometry is a multipart geometry type with a single member geometry,
* a reference to that part will be returned.
*
* This method employs the following logic:
*
* - For multipart geometries containing a single part only a direct reference to that part will be returned.
* - For compound curve geometries containing a single curve only a direct reference to that curve will be returned.
*
* This method returns a reference only, and does not involve any geometry cloning.
*
* \since QGIS 3.20
*/
virtual const QgsAbstractGeometry *simplifiedTypeRef() const SIP_HOLDGIL;

/**
* Checks validity of the geometry, and returns TRUE if the geometry is valid.
*
Expand Down
8 changes: 8 additions & 0 deletions src/core/geometry/qgscompoundcurve.cpp
Expand Up @@ -562,6 +562,14 @@ bool QgsCompoundCurve::boundingBoxIntersects( const QgsRectangle &rectangle ) co
return QgsAbstractGeometry::boundingBoxIntersects( rectangle );
}

const QgsAbstractGeometry *QgsCompoundCurve::simplifiedTypeRef() const
{
if ( mCurves.size() == 1 )
return mCurves.at( 0 );
else
return this;
}

const QgsCurve *QgsCompoundCurve::curveAt( int i ) const
{
if ( i < 0 || i >= mCurves.size() )
Expand Down
1 change: 1 addition & 0 deletions src/core/geometry/qgscompoundcurve.h
Expand Up @@ -74,6 +74,7 @@ class CORE_EXPORT QgsCompoundCurve: public QgsCurve
QgsCompoundCurve *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const override SIP_FACTORY;
bool removeDuplicateNodes( double epsilon = 4 * std::numeric_limits<double>::epsilon(), bool useZValues = false ) override;
bool boundingBoxIntersects( const QgsRectangle &rectangle ) const override SIP_HOLDGIL;
const QgsAbstractGeometry *simplifiedTypeRef() const override SIP_HOLDGIL;

/**
* Returns the number of curves in the geometry.
Expand Down
8 changes: 8 additions & 0 deletions src/core/geometry/qgsgeometrycollection.cpp
Expand Up @@ -1020,6 +1020,14 @@ QgsGeometryCollection *QgsGeometryCollection::toCurveType() const
return newCollection.release();
}

const QgsAbstractGeometry *QgsGeometryCollection::simplifiedTypeRef() const
{
if ( mGeometries.size() == 1 )
return mGeometries.at( 0 )->simplifiedTypeRef();
else
return this;
}

bool QgsGeometryCollection::transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback )
{
if ( !transformer )
Expand Down
1 change: 1 addition & 0 deletions src/core/geometry/qgsgeometrycollection.h
Expand Up @@ -237,6 +237,7 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
bool dropMValue() override;
void swapXy() override;
QgsGeometryCollection *toCurveType() const override SIP_FACTORY;
const QgsAbstractGeometry *simplifiedTypeRef() const override SIP_HOLDGIL;

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

Expand Down
33 changes: 33 additions & 0 deletions tests/src/core/testqgsgeometry.cpp
Expand Up @@ -172,6 +172,9 @@ class TestQgsGeometry : public QObject
void normalize_data();
void normalize();

void simplifiedTypeRef_data();
void simplifiedTypeRef();

// MK, Disabled 14.11.2014
// Too unclear what exactly should be tested and which variations are allowed for the line
#if 0
Expand Down Expand Up @@ -17878,6 +17881,36 @@ void TestQgsGeometry::normalize()
QCOMPARE( geom.asWkt( 1 ), expected );
}


void TestQgsGeometry::simplifiedTypeRef_data()
{
QTest::addColumn<QString>( "wkt" );
QTest::addColumn<QString>( "expected" );

QTest::newRow( "point empty" ) << QStringLiteral( "POINT EMPTY" ) << QStringLiteral( "Point EMPTY" );
QTest::newRow( "point" ) << QStringLiteral( "POINT (1 2)" ) << QStringLiteral( "Point (1 2)" );
QTest::newRow( "line empty" ) << QStringLiteral( "LINESTRING EMPTY" ) << QStringLiteral( "LineString EMPTY" );
QTest::newRow( "line" ) << QStringLiteral( "LINESTRING (1 1, 1 2, 1 3)" ) << QStringLiteral( "LineString (1 1, 1 2, 1 3)" );
QTest::newRow( "circular string" ) << QStringLiteral( "CIRCULARSTRINGZM (1 1 11 21, 1 0 12 22, 0 0 13 23, 0 1 14 24, 1 1 11 21)" ) << QStringLiteral( "CircularStringZM (1 1 11 21, 1 0 12 22, 0 0 13 23, 0 1 14 24, 1 1 11 21)" );
QTest::newRow( "compound curve empty" ) << QStringLiteral( "COMPOUNDCURVE EMPTY" ) << QStringLiteral( "CompoundCurve EMPTY" );
QTest::newRow( "compound curve one curve" ) << QStringLiteral( "COMPOUNDCURVE ((1 1, 1 2, 2 3))" ) << QStringLiteral( "LineString (1 1, 1 2, 2 3)" );
QTest::newRow( "compound curve two curves" ) << QStringLiteral( "COMPOUNDCURVE ((1 1, 1 2, 2 3),(2 3, 4 4))" ) << QStringLiteral( "CompoundCurve ((1 1, 1 2, 2 3),(2 3, 4 4))" );
QTest::newRow( "polygon empty" ) << QStringLiteral( "POLYGON EMPTY" ) << QStringLiteral( "Polygon EMPTY" );
QTest::newRow( "polygon exterior" ) << QStringLiteral( "POLYGON ((1 1, 0 1, 0 0, 1 0, 1 1))" ) << QStringLiteral( "Polygon ((1 1, 0 1, 0 0, 1 0, 1 1))" );
QTest::newRow( "collection empty" ) << QStringLiteral( "GEOMETRYCOLLECTION EMPTY" ) << QStringLiteral( "GeometryCollection EMPTY" );
QTest::newRow( "multipoint one point" ) << QStringLiteral( "MULTIPOINT(1 1)" ) << QStringLiteral( "Point (1 1)" );
QTest::newRow( "multipoint" ) << QStringLiteral( "MULTIPOINT(1 1, 3 4, 1 3, 2 2)" ) << QStringLiteral( "MultiPoint ((1 1),(3 4),(1 3),(2 2))" );
}

void TestQgsGeometry::simplifiedTypeRef()
{
QFETCH( QString, wkt );
QFETCH( QString, expected );

QgsGeometry geom = QgsGeometry::fromWkt( wkt );
QCOMPARE( geom.constGet()->simplifiedTypeRef()->asWkt( 1 ), expected );
}

// MK, Disabled 14.11.2014
// Too unclear what exactly should be tested and which variations are allowed for the line
#if 0
Expand Down

0 comments on commit 0d768fa

Please sign in to comment.