Skip to content

Commit

Permalink
Add option to remove redundant vertices in QgsAbstractGeometry::snapp…
Browse files Browse the repository at this point in the history
…edToGrid

If opted in, then vertices which form a midpoint of a straight line segment will be removed in the output
  • Loading branch information
nyalldawson committed Jun 3, 2024
1 parent ec49513 commit 57965ee
Show file tree
Hide file tree
Showing 28 changed files with 81 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ E.g. :py:class:`QgsLineString` -> :py:class:`QgsCompoundCurve`, :py:class:`QgsPo
:return: the converted geometry. Caller takes ownership
%End

virtual QgsAbstractGeometry *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const = 0 /Factory/;
virtual QgsAbstractGeometry *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const = 0 /Factory/;
%Docstring
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
Ownership is transferred to the caller.
Expand All @@ -626,6 +626,7 @@ In this case, it can be thought like rounding the x and y of all the points/vert
:param vSpacing: Vertical spacing of the grid (y axis). 0 to disable.
:param dSpacing: Depth spacing of the grid (z axis). 0 (default) to disable.
:param mSpacing: Custom dimension spacing of the grid (m axis). 0 (default) to disable.
:param removeRedundantPoints: if ``True``, then points which are redundant (e.g. they represent mid points on a straight line segment) will be skipped (since QGIS 3.38)
%End

virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false ) = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ Appends the contents of another circular ``string`` to the end of this circular

virtual QgsLineString *curveToLine( double tolerance = M_PI_2 / 90, SegmentationToleranceType toleranceType = MaximumAngle ) const /Factory/;

virtual QgsCircularString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const /Factory/;
virtual QgsCircularString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;

virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ of the curve.
:param toleranceType: maximum segmentation angle or maximum difference between approximation and curve
%End

virtual QgsCompoundCurve *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const /Factory/;
virtual QgsCompoundCurve *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;

virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Curve polygon geometry type

virtual QgsAbstractGeometry *boundary() const /Factory/;

virtual QgsCurvePolygon *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const /Factory/;
virtual QgsCurvePolygon *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;

virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Returns a geometry from within the collection.

virtual void clear();

virtual QgsGeometryCollection *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const /Factory/;
virtual QgsGeometryCollection *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;

virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ segment in the line.
int indexOf( const QgsPoint &point ) const final;
virtual bool isValid( QString &error /Out/, Qgis::GeometryValidityFlags flags = Qgis::GeometryValidityFlags() ) const;

virtual QgsLineString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const /Factory/;
virtual QgsLineString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;

virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );

Expand Down
2 changes: 1 addition & 1 deletion python/PyQt6/core/auto_generated/geometry/qgspoint.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ Example

virtual QgsPoint *clone() const /Factory/;

virtual QgsPoint *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const /Factory/;
virtual QgsPoint *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;

virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ E.g. :py:class:`QgsLineString` -> :py:class:`QgsCompoundCurve`, :py:class:`QgsPo
:return: the converted geometry. Caller takes ownership
%End

virtual QgsAbstractGeometry *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const = 0 /Factory/;
virtual QgsAbstractGeometry *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const = 0 /Factory/;
%Docstring
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
Ownership is transferred to the caller.
Expand All @@ -626,6 +626,7 @@ In this case, it can be thought like rounding the x and y of all the points/vert
:param vSpacing: Vertical spacing of the grid (y axis). 0 to disable.
:param dSpacing: Depth spacing of the grid (z axis). 0 (default) to disable.
:param mSpacing: Custom dimension spacing of the grid (m axis). 0 (default) to disable.
:param removeRedundantPoints: if ``True``, then points which are redundant (e.g. they represent mid points on a straight line segment) will be skipped (since QGIS 3.38)
%End

virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false ) = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ Appends the contents of another circular ``string`` to the end of this circular

virtual QgsLineString *curveToLine( double tolerance = M_PI_2 / 90, SegmentationToleranceType toleranceType = MaximumAngle ) const /Factory/;

virtual QgsCircularString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const /Factory/;
virtual QgsCircularString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;

virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ of the curve.
:param toleranceType: maximum segmentation angle or maximum difference between approximation and curve
%End

virtual QgsCompoundCurve *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const /Factory/;
virtual QgsCompoundCurve *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;

virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );

Expand Down
2 changes: 1 addition & 1 deletion python/core/auto_generated/geometry/qgscurvepolygon.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Curve polygon geometry type

virtual QgsAbstractGeometry *boundary() const /Factory/;

virtual QgsCurvePolygon *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const /Factory/;
virtual QgsCurvePolygon *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;

virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Returns a geometry from within the collection.

virtual void clear();

virtual QgsGeometryCollection *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const /Factory/;
virtual QgsGeometryCollection *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;

virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );

Expand Down
2 changes: 1 addition & 1 deletion python/core/auto_generated/geometry/qgslinestring.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ segment in the line.
int indexOf( const QgsPoint &point ) const final;
virtual bool isValid( QString &error /Out/, Qgis::GeometryValidityFlags flags = Qgis::GeometryValidityFlags() ) const;

virtual QgsLineString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const /Factory/;
virtual QgsLineString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;

virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );

Expand Down
2 changes: 1 addition & 1 deletion python/core/auto_generated/geometry/qgspoint.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ Example

virtual QgsPoint *clone() const /Factory/;

virtual QgsPoint *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const /Factory/;
virtual QgsPoint *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;

virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );

Expand Down
3 changes: 2 additions & 1 deletion src/core/geometry/qgsabstractgeometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -644,8 +644,9 @@ class CORE_EXPORT QgsAbstractGeometry
* \param vSpacing Vertical spacing of the grid (y axis). 0 to disable.
* \param dSpacing Depth spacing of the grid (z axis). 0 (default) to disable.
* \param mSpacing Custom dimension spacing of the grid (m axis). 0 (default) to disable.
* \param removeRedundantPoints if TRUE, then points which are redundant (e.g. they represent mid points on a straight line segment) will be skipped (since QGIS 3.38)
*/
virtual QgsAbstractGeometry *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const = 0 SIP_FACTORY;
virtual QgsAbstractGeometry *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const = 0 SIP_FACTORY;

/**
* Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a
Expand Down
2 changes: 1 addition & 1 deletion src/core/geometry/qgscircularstring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ QgsLineString *QgsCircularString::curveToLine( double tolerance, SegmentationTol
return line;
}

QgsCircularString *QgsCircularString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
QgsCircularString *QgsCircularString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool ) const
{
// prepare result
std::unique_ptr<QgsCircularString> result { createEmptyWithSameType() };
Expand Down
2 changes: 1 addition & 1 deletion src/core/geometry/qgscircularstring.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ class CORE_EXPORT QgsCircularString: public QgsCurve
QgsPoint startPoint() const override SIP_HOLDGIL;
QgsPoint endPoint() const override SIP_HOLDGIL;
QgsLineString *curveToLine( double tolerance = M_PI_2 / 90, SegmentationToleranceType toleranceType = MaximumAngle ) const override SIP_FACTORY;
QgsCircularString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const override SIP_FACTORY;
QgsCircularString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const override SIP_FACTORY;
bool removeDuplicateNodes( double epsilon = 4 * std::numeric_limits<double>::epsilon(), bool useZValues = false ) override;

void draw( QPainter &p ) const override;
Expand Down
4 changes: 2 additions & 2 deletions src/core/geometry/qgscompoundcurve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -465,13 +465,13 @@ QgsLineString *QgsCompoundCurve::curveToLine( double tolerance, SegmentationTole
return line;
}

QgsCompoundCurve *QgsCompoundCurve::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
QgsCompoundCurve *QgsCompoundCurve::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
{
std::unique_ptr<QgsCompoundCurve> result( createEmptyWithSameType() );

for ( QgsCurve *curve : mCurves )
{
std::unique_ptr<QgsCurve> gridified( static_cast< QgsCurve * >( curve->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) ) );
std::unique_ptr<QgsCurve> gridified( static_cast< QgsCurve * >( curve->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) ) );
if ( gridified )
{
result->mCurves.append( gridified.release() );
Expand Down
2 changes: 1 addition & 1 deletion src/core/geometry/qgscompoundcurve.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class CORE_EXPORT QgsCompoundCurve: public QgsCurve
*/
QgsLineString *curveToLine( double tolerance = M_PI_2 / 90, SegmentationToleranceType toleranceType = MaximumAngle ) const override SIP_FACTORY;

QgsCompoundCurve *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const override SIP_FACTORY;
QgsCompoundCurve *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const override SIP_FACTORY;
bool removeDuplicateNodes( double epsilon = 4 * std::numeric_limits<double>::epsilon(), bool useZValues = false ) override;
bool boundingBoxIntersects( const QgsBox3D &box3d ) const override SIP_HOLDGIL;
const QgsAbstractGeometry *simplifiedTypeRef() const override SIP_HOLDGIL;
Expand Down
6 changes: 3 additions & 3 deletions src/core/geometry/qgscurvepolygon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ QgsAbstractGeometry *QgsCurvePolygon::boundary() const
}
}

QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
{
if ( !mExteriorRing )
return nullptr;
Expand All @@ -560,7 +560,7 @@ QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacin
std::unique_ptr< QgsCurvePolygon > polygon( createEmptyWithSameType() );

// exterior ring
auto exterior = std::unique_ptr<QgsCurve> { static_cast< QgsCurve *>( mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) ) };
auto exterior = std::unique_ptr<QgsCurve> { static_cast< QgsCurve *>( mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) ) };

if ( !exterior )
return nullptr;
Expand All @@ -573,7 +573,7 @@ QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacin
if ( !interior )
continue;

QgsCurve *gridifiedInterior = static_cast< QgsCurve * >( interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) );
QgsCurve *gridifiedInterior = static_cast< QgsCurve * >( interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) );

if ( !gridifiedInterior )
continue;
Expand Down
2 changes: 1 addition & 1 deletion src/core/geometry/qgscurvepolygon.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
double perimeter() const override SIP_HOLDGIL;
QgsPolygon *surfaceToPolygon() const override SIP_FACTORY;
QgsAbstractGeometry *boundary() const override SIP_FACTORY;
QgsCurvePolygon *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const override SIP_FACTORY;
QgsCurvePolygon *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const override SIP_FACTORY;
bool removeDuplicateNodes( double epsilon = 4 * std::numeric_limits<double>::epsilon(), bool useZValues = false ) override;
bool boundingBoxIntersects( const QgsBox3D &box3d ) const override SIP_HOLDGIL;

Expand Down
4 changes: 2 additions & 2 deletions src/core/geometry/qgsgeometrycollection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,13 @@ void QgsGeometryCollection::clear()
clearCache(); //set bounding box invalid
}

QgsGeometryCollection *QgsGeometryCollection::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
QgsGeometryCollection *QgsGeometryCollection::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
{
std::unique_ptr<QgsGeometryCollection> result;

for ( auto geom : mGeometries )
{
std::unique_ptr<QgsAbstractGeometry> gridified { geom->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing ) };
std::unique_ptr<QgsAbstractGeometry> gridified { geom->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, removeRedundantPoints ) };
if ( gridified )
{
if ( !result )
Expand Down
2 changes: 1 addition & 1 deletion src/core/geometry/qgsgeometrycollection.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
int dimension() const override SIP_HOLDGIL;
QString geometryType() const override SIP_HOLDGIL;
void clear() override;
QgsGeometryCollection *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0 ) const override SIP_FACTORY;
QgsGeometryCollection *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const override SIP_FACTORY;
bool removeDuplicateNodes( double epsilon = 4 * std::numeric_limits<double>::epsilon(), bool useZValues = false ) override;
QgsAbstractGeometry *boundary() const override SIP_FACTORY;
void adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex SIP_OUT, QgsVertexId &nextVertex SIP_OUT ) const override;
Expand Down
48 changes: 39 additions & 9 deletions src/core/geometry/qgslinestring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ bool QgsLineString::isValid( QString &error, Qgis::GeometryValidityFlags flags )
return QgsCurve::isValid( error, flags );
}

QgsLineString *QgsLineString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing ) const
QgsLineString *QgsLineString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing, bool removeRedundantPoints ) const
{
const int length = numPoints();
if ( length < 2 )
Expand Down Expand Up @@ -355,6 +355,7 @@ QgsLineString *QgsLineString::snappedToGrid( double hSpacing, double vSpacing, d
double previousY = 0;
double previousZ = 0;
double previousM = 0;
int outSize = 0;
for ( int i = 0; i < length; ++i )
{
const double currentX = *xIn++;
Expand All @@ -375,22 +376,51 @@ QgsLineString *QgsLineString::snappedToGrid( double hSpacing, double vSpacing, d
&& ( !hasM || mSpacing <= 0 || qgsDoubleNear( roundedM, previousM ) );
if ( isPointEqual )
continue;
}

outX.append( roundedX );
outY.append( roundedY );
if ( hasZ )
outZ.append( roundedZ );
if ( hasM )
outM.append( roundedM );
// maybe previous point is redundant and is just a midpoint on a straight line -- let's check
bool previousPointRedundant = false;
if ( outSize > 1 && !hasZ && !hasM )
{
previousPointRedundant = QgsGeometryUtilsBase::leftOfLine( outX.at( outSize - 1 ),
outY.at( outSize - 1 ),
outX.at( outSize - 2 ),
outY.at( outSize - 2 ),
roundedX, roundedY ) == 0;
}
if ( previousPointRedundant )
{
outX[ outSize - 1 ] = roundedX;
outY[ outSize - 1 ] = roundedY;
}
else
{
outX.append( roundedX );
outY.append( roundedY );
if ( hasZ )
outZ.append( roundedZ );
if ( hasM )
outM.append( roundedM );
outSize++;
}
}
else
{
outX.append( roundedX );
outY.append( roundedY );
if ( hasZ )
outZ.append( roundedZ );
if ( hasM )
outM.append( roundedM );
outSize++;
}

previousX = roundedX;
previousY = roundedY;
previousZ = roundedZ;
previousM = roundedM;
}

if ( outX.length() < 2 || ( isClosed() && outX.length() < 4 ) )
if ( outSize < 2 || ( isClosed() && outSize < 4 ) )
return nullptr;

return new QgsLineString( outX, outY, outZ, outM );
Expand Down
Loading

0 comments on commit 57965ee

Please sign in to comment.