mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-15 00:07:25 -05:00
Port geos simplify linestring
Add QgsAbstractGeometry::simplifyByDistance, which is a direct port of GEOS Douglas Peucker algorithm. This is a trivial algorithm to implement, and we benefit from avoiding the conversion to/from GEOS geometries.
This commit is contained in:
parent
e3d36d9966
commit
102874e6d2
@ -627,6 +627,20 @@ In this case, it can be thought like rounding the x and y of all the points/vert
|
||||
: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 QgsAbstractGeometry *simplifyByDistance( double tolerance ) const = 0 /Factory/;
|
||||
%Docstring
|
||||
Simplifies the geometry by applying the Douglas Peucker simplification by distance
|
||||
algorithm.
|
||||
|
||||
The caller takes ownership of the returned geometry. Curved geometries will be segmentized prior to simplification.
|
||||
|
||||
If a simplified geometry cannot be calculated ``None`` will be returned.
|
||||
|
||||
The returned geometry may be invalid and contain self-intersecting rings.
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
|
||||
virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false ) = 0;
|
||||
|
||||
@ -153,6 +153,8 @@ Appends the contents of another circular ``string`` to the end of this circular
|
||||
|
||||
virtual QgsCircularString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;
|
||||
|
||||
virtual QgsAbstractGeometry *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );
|
||||
|
||||
|
||||
|
||||
@ -83,6 +83,8 @@ of the curve.
|
||||
|
||||
virtual QgsCompoundCurve *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;
|
||||
|
||||
virtual QgsAbstractGeometry *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );
|
||||
|
||||
virtual bool boundingBoxIntersects( const QgsBox3D &box3d ) const /HoldGIL/;
|
||||
|
||||
@ -70,6 +70,8 @@ Curve polygon geometry type
|
||||
|
||||
virtual QgsCurvePolygon *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;
|
||||
|
||||
virtual QgsCurvePolygon *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );
|
||||
|
||||
virtual bool boundingBoxIntersects( const QgsBox3D &box3d ) const /HoldGIL/;
|
||||
|
||||
@ -267,6 +267,7 @@ Returns a geometry without curves. Caller takes ownership
|
||||
|
||||
virtual const QgsAbstractGeometry *simplifiedTypeRef() const /HoldGIL/;
|
||||
|
||||
virtual QgsGeometryCollection *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
virtual bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = 0 );
|
||||
|
||||
|
||||
@ -619,6 +619,8 @@ If ``useZValues`` is ``True`` then z values will also be considered when testing
|
||||
virtual QPolygonF asQPolygonF() const;
|
||||
|
||||
|
||||
virtual QgsLineString *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
virtual bool fromWkb( QgsConstWkbPtr &wkb );
|
||||
|
||||
virtual bool fromWkt( const QString &wkt );
|
||||
|
||||
@ -63,6 +63,8 @@ Returns the curve with the specified ``index``.
|
||||
|
||||
virtual bool insertGeometry( QgsAbstractGeometry *g /Transfer/, int index );
|
||||
|
||||
virtual QgsMultiCurve *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
|
||||
QgsMultiCurve *reversed() const /Factory/;
|
||||
%Docstring
|
||||
|
||||
@ -82,6 +82,8 @@ Returns the line string with the specified ``index``.
|
||||
bool addGeometries( const QVector< QgsAbstractGeometry * > &geometries /Transfer/ ) final;
|
||||
virtual bool insertGeometry( QgsAbstractGeometry *g /Transfer/, int index );
|
||||
|
||||
virtual QgsMultiLineString *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
|
||||
virtual QgsMultiCurve *toCurveType() const /Factory/;
|
||||
|
||||
|
||||
@ -294,6 +294,8 @@ Returns the point with the specified ``index``.
|
||||
|
||||
virtual bool isValid( QString &error /Out/, Qgis::GeometryValidityFlags flags = Qgis::GeometryValidityFlags() ) const /HoldGIL/;
|
||||
|
||||
virtual QgsMultiPoint *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
|
||||
|
||||
virtual QgsMultiPoint *createEmptyWithSameType() const /Factory/;
|
||||
|
||||
@ -82,6 +82,8 @@ Returns the polygon with the specified ``index``.
|
||||
bool addGeometries( const QVector< QgsAbstractGeometry * > &geometries /Transfer/ ) final;
|
||||
virtual bool insertGeometry( QgsAbstractGeometry *g /Transfer/, int index );
|
||||
|
||||
virtual QgsMultiPolygon *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
|
||||
virtual QgsMultiSurface *toCurveType() const /Factory/;
|
||||
|
||||
|
||||
@ -70,6 +70,8 @@ Returns the surface with the specified ``index``.
|
||||
|
||||
virtual QgsAbstractGeometry *boundary() const /Factory/;
|
||||
|
||||
virtual QgsMultiSurface *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
|
||||
|
||||
virtual QgsMultiSurface *createEmptyWithSameType() const /Factory/;
|
||||
|
||||
@ -357,6 +357,8 @@ Example
|
||||
|
||||
virtual QgsPoint *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;
|
||||
|
||||
virtual QgsPoint *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );
|
||||
|
||||
virtual void clear();
|
||||
|
||||
@ -627,6 +627,20 @@ In this case, it can be thought like rounding the x and y of all the points/vert
|
||||
: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 QgsAbstractGeometry *simplifyByDistance( double tolerance ) const = 0 /Factory/;
|
||||
%Docstring
|
||||
Simplifies the geometry by applying the Douglas Peucker simplification by distance
|
||||
algorithm.
|
||||
|
||||
The caller takes ownership of the returned geometry. Curved geometries will be segmentized prior to simplification.
|
||||
|
||||
If a simplified geometry cannot be calculated ``None`` will be returned.
|
||||
|
||||
The returned geometry may be invalid and contain self-intersecting rings.
|
||||
|
||||
.. versionadded:: 3.38
|
||||
%End
|
||||
|
||||
virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false ) = 0;
|
||||
|
||||
@ -153,6 +153,8 @@ Appends the contents of another circular ``string`` to the end of this circular
|
||||
|
||||
virtual QgsCircularString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;
|
||||
|
||||
virtual QgsAbstractGeometry *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );
|
||||
|
||||
|
||||
|
||||
@ -83,6 +83,8 @@ of the curve.
|
||||
|
||||
virtual QgsCompoundCurve *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;
|
||||
|
||||
virtual QgsAbstractGeometry *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );
|
||||
|
||||
virtual bool boundingBoxIntersects( const QgsBox3D &box3d ) const /HoldGIL/;
|
||||
|
||||
@ -70,6 +70,8 @@ Curve polygon geometry type
|
||||
|
||||
virtual QgsCurvePolygon *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;
|
||||
|
||||
virtual QgsCurvePolygon *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );
|
||||
|
||||
virtual bool boundingBoxIntersects( const QgsBox3D &box3d ) const /HoldGIL/;
|
||||
|
||||
@ -267,6 +267,7 @@ Returns a geometry without curves. Caller takes ownership
|
||||
|
||||
virtual const QgsAbstractGeometry *simplifiedTypeRef() const /HoldGIL/;
|
||||
|
||||
virtual QgsGeometryCollection *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
virtual bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = 0 );
|
||||
|
||||
|
||||
@ -619,6 +619,8 @@ If ``useZValues`` is ``True`` then z values will also be considered when testing
|
||||
virtual QPolygonF asQPolygonF() const;
|
||||
|
||||
|
||||
virtual QgsLineString *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
virtual bool fromWkb( QgsConstWkbPtr &wkb );
|
||||
|
||||
virtual bool fromWkt( const QString &wkt );
|
||||
|
||||
@ -63,6 +63,8 @@ Returns the curve with the specified ``index``.
|
||||
|
||||
virtual bool insertGeometry( QgsAbstractGeometry *g /Transfer/, int index );
|
||||
|
||||
virtual QgsMultiCurve *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
|
||||
QgsMultiCurve *reversed() const /Factory/;
|
||||
%Docstring
|
||||
|
||||
@ -82,6 +82,8 @@ Returns the line string with the specified ``index``.
|
||||
bool addGeometries( const QVector< QgsAbstractGeometry * > &geometries /Transfer/ ) final;
|
||||
virtual bool insertGeometry( QgsAbstractGeometry *g /Transfer/, int index );
|
||||
|
||||
virtual QgsMultiLineString *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
|
||||
virtual QgsMultiCurve *toCurveType() const /Factory/;
|
||||
|
||||
|
||||
@ -294,6 +294,8 @@ Returns the point with the specified ``index``.
|
||||
|
||||
virtual bool isValid( QString &error /Out/, Qgis::GeometryValidityFlags flags = Qgis::GeometryValidityFlags() ) const /HoldGIL/;
|
||||
|
||||
virtual QgsMultiPoint *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
|
||||
|
||||
virtual QgsMultiPoint *createEmptyWithSameType() const /Factory/;
|
||||
|
||||
@ -82,6 +82,8 @@ Returns the polygon with the specified ``index``.
|
||||
bool addGeometries( const QVector< QgsAbstractGeometry * > &geometries /Transfer/ ) final;
|
||||
virtual bool insertGeometry( QgsAbstractGeometry *g /Transfer/, int index );
|
||||
|
||||
virtual QgsMultiPolygon *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
|
||||
virtual QgsMultiSurface *toCurveType() const /Factory/;
|
||||
|
||||
|
||||
@ -70,6 +70,8 @@ Returns the surface with the specified ``index``.
|
||||
|
||||
virtual QgsAbstractGeometry *boundary() const /Factory/;
|
||||
|
||||
virtual QgsMultiSurface *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
|
||||
|
||||
virtual QgsMultiSurface *createEmptyWithSameType() const /Factory/;
|
||||
|
||||
@ -357,6 +357,8 @@ Example
|
||||
|
||||
virtual QgsPoint *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const /Factory/;
|
||||
|
||||
virtual QgsPoint *simplifyByDistance( double tolerance ) const /Factory/;
|
||||
|
||||
virtual bool removeDuplicateNodes( double epsilon = 4 * DBL_EPSILON, bool useZValues = false );
|
||||
|
||||
virtual void clear();
|
||||
|
||||
@ -648,6 +648,20 @@ class CORE_EXPORT QgsAbstractGeometry
|
||||
*/
|
||||
virtual QgsAbstractGeometry *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const = 0 SIP_FACTORY;
|
||||
|
||||
/**
|
||||
* Simplifies the geometry by applying the Douglas Peucker simplification by distance
|
||||
* algorithm.
|
||||
*
|
||||
* The caller takes ownership of the returned geometry. Curved geometries will be segmentized prior to simplification.
|
||||
*
|
||||
* If a simplified geometry cannot be calculated NULLPTR will be returned.
|
||||
*
|
||||
* The returned geometry may be invalid and contain self-intersecting rings.
|
||||
*
|
||||
* \since QGIS 3.38
|
||||
*/
|
||||
virtual QgsAbstractGeometry *simplifyByDistance( double tolerance ) const = 0 SIP_FACTORY;
|
||||
|
||||
/**
|
||||
* Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a
|
||||
* degenerate geometry.
|
||||
|
||||
@ -638,6 +638,12 @@ QgsCircularString *QgsCircularString::snappedToGrid( double hSpacing, double vSp
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QgsAbstractGeometry *QgsCircularString::simplifyByDistance( double tolerance ) const
|
||||
{
|
||||
std::unique_ptr< QgsLineString > line( curveToLine() );
|
||||
return line->simplifyByDistance( tolerance );
|
||||
}
|
||||
|
||||
bool QgsCircularString::removeDuplicateNodes( double epsilon, bool useZValues )
|
||||
{
|
||||
if ( mX.count() <= 3 )
|
||||
|
||||
@ -260,6 +260,7 @@ class CORE_EXPORT QgsCircularString: public QgsCurve
|
||||
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, bool removeRedundantPoints = false ) const override SIP_FACTORY;
|
||||
QgsAbstractGeometry *simplifyByDistance( double tolerance ) const override SIP_FACTORY;
|
||||
bool removeDuplicateNodes( double epsilon = 4 * std::numeric_limits<double>::epsilon(), bool useZValues = false ) override;
|
||||
|
||||
void draw( QPainter &p ) const override;
|
||||
|
||||
@ -484,6 +484,12 @@ QgsCompoundCurve *QgsCompoundCurve::snappedToGrid( double hSpacing, double vSpac
|
||||
return result.release();
|
||||
}
|
||||
|
||||
QgsAbstractGeometry *QgsCompoundCurve::simplifyByDistance( double tolerance ) const
|
||||
{
|
||||
std::unique_ptr< QgsLineString > line( curveToLine() );
|
||||
return line->simplifyByDistance( tolerance );
|
||||
}
|
||||
|
||||
bool QgsCompoundCurve::removeDuplicateNodes( double epsilon, bool useZValues )
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
@ -122,6 +122,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, bool removeRedundantPoints = false ) const override SIP_FACTORY;
|
||||
QgsAbstractGeometry *simplifyByDistance( double tolerance ) 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;
|
||||
|
||||
@ -585,6 +585,37 @@ QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacin
|
||||
|
||||
}
|
||||
|
||||
QgsCurvePolygon *QgsCurvePolygon::simplifyByDistance( double tolerance ) const
|
||||
{
|
||||
if ( !mExteriorRing )
|
||||
return nullptr;
|
||||
|
||||
// exterior ring
|
||||
std::unique_ptr< QgsAbstractGeometry > exterior( mExteriorRing->simplifyByDistance( tolerance ) );
|
||||
if ( !qgsgeometry_cast< QgsLineString * >( exterior.get() ) )
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >( qgis::down_cast< QgsLineString * >( exterior.release() ) );
|
||||
|
||||
//interior rings
|
||||
for ( const QgsCurve *interior : mInteriorRings )
|
||||
{
|
||||
if ( !interior )
|
||||
continue;
|
||||
|
||||
std::unique_ptr< QgsAbstractGeometry > simplifiedRing( interior->simplifyByDistance( tolerance ) );
|
||||
if ( !simplifiedRing )
|
||||
return nullptr;
|
||||
|
||||
if ( !qgsgeometry_cast< QgsLineString * >( simplifiedRing.get() ) )
|
||||
return nullptr;
|
||||
|
||||
polygon->mInteriorRings.append( qgis::down_cast< QgsLineString * >( simplifiedRing.release() ) );
|
||||
}
|
||||
|
||||
return polygon.release();
|
||||
}
|
||||
|
||||
bool QgsCurvePolygon::removeDuplicateNodes( double epsilon, bool useZValues )
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
@ -138,6 +138,7 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
|
||||
QgsPolygon *surfaceToPolygon() const override SIP_FACTORY;
|
||||
QgsAbstractGeometry *boundary() const override SIP_FACTORY;
|
||||
QgsCurvePolygon *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const override SIP_FACTORY;
|
||||
QgsCurvePolygon *simplifyByDistance( double tolerance ) 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;
|
||||
|
||||
|
||||
@ -1074,6 +1074,17 @@ const QgsAbstractGeometry *QgsGeometryCollection::simplifiedTypeRef() const
|
||||
return this;
|
||||
}
|
||||
|
||||
QgsGeometryCollection *QgsGeometryCollection::simplifyByDistance( double tolerance ) const
|
||||
{
|
||||
std::unique_ptr< QgsGeometryCollection > res = std::make_unique< QgsGeometryCollection >();
|
||||
res->reserve( mGeometries.size() );
|
||||
for ( int i = 0; i < mGeometries.size(); ++i )
|
||||
{
|
||||
res->addGeometry( mGeometries.at( i )->simplifyByDistance( tolerance ) );
|
||||
}
|
||||
return res.release();
|
||||
}
|
||||
|
||||
bool QgsGeometryCollection::transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback )
|
||||
{
|
||||
if ( !transformer )
|
||||
|
||||
@ -318,6 +318,7 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
|
||||
void swapXy() override;
|
||||
QgsGeometryCollection *toCurveType() const override SIP_FACTORY;
|
||||
const QgsAbstractGeometry *simplifiedTypeRef() const override SIP_HOLDGIL;
|
||||
virtual QgsGeometryCollection *simplifyByDistance( double tolerance ) const override SIP_FACTORY;
|
||||
|
||||
bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = nullptr ) override;
|
||||
|
||||
|
||||
@ -620,6 +620,125 @@ QPolygonF QgsLineString::asQPolygonF() const
|
||||
return points;
|
||||
}
|
||||
|
||||
|
||||
void simplifySection( int i, int j, const double *x, const double *y, std::vector< bool > &usePoint, const double distanceToleranceSquared, const double epsilon )
|
||||
{
|
||||
if ( i + 1 == j )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double maxDistanceSquared = -1.0;
|
||||
|
||||
int maxIndex = i;
|
||||
double mx, my;
|
||||
|
||||
for ( int k = i + 1; k < j; k++ )
|
||||
{
|
||||
const double distanceSquared = QgsGeometryUtilsBase::sqrDistToLine(
|
||||
x[k], y[k], x[i], y[i], x[j], y[j], mx, my, epsilon );
|
||||
|
||||
if ( distanceSquared > maxDistanceSquared )
|
||||
{
|
||||
maxDistanceSquared = distanceSquared;
|
||||
maxIndex = k;
|
||||
}
|
||||
}
|
||||
if ( maxDistanceSquared <= distanceToleranceSquared )
|
||||
{
|
||||
for ( int k = i + 1; k < j; k++ )
|
||||
{
|
||||
usePoint[k] = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
simplifySection( i, maxIndex, x, y, usePoint, distanceToleranceSquared, epsilon );
|
||||
simplifySection( maxIndex, j, x, y, usePoint, distanceToleranceSquared, epsilon );
|
||||
}
|
||||
};
|
||||
|
||||
QgsLineString *QgsLineString::simplifyByDistance( double tolerance ) const
|
||||
{
|
||||
if ( mX.empty() )
|
||||
{
|
||||
return new QgsLineString();
|
||||
}
|
||||
|
||||
// ported from GEOS DouglasPeuckerLineSimplifier::simplify
|
||||
|
||||
const double distanceToleranceSquared = tolerance * tolerance;
|
||||
const double *xData = mX.constData();
|
||||
const double *yData = mY.constData();
|
||||
const double *zData = mZ.constData();
|
||||
const double *mData = mM.constData();
|
||||
|
||||
const int size = mX.size();
|
||||
|
||||
std::vector< bool > usePoint( size, true );
|
||||
|
||||
constexpr double epsilon = 4 * std::numeric_limits<double>::epsilon();
|
||||
simplifySection( 0, size - 1, xData, yData, usePoint, distanceToleranceSquared, epsilon );
|
||||
|
||||
QVector< double > newX;
|
||||
newX.reserve( size );
|
||||
QVector< double > newY;
|
||||
newY.reserve( size );
|
||||
|
||||
const bool hasZ = is3D();
|
||||
const bool hasM = isMeasure();
|
||||
QVector< double > newZ;
|
||||
if ( hasZ )
|
||||
newZ.reserve( size );
|
||||
QVector< double > newM;
|
||||
if ( hasM )
|
||||
newM.reserve( size );
|
||||
|
||||
for ( int i = 0, n = size; i < n; ++i )
|
||||
{
|
||||
if ( usePoint[i] || i == n - 1 )
|
||||
{
|
||||
newX.append( xData[i ] );
|
||||
newY.append( yData[i ] );
|
||||
if ( hasZ )
|
||||
newZ.append( zData[i] );
|
||||
if ( hasM )
|
||||
newM.append( mData[i] );
|
||||
}
|
||||
}
|
||||
|
||||
const bool simplifyRing = isRing();
|
||||
const int newSize = newX.size();
|
||||
if ( simplifyRing && newSize > 3 )
|
||||
{
|
||||
double mx, my;
|
||||
const double distanceSquared = QgsGeometryUtilsBase::sqrDistToLine(
|
||||
newX[0], newY[ 0],
|
||||
newX[ newSize - 2], newY[ newSize - 2 ],
|
||||
newX[ 1 ], newY[ 1], mx, my, epsilon );
|
||||
|
||||
if ( distanceSquared <= distanceToleranceSquared )
|
||||
{
|
||||
newX.removeFirst();
|
||||
newX.last() = newX.first();
|
||||
newY.removeFirst();
|
||||
newY.last() = newY.first();
|
||||
if ( hasZ )
|
||||
{
|
||||
newZ.removeFirst();
|
||||
newZ.last() = newZ.first();
|
||||
}
|
||||
if ( hasM )
|
||||
{
|
||||
newM.removeFirst();
|
||||
newM.last() = newM.first();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new QgsLineString( newX, newY, newZ, newM );
|
||||
}
|
||||
|
||||
bool QgsLineString::fromWkb( QgsConstWkbPtr &wkbPtr )
|
||||
{
|
||||
if ( !wkbPtr )
|
||||
|
||||
@ -972,6 +972,7 @@ class CORE_EXPORT QgsLineString: public QgsCurve
|
||||
|
||||
QPolygonF asQPolygonF() const override;
|
||||
|
||||
QgsLineString *simplifyByDistance( double tolerance ) const override SIP_FACTORY;
|
||||
bool fromWkb( QgsConstWkbPtr &wkb ) override;
|
||||
bool fromWkt( const QString &wkt ) override;
|
||||
|
||||
|
||||
@ -209,6 +209,17 @@ bool QgsMultiCurve::insertGeometry( QgsAbstractGeometry *g, int index )
|
||||
return QgsGeometryCollection::insertGeometry( g, index );
|
||||
}
|
||||
|
||||
QgsMultiCurve *QgsMultiCurve::simplifyByDistance( double tolerance ) const
|
||||
{
|
||||
std::unique_ptr< QgsMultiCurve > res = std::make_unique< QgsMultiCurve >();
|
||||
res->reserve( mGeometries.size() );
|
||||
for ( int i = 0; i < mGeometries.size(); ++i )
|
||||
{
|
||||
res->addGeometry( mGeometries.at( i )->simplifyByDistance( tolerance ) );
|
||||
}
|
||||
return res.release();
|
||||
}
|
||||
|
||||
QgsMultiCurve *QgsMultiCurve::reversed() const
|
||||
{
|
||||
QgsMultiCurve *reversedMultiCurve = new QgsMultiCurve();
|
||||
|
||||
@ -85,6 +85,7 @@ class CORE_EXPORT QgsMultiCurve: public QgsGeometryCollection
|
||||
bool addGeometry( QgsAbstractGeometry *g SIP_TRANSFER ) override;
|
||||
bool addGeometries( const QVector< QgsAbstractGeometry * > &geometries SIP_TRANSFER ) override;
|
||||
bool insertGeometry( QgsAbstractGeometry *g SIP_TRANSFER, int index ) override;
|
||||
QgsMultiCurve *simplifyByDistance( double tolerance ) const override SIP_FACTORY;
|
||||
|
||||
/**
|
||||
* Returns a copy of the multi curve, where each component curve has had its line direction reversed.
|
||||
|
||||
@ -224,6 +224,17 @@ bool QgsMultiLineString::insertGeometry( QgsAbstractGeometry *g, int index )
|
||||
return QgsMultiCurve::insertGeometry( g, index );
|
||||
}
|
||||
|
||||
QgsMultiLineString *QgsMultiLineString::simplifyByDistance( double tolerance ) const
|
||||
{
|
||||
std::unique_ptr< QgsMultiLineString > result = std::make_unique< QgsMultiLineString >();
|
||||
result->reserve( mGeometries.size() );
|
||||
for ( int i = 0; i < mGeometries.size(); ++i )
|
||||
{
|
||||
result->addGeometry( mGeometries.at( i )->simplifyByDistance( tolerance ) );
|
||||
}
|
||||
return result.release();
|
||||
}
|
||||
|
||||
QgsMultiCurve *QgsMultiLineString::toCurveType() const
|
||||
{
|
||||
QgsMultiCurve *multiCurve = new QgsMultiCurve();
|
||||
|
||||
@ -107,6 +107,7 @@ class CORE_EXPORT QgsMultiLineString: public QgsMultiCurve
|
||||
bool addGeometry( QgsAbstractGeometry *g SIP_TRANSFER ) override;
|
||||
bool addGeometries( const QVector< QgsAbstractGeometry * > &geometries SIP_TRANSFER ) final;
|
||||
bool insertGeometry( QgsAbstractGeometry *g SIP_TRANSFER, int index ) override;
|
||||
QgsMultiLineString *simplifyByDistance( double tolerance ) const override SIP_FACTORY;
|
||||
|
||||
/**
|
||||
* Returns the geometry converted to the more generic curve type QgsMultiCurve
|
||||
|
||||
@ -315,6 +315,11 @@ bool QgsMultiPoint::isValid( QString &, Qgis::GeometryValidityFlags ) const
|
||||
return true;
|
||||
}
|
||||
|
||||
QgsMultiPoint *QgsMultiPoint::simplifyByDistance( double ) const
|
||||
{
|
||||
return clone();
|
||||
}
|
||||
|
||||
void QgsMultiPoint::filterVertices( const std::function<bool ( const QgsPoint & )> &filter )
|
||||
{
|
||||
mGeometries.erase( std::remove_if( mGeometries.begin(), mGeometries.end(), // clazy:exclude=detaching-member
|
||||
|
||||
@ -343,6 +343,7 @@ class CORE_EXPORT QgsMultiPoint: public QgsGeometryCollection
|
||||
int vertexNumberFromVertexId( QgsVertexId id ) const override;
|
||||
double segmentLength( QgsVertexId startVertex ) const override;
|
||||
bool isValid( QString &error SIP_OUT, Qgis::GeometryValidityFlags flags = Qgis::GeometryValidityFlags() ) const override SIP_HOLDGIL;
|
||||
QgsMultiPoint *simplifyByDistance( double tolerance ) const override SIP_FACTORY;
|
||||
|
||||
#ifndef SIP_RUN
|
||||
void filterVertices( const std::function< bool( const QgsPoint & ) > &filter ) override;
|
||||
|
||||
@ -236,6 +236,17 @@ bool QgsMultiPolygon::insertGeometry( QgsAbstractGeometry *g, int index )
|
||||
return QgsMultiSurface::insertGeometry( g, index );
|
||||
}
|
||||
|
||||
QgsMultiPolygon *QgsMultiPolygon::simplifyByDistance( double tolerance ) const
|
||||
{
|
||||
std::unique_ptr< QgsMultiPolygon > res = std::make_unique< QgsMultiPolygon >();
|
||||
res->reserve( mGeometries.size() );
|
||||
for ( int i = 0; i < mGeometries.size(); ++i )
|
||||
{
|
||||
res->addGeometry( mGeometries.at( i )->simplifyByDistance( tolerance ) );
|
||||
}
|
||||
return res.release();
|
||||
}
|
||||
|
||||
QgsMultiSurface *QgsMultiPolygon::toCurveType() const
|
||||
{
|
||||
QgsMultiSurface *multiSurface = new QgsMultiSurface();
|
||||
|
||||
@ -107,6 +107,7 @@ class CORE_EXPORT QgsMultiPolygon: public QgsMultiSurface
|
||||
bool addGeometry( QgsAbstractGeometry *g SIP_TRANSFER ) override;
|
||||
bool addGeometries( const QVector< QgsAbstractGeometry * > &geometries SIP_TRANSFER ) final;
|
||||
bool insertGeometry( QgsAbstractGeometry *g SIP_TRANSFER, int index ) override;
|
||||
QgsMultiPolygon *simplifyByDistance( double tolerance ) const override SIP_FACTORY;
|
||||
|
||||
/**
|
||||
* Returns the geometry converted to the more generic curve type QgsMultiSurface
|
||||
|
||||
@ -236,3 +236,14 @@ QgsAbstractGeometry *QgsMultiSurface::boundary() const
|
||||
}
|
||||
return multiCurve.release();
|
||||
}
|
||||
|
||||
QgsMultiSurface *QgsMultiSurface::simplifyByDistance( double tolerance ) const
|
||||
{
|
||||
std::unique_ptr< QgsMultiSurface > res = std::make_unique< QgsMultiSurface >();
|
||||
res->reserve( mGeometries.size() );
|
||||
for ( int i = 0; i < mGeometries.size(); ++i )
|
||||
{
|
||||
res->addGeometry( mGeometries.at( i )->simplifyByDistance( tolerance ) );
|
||||
}
|
||||
return res.release();
|
||||
}
|
||||
|
||||
@ -92,6 +92,7 @@ class CORE_EXPORT QgsMultiSurface: public QgsGeometryCollection
|
||||
bool addGeometries( const QVector< QgsAbstractGeometry * > &geometries SIP_TRANSFER ) override;
|
||||
bool insertGeometry( QgsAbstractGeometry *g SIP_TRANSFER, int index ) override;
|
||||
QgsAbstractGeometry *boundary() const override SIP_FACTORY;
|
||||
QgsMultiSurface *simplifyByDistance( double tolerance ) const override SIP_FACTORY;
|
||||
|
||||
#ifndef SIP_RUN
|
||||
|
||||
|
||||
@ -20,7 +20,6 @@
|
||||
#include "qgsapplication.h"
|
||||
#include "qgscoordinatetransform.h"
|
||||
#include "qgsgeometryutils.h"
|
||||
#include "qgsmaptopixel.h"
|
||||
#include "qgswkbptr.h"
|
||||
#include "qgsgeometrytransformer.h"
|
||||
#include "qgsbox3d.h"
|
||||
@ -128,6 +127,11 @@ QgsPoint *QgsPoint::snappedToGrid( double hSpacing, double vSpacing, double dSpa
|
||||
return new QgsPoint( mWkbType, x, y, z, m );
|
||||
}
|
||||
|
||||
QgsPoint *QgsPoint::simplifyByDistance( double ) const
|
||||
{
|
||||
return clone();
|
||||
}
|
||||
|
||||
bool QgsPoint::removeDuplicateNodes( double, bool )
|
||||
{
|
||||
return false;
|
||||
|
||||
@ -563,6 +563,7 @@ class CORE_EXPORT QgsPoint: public QgsAbstractGeometry
|
||||
int dimension() const override SIP_HOLDGIL;
|
||||
QgsPoint *clone() const override SIP_FACTORY;
|
||||
QgsPoint *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, bool removeRedundantPoints = false ) const override SIP_FACTORY;
|
||||
QgsPoint *simplifyByDistance( double tolerance ) const override SIP_FACTORY;
|
||||
bool removeDuplicateNodes( double epsilon = 4 * std::numeric_limits<double>::epsilon(), bool useZValues = false ) override;
|
||||
void clear() override;
|
||||
bool fromWkb( QgsConstWkbPtr &wkb ) override;
|
||||
|
||||
@ -89,6 +89,16 @@ class TestQgsCircularString(QgisTestCase):
|
||||
self.assertTrue(geom1.fuzzyEqual(geom2, epsilon))
|
||||
self.assertTrue(geom1.fuzzyDistanceEqual(geom2, epsilon))
|
||||
|
||||
def test_simplify_by_distance(self):
|
||||
"""
|
||||
test simplifyByDistance
|
||||
"""
|
||||
p = QgsCircularString()
|
||||
p.fromWkt('CircularString (0.58883883211678167 0.93610259854013833, 8.76977664233575993 27.04692910948904228, 31.60822802919707541 31.61461938686130324)')
|
||||
self.assertEqual(p.simplifyByDistance(0.5).asWkt(3), 'LineString (0.589 0.936, -0.368 7.336, 0.467 14.185, 2.932 20.168, 6.857 25.312, 11.976 29.27, 18.362 31.883, 24.787 32.651, 31.608 31.615)')
|
||||
self.assertEqual(p.simplifyByDistance(1).asWkt(3),
|
||||
'LineString (0.589 0.936, 0.467 14.185, 6.857 25.312, 18.362 31.883, 31.608 31.615)')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@ -234,6 +234,18 @@ class TestQgsCompoundCurve(QgisTestCase):
|
||||
self.assertTrue(geom1.fuzzyEqual(geom2, epsilon))
|
||||
self.assertTrue(geom1.fuzzyDistanceEqual(geom2, epsilon))
|
||||
|
||||
def test_simplify_by_distance(self):
|
||||
"""
|
||||
test simplifyByDistance
|
||||
"""
|
||||
p = QgsCompoundCurve()
|
||||
p.fromWkt('CompoundCurve (CircularString (4.40660981021897413 0.93610259854013833, 11.01953454014598321 23.6382050218978037, 34.67607970802919226 28.41041874452553984),(34.67607970802919226 28.41041874452553984, 46.06121816058393392 30.38747871532845934, 61.74134896350363988 29.02398908029196178))')
|
||||
self.assertEqual(p.simplifyByDistance(0.5).asWkt(3), 'LineString (4.407 0.936, 3.765 8.905, 5.88 16.615, 10.217 22.855, 16.706 27.525, 21.235 29.154, 26.003 29.808, 34.676 28.41, 46.061 30.387, 61.741 29.024)')
|
||||
self.assertEqual(p.simplifyByDistance(0.75).asWkt(3),
|
||||
'LineString (4.407 0.936, 3.765 8.905, 5.88 16.615, 10.217 22.855, 16.706 27.525, 26.003 29.808, 34.676 28.41, 46.061 30.387, 61.741 29.024)')
|
||||
self.assertEqual(p.simplifyByDistance(1).asWkt(3),
|
||||
'LineString (4.407 0.936, 3.765 8.905, 5.88 16.615, 10.217 22.855, 16.706 27.525, 26.003 29.808, 34.676 28.41, 46.061 30.387, 61.741 29.024)')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@ -588,6 +588,14 @@ class TestQgsGeometryCollection(QgisTestCase):
|
||||
self.assertEqual(collection.boundingBox(),
|
||||
QgsRectangle(1, 2, 100, 200))
|
||||
|
||||
def test_simplify_by_distance(self):
|
||||
"""
|
||||
test simplifyByDistance
|
||||
"""
|
||||
p = QgsGeometryCollection()
|
||||
p.fromWkt('GeometryCollection( LineString(0 0, 50 0, 70 0, 80 0, 100 0), LineString(0 0, 50 1, 60 1, 100 0) )')
|
||||
self.assertEqual(p.simplifyByDistance(10).asWkt(), 'GeometryCollection (LineString (0 0, 100 0),LineString (0 0, 100 0))')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@ -219,6 +219,27 @@ class TestQgsLineString(QgisTestCase):
|
||||
# 3d distance
|
||||
self.assertEqual(line.interpolateM(True).asWkt(2), 'LineStringZM (5 10 15 17, 6 11 16 17, 20 10 5 17, 30 10 12 19.05, 30 40 17 25, 20 40 19 27, 20 50 21 27, 25 50 22 27, 25 55 22 27)')
|
||||
|
||||
def test_simplify_by_distance(self):
|
||||
"""
|
||||
test simplifyByDistance
|
||||
"""
|
||||
p = QgsLineString()
|
||||
# should never become < 2 vertices
|
||||
p.fromWkt('LineString(1 1, 1.001 1.001)')
|
||||
self.assertEqual(p.simplifyByDistance(0.5).asWkt(3), 'LineString (1 1, 1.001 1.001)')
|
||||
p.fromWkt('LineString (4.40700000000000003 0.93600000000000005, 3.76500000000000012 8.90499999999999936, 5.87999999999999989 16.61499999999999844, 10.21700000000000053 22.85500000000000043, 16.70599999999999952 27.52499999999999858, 26.00300000000000011 29.80799999999999983, 34.67600000000000193 28.41000000000000014, 46.06099999999999994 30.38700000000000045, 61.74099999999999966 29.02400000000000091)')
|
||||
self.assertEqual(p.simplifyByDistance(0.75).asWkt(3),
|
||||
'LineString (4.407 0.936, 3.765 8.905, 5.88 16.615, 10.217 22.855, 16.706 27.525, 26.003 29.808, 34.676 28.41, 46.061 30.387, 61.741 29.024)')
|
||||
self.assertEqual(p.simplifyByDistance(2).asWkt(3),
|
||||
'LineString (4.407 0.936, 5.88 16.615, 16.706 27.525, 61.741 29.024)')
|
||||
# ported geos tests
|
||||
p.fromWkt('LINESTRING (0 5, 1 5, 2 5, 5 5)')
|
||||
self.assertEqual(p.simplifyByDistance(10).asWkt(),
|
||||
'LineString (0 5, 5 5)')
|
||||
p.fromWkt('LINESTRING (1 0, 2 0, 2 2, 0 2, 0 0, 1 0)')
|
||||
self.assertEqual(p.simplifyByDistance(0).asWkt(),
|
||||
'LineString (2 0, 2 2, 0 2, 0 0, 2 0)')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@ -233,6 +233,14 @@ class TestQgsMultiLineString(QgisTestCase):
|
||||
self.assertEqual(collection.boundingBox(),
|
||||
QgsRectangle(1, 2, 300, 22))
|
||||
|
||||
def test_simplify_by_distance(self):
|
||||
"""
|
||||
test simplifyByDistance
|
||||
"""
|
||||
p = QgsMultiLineString()
|
||||
p.fromWkt('MultiLineString( (0 0, 50 0, 70 0, 80 0, 100 0), (0 0, 50 1, 60 1, 100 0) )')
|
||||
self.assertEqual(p.simplifyByDistance(10).asWkt(), 'MultiLineString ((0 0, 100 0),(0 0, 100 0))')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@ -148,6 +148,15 @@ class TestQgsMultiPoint(QgisTestCase):
|
||||
self.assertEqual(collection.boundingBox(),
|
||||
QgsRectangle(1, 2, 100, 22))
|
||||
|
||||
def test_simplify_by_distance(self):
|
||||
"""
|
||||
test simplifyByDistance
|
||||
"""
|
||||
p = QgsMultiPoint()
|
||||
p.fromWkt('MultiPoint( 0 0, 50 0, 70 0, 80 0, 100 0)')
|
||||
# this is just a clone
|
||||
self.assertEqual(p.simplifyByDistance(10).asWkt(), 'MultiPoint ((0 0),(50 0),(70 0),(80 0),(100 0))')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@ -244,6 +244,15 @@ class TestQgsMultiPolygon(QgisTestCase):
|
||||
self.assertEqual(collection.boundingBox(),
|
||||
QgsRectangle(1, 2, 300, 100))
|
||||
|
||||
def test_simplify_by_distance(self):
|
||||
"""
|
||||
test simplifyByDistance
|
||||
"""
|
||||
p = QgsMultiPolygon()
|
||||
p.fromWkt('MultiPolygon (((4.33843532846714908 1.48149845255473167, 4.47478429197079919 8.6398190364963412, 5.8382739270072932 16.47988443795619418, 10.61048764963503288 22.88828572262772809, 17.63245927007298519 27.72867392700729283, 27.04053775182481445 30.11478078832115912, 34.81242867153284237 28.34224426277371478, 42.51614510948904524 23.97907743065692188, 44.83407748905109713 17.84337407299268818, 44.15233267153284658 9.52608729927005982, 42.44797062773722018 1.75419637956203189, 37.26671001459853727 -4.65420490510949492, 29.5629935766423344 -6.63126487591242153, 18.51872753284671091 -7.31300969343067209, 7.1335890802919657 -5.13142627737227031, 5.15652910948904619 -1.9272256350365069, 4.33843532846714908 1.48149845255473167),(20.31173353218648003 19.78274965689762155, 17.28447821560356346 9.99697084282726678, 21.22695025580456729 4.57607178755088739, 26.01423773319150001 3.23844734533983569, 28.33748018545281155 3.87205892322928236, 32.20955093922164991 5.6320910840332985, 34.60319467791511983 8.37774125488756738, 35.23680625580456649 12.24981200865641284, 34.6735959643472782 15.84027761669661771, 32.13914965278949154 19.43074322473681548, 26.92945445680959438 22.03559082272676761, 22.98698241660859054 21.04997281267651488, 20.31173353218648003 19.78274965689762155)),((55.16037031610606789 15.48827118453581164, 57.8356192005281855 18.16352006895792215, 64.10133369299049377 20.27555866192274436, 70.71905461761360812 18.86753293327952719, 74.09831636635732366 16.4034879081538989, 75.71754595429702306 11.33459528503832558, 74.30952022565381299 6.47690652121922739, 69.38143017540255642 2.6752370538825474, 61.63728866786486549 1.90082290312877689, 56.4979947583171338 2.60483576745038192, 53.11873300957341826 6.82891295338003346, 52.83712786384477056 12.32021329508857832, 55.16037031610606789 15.48827118453581164)))')
|
||||
self.assertEqual(p.simplifyByDistance(1).asWkt(3), 'MultiPolygon (((4.338 1.481, 5.838 16.48, 10.61 22.888, 17.632 27.729, 27.041 30.115, 34.812 28.342, 42.516 23.979, 44.834 17.843, 44.152 9.526, 42.448 1.754, 37.267 -4.654, 18.519 -7.313, 7.134 -5.131, 4.338 1.481),(20.312 19.783, 17.284 9.997, 21.227 4.576, 26.014 3.238, 32.21 5.632, 34.603 8.378, 35.237 12.25, 32.139 19.431, 26.929 22.036, 20.312 19.783)),((57.836 18.164, 64.101 20.276, 70.719 18.868, 74.098 16.403, 75.718 11.335, 74.31 6.477, 69.381 2.675, 56.498 2.605, 53.119 6.829, 52.837 12.32, 57.836 18.164)))')
|
||||
self.assertEqual(p.simplifyByDistance(2).asWkt(3), 'MultiPolygon (((4.338 1.481, 5.838 16.48, 17.632 27.729, 27.041 30.115, 42.516 23.979, 44.152 9.526, 37.267 -4.654, 18.519 -7.313, 7.134 -5.131, 4.338 1.481),(20.312 19.783, 17.284 9.997, 21.227 4.576, 32.21 5.632, 35.237 12.25, 32.139 19.431, 26.929 22.036, 20.312 19.783)),((55.16 15.488, 64.101 20.276, 70.719 18.868, 75.718 11.335, 74.31 6.477, 69.381 2.675, 56.498 2.605, 53.119 6.829, 55.16 15.488)))')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@ -167,6 +167,16 @@ class TestQgsPoint(QgisTestCase):
|
||||
self.assertTrue(geom1.fuzzyEqual(geom2, epsilon))
|
||||
self.assertTrue(geom1.fuzzyDistanceEqual(geom2, epsilon))
|
||||
|
||||
def test_simplify_by_distance(self):
|
||||
"""
|
||||
test simplifyByDistance
|
||||
"""
|
||||
# for points this is just a clone
|
||||
p = QgsPoint(1.1,2 .2)
|
||||
self.assertEqual(p.simplifyByDistance(0.5), QgsPoint(1.1,2 .2))
|
||||
p = QgsPoint(1.1, 2.2, 3.3, 4.4)
|
||||
self.assertEqual(p.simplifyByDistance(0.5), QgsPoint(1.1, 2.2, 3.3, 4.4))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@ -97,6 +97,27 @@ class TestQgsPolygon(QgisTestCase):
|
||||
self.assertTrue(geom1.fuzzyEqual(geom2, epsilon))
|
||||
self.assertTrue(geom1.fuzzyDistanceEqual(geom2, epsilon))
|
||||
|
||||
def test_simplify_by_distance(self):
|
||||
"""
|
||||
test simplifyByDistance
|
||||
"""
|
||||
p = QgsPolygon()
|
||||
p.fromWkt('Polygon ((4.33843532846714908 1.48149845255473167, 4.47478429197079919 8.6398190364963412, 5.8382739270072932 16.47988443795619418, 10.61048764963503288 22.88828572262772809, 17.63245927007298519 27.72867392700729283, 27.04053775182481445 30.11478078832115912, 34.81242867153284237 28.34224426277371478, 42.51614510948904524 23.97907743065692188, 44.83407748905109713 17.84337407299268818, 44.15233267153284658 9.52608729927005982, 42.44797062773722018 1.75419637956203189, 37.26671001459853727 -4.65420490510949492, 29.5629935766423344 -6.63126487591242153, 18.51872753284671091 -7.31300969343067209, 7.1335890802919657 -5.13142627737227031, 5.15652910948904619 -1.9272256350365069, 4.33843532846714908 1.48149845255473167),(20.31173353218648003 19.78274965689762155, 17.28447821560356346 9.99697084282726678, 21.22695025580456729 4.57607178755088739, 26.01423773319150001 3.23844734533983569, 28.33748018545281155 3.87205892322928236, 32.20955093922164991 5.6320910840332985, 34.60319467791511983 8.37774125488756738, 35.23680625580456649 12.24981200865641284, 34.6735959643472782 15.84027761669661771, 32.13914965278949154 19.43074322473681548, 26.92945445680959438 22.03559082272676761, 22.98698241660859054 21.04997281267651488, 20.31173353218648003 19.78274965689762155))')
|
||||
self.assertEqual(p.simplifyByDistance(1).asWkt(3), 'Polygon ((4.338 1.481, 5.838 16.48, 10.61 22.888, 17.632 27.729, 27.041 30.115, 34.812 28.342, 42.516 23.979, 44.834 17.843, 44.152 9.526, 42.448 1.754, 37.267 -4.654, 18.519 -7.313, 7.134 -5.131, 4.338 1.481),(20.312 19.783, 17.284 9.997, 21.227 4.576, 26.014 3.238, 32.21 5.632, 34.603 8.378, 35.237 12.25, 32.139 19.431, 26.929 22.036, 20.312 19.783))')
|
||||
self.assertEqual(p.simplifyByDistance(2).asWkt(3), 'Polygon ((4.338 1.481, 5.838 16.48, 17.632 27.729, 27.041 30.115, 42.516 23.979, 44.152 9.526, 37.267 -4.654, 18.519 -7.313, 7.134 -5.131, 4.338 1.481),(20.312 19.783, 17.284 9.997, 21.227 4.576, 32.21 5.632, 35.237 12.25, 32.139 19.431, 26.929 22.036, 20.312 19.783))')
|
||||
|
||||
# ported GEOS tests
|
||||
p.fromWkt('POLYGON ((20 220, 40 220, 60 220, 80 220, 100 220, 120 220, 140 220, 140 180, 100 180, 60 180, 20 180, 20 220))')
|
||||
self.assertEqual(p.simplifyByDistance(10).asWkt(), 'Polygon ((20 220, 140 220, 140 180, 20 180, 20 220))')
|
||||
p.fromWkt('POLYGON ((120 120, 121 121, 122 122, 220 120, 180 199, 160 200, 140 199, 120 120))')
|
||||
self.assertEqual(p.simplifyByDistance(10).asWkt(), 'Polygon ((120 120, 220 120, 180 199, 160 200, 140 199, 120 120))')
|
||||
p.fromWkt('POLYGON ((80 200, 240 200, 240 60, 80 60, 80 200), (120 120, 220 120, 180 199, 160 200, 140 199, 120 120))')
|
||||
self.assertEqual(p.simplifyByDistance(10).asWkt(), 'Polygon ((80 200, 240 200, 240 60, 80 60, 80 200),(120 120, 220 120, 180 199, 160 200, 140 199, 120 120))')
|
||||
p.fromWkt('POLYGON ((1 0, 2 0, 2 2, 0 2, 0 0, 1 0))')
|
||||
self.assertEqual(p.simplifyByDistance(0).asWkt(), 'Polygon ((2 0, 2 2, 0 2, 0 0, 2 0))')
|
||||
p.fromWkt('POLYGON ((42 42, 0 42, 0 100, 42 100, 100 42, 42 42))')
|
||||
self.assertEqual(p.simplifyByDistance(1).asWkt(), 'Polygon ((0 42, 0 100, 42 100, 100 42, 0 42))')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user