Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add method to add a linestring to compound curves where we extend
the existing final part if it's a linestring and the newly added
curve is also a linestring, instead of adding a whole new curve
  • Loading branch information
nyalldawson committed Mar 4, 2021
1 parent 0908a09 commit afccd31
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 29 deletions.
10 changes: 8 additions & 2 deletions python/core/auto_generated/geometry/qgscompoundcurve.sip.in
Expand Up @@ -93,9 +93,15 @@ Returns the number of curves in the geometry.
Returns the curve at the specified index.
%End

void addCurve( QgsCurve *c /Transfer/ );
void addCurve( QgsCurve *c /Transfer/, bool extendPrevious = false );
%Docstring
Adds a curve to the geometry (takes ownership)
Adds a curve to the geometry (takes ownership).

Since QGIS 3.20, if ``extendPrevious`` is ``True``, then adding a LineString when the last existing curve
in the compound curve is also a LineString will cause the existing linestring to be
extended with the newly added LineString vertices instead of appending a whole new
LineString curve to the compound curve. This can result in simplified compound curves with lesser number
of component curves while still being topologically identical to the desired result.
%End

void removeCurve( int i );
Expand Down
62 changes: 38 additions & 24 deletions src/core/geometry/qgscompoundcurve.cpp
Expand Up @@ -474,35 +474,49 @@ const QgsCurve *QgsCompoundCurve::curveAt( int i ) const
return mCurves.at( i );
}

void QgsCompoundCurve::addCurve( QgsCurve *c )
void QgsCompoundCurve::addCurve( QgsCurve *c, const bool extendPrevious )
{
if ( c )
if ( !c )
return;

if ( mCurves.empty() )
{
if ( mCurves.empty() )
{
setZMTypeFromSubGeometry( c, QgsWkbTypes::CompoundCurve );
}
setZMTypeFromSubGeometry( c, QgsWkbTypes::CompoundCurve );
}

mCurves.append( c );
if ( QgsWkbTypes::hasZ( mWkbType ) && !QgsWkbTypes::hasZ( c->wkbType() ) )
{
c->addZValue();
}
else if ( !QgsWkbTypes::hasZ( mWkbType ) && QgsWkbTypes::hasZ( c->wkbType() ) )
{
c->dropZValue();
}
if ( QgsWkbTypes::hasM( mWkbType ) && !QgsWkbTypes::hasM( c->wkbType() ) )
{
c->addMValue();
}
else if ( !QgsWkbTypes::hasM( mWkbType ) && QgsWkbTypes::hasM( c->wkbType() ) )
{
c->dropMValue();
}

if ( QgsWkbTypes::hasZ( mWkbType ) && !QgsWkbTypes::hasZ( c->wkbType() ) )
{
c->addZValue();
}
else if ( !QgsWkbTypes::hasZ( mWkbType ) && QgsWkbTypes::hasZ( c->wkbType() ) )
{
c->dropZValue();
}
if ( QgsWkbTypes::hasM( mWkbType ) && !QgsWkbTypes::hasM( c->wkbType() ) )
{
c->addMValue();
}
else if ( !QgsWkbTypes::hasM( mWkbType ) && QgsWkbTypes::hasM( c->wkbType() ) )
{
c->dropMValue();
}
clearCache();
QgsLineString *previousLineString = !mCurves.empty() ? qgsgeometry_cast< QgsLineString * >( mCurves.constLast() ) : nullptr;
const QgsLineString *newLineString = qgsgeometry_cast< const QgsLineString * >( c );
const bool canExtendPrevious = extendPrevious && previousLineString && newLineString;
if ( canExtendPrevious )
{
previousLineString->append( newLineString );
// we are taking ownership, so delete the input curve
delete c;
c = nullptr;
}
else
{
mCurves.append( c );
}

clearCache();
}

void QgsCompoundCurve::removeCurve( int i )
Expand Down
10 changes: 8 additions & 2 deletions src/core/geometry/qgscompoundcurve.h
Expand Up @@ -84,9 +84,15 @@ class CORE_EXPORT QgsCompoundCurve: public QgsCurve
const QgsCurve *curveAt( int i ) const SIP_HOLDGIL;

/**
* Adds a curve to the geometry (takes ownership)
* Adds a curve to the geometry (takes ownership).
*
* Since QGIS 3.20, if \a extendPrevious is TRUE, then adding a LineString when the last existing curve
* in the compound curve is also a LineString will cause the existing linestring to be
* extended with the newly added LineString vertices instead of appending a whole new
* LineString curve to the compound curve. This can result in simplified compound curves with lesser number
* of component curves while still being topologically identical to the desired result.
*/
void addCurve( QgsCurve *c SIP_TRANSFER );
void addCurve( QgsCurve *c SIP_TRANSFER, bool extendPrevious = false );

/**
* Removes a curve from the geometry.
Expand Down
2 changes: 1 addition & 1 deletion src/core/geometry/qgslinestring.cpp
Expand Up @@ -914,7 +914,7 @@ void QgsLineString::append( const QgsLineString *line )
setZMTypeFromSubGeometry( line, QgsWkbTypes::LineString );
}

// do not store duplicit points
// do not store duplicate points
if ( numPoints() > 0 &&
line->numPoints() > 0 &&
endPoint() == line->startPoint() )
Expand Down
32 changes: 32 additions & 0 deletions tests/src/core/testqgsgeometry.cpp
Expand Up @@ -10319,6 +10319,38 @@ void TestQgsGeometry::compoundCurve()
<< QgsPoint( QgsWkbTypes::PointM, 3, 4, 0, 9 ) );
c8.removeCurve( 1 );

// add curve and extend existing
QgsCompoundCurve c8j;
// try to extend empty compound curve
l8.setPoints( QgsPointSequence() << QgsPoint( 1, 2 ) << QgsPoint( 2, 3 ) << QgsPoint( 3, 4 ) );
c8j.addCurve( l8.clone(), true );
QCOMPARE( c8j.asWkt(), QStringLiteral( "CompoundCurve (CircularString (1 2, 2 3, 3 4))" ) );
// try to add another curve with extend existing as true - should be ignored.
l8.setPoints( QgsPointSequence() << QgsPoint( 6, 6 ) << QgsPoint( 7, 8 ) );
c8j.addCurve( l8.clone(), true );
QCOMPARE( c8j.asWkt(), QStringLiteral( "CompoundCurve (CircularString (1 2, 2 3, 3 4),CircularString (6 6, 7 8))" ) );
// try to add a linestring with extend existing as true - should be ignored because the last curve isn't a linestring
QgsLineString l8j;
l8j.setPoints( QgsPointSequence() << QgsPoint( 10, 8 ) << QgsPoint( 10, 12 ) );
c8j.addCurve( l8j.clone(), true );
QCOMPARE( c8j.asWkt(), QStringLiteral( "CompoundCurve (CircularString (1 2, 2 3, 3 4),CircularString (6 6, 7 8),(10 8, 10 12))" ) );
// try to extend with another linestring -- should add to final part
l8j.setPoints( QgsPointSequence() << QgsPoint( 11, 13 ) << QgsPoint( 12, 12 ) );
c8j.addCurve( l8j.clone(), true );
QCOMPARE( c8j.asWkt(), QStringLiteral( "CompoundCurve (CircularString (1 2, 2 3, 3 4),CircularString (6 6, 7 8),(10 8, 10 12, 11 13, 12 12))" ) );
// try to extend with another linestring -- should add to final part, with no duplicate points
l8j.setPoints( QgsPointSequence() << QgsPoint( 12, 12 ) << QgsPoint( 13, 12 ) << QgsPoint( 14, 15 ) );
c8j.addCurve( l8j.clone(), true );
QCOMPARE( c8j.asWkt(), QStringLiteral( "CompoundCurve (CircularString (1 2, 2 3, 3 4),CircularString (6 6, 7 8),(10 8, 10 12, 11 13, 12 12, 13 12, 14 15))" ) );
// not extending, should be added as new curve
l8j.setPoints( QgsPointSequence() << QgsPoint( 15, 16 ) << QgsPoint( 17, 12 ) );
c8j.addCurve( l8j.clone(), false );
QCOMPARE( c8j.asWkt(), QStringLiteral( "CompoundCurve (CircularString (1 2, 2 3, 3 4),CircularString (6 6, 7 8),(10 8, 10 12, 11 13, 12 12, 13 12, 14 15),(15 16, 17 12))" ) );
c8j.clear();
// adding a linestring as first part, with extend as true
c8j.addCurve( l8j.clone(), true );
QCOMPARE( c8j.asWkt(), QStringLiteral( "CompoundCurve ((15 16, 17 12))" ) );

//test getters/setters
QgsCompoundCurve c9;

Expand Down

0 comments on commit afccd31

Please sign in to comment.