Make asGridified and newSameType (helper function)

This commit is contained in:
Martí Angelats i Ribera 2017-08-31 19:39:37 +02:00 committed by Nyall Dawson
parent 99b1c74c4e
commit 3f5b11bffe
49 changed files with 576 additions and 14 deletions

View File

@ -71,6 +71,16 @@ class QgsAbstractGeometry
virtual ~QgsAbstractGeometry();
QgsAbstractGeometry( const QgsAbstractGeometry &geom );
virtual QgsAbstractGeometry *createEmptyWithSameType() const = 0 /Factory/;
%Docstring
Makes a new geometry with the same class and same WKB and transfers ownership.
To create it, the geometry is default constructedand then the WKB is changed.
:return: the new empty geometry. Callee takes ownership.
.. seealso:: clone
.. versionadded:: 3.0
:rtype: QgsAbstractGeometry
%End
virtual QgsAbstractGeometry *clone() const = 0 /Factory/;
%Docstring
Clones the geometry by performing a deep copy
@ -402,6 +412,32 @@ Returns the centroid of the geometry
:rtype: QgsAbstractGeometry
%End
virtual QgsAbstractGeometry *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, double tolerance = M_PI / 180., SegmentationToleranceType toleranceType = MaximumAngle ) const = 0 /Factory/;
%Docstring
Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
It transfers ownership to the callee.
If it couldn't make the gridified geometry it returns None.
It may generate an invalid geometry (in some corner cases).
It can also be thought as rounding the edges and it may be useful for removing errors.
If the geometry is curved, it will be segmentized before gridifying it.
Example:
\code
geometry->snappedToGrid(1, 1);
\endcode
In this case we use a 2D grid of 1x1 to gridify.
In this case, it can be thought like rounding the x and y of all the points/vertices to full units (remove all decimals).
\param hSpacing Horizontal spacing of the grid (x axis). 0 to disable.
\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 tolerance In case of segmentation, the tolerance to use (passed to segmentize as is).
\param toleranceType In case of segmentation, the toleranceType to use (passed to segmentize as is).
:return: the segmentized geometry or None if it wasn't possible to make. Caller takes ownership.
.. seealso:: segmentize
.. versionadded:: 3.0
:rtype: QgsAbstractGeometry
%End
virtual double vertexAngle( QgsVertexId vertex ) const = 0;
%Docstring
Returns approximate angle at a vertex. This is usually the average angle between adjacent

View File

@ -29,6 +29,7 @@ class QgsCircularString: QgsCurve
virtual QString geometryType() const;
virtual int dimension() const;
virtual QgsCircularString *createEmptyWithSameType() const /Factory/;
virtual QgsCircularString *clone() const /Factory/;
virtual void clear();

View File

@ -29,6 +29,7 @@ class QgsCompoundCurve: QgsCurve
virtual QString geometryType() const;
virtual int dimension() const;
virtual QgsCompoundCurve *createEmptyWithSameType() const /Factory/;
virtual QgsCompoundCurve *clone() const /Factory/;
virtual void clear();

View File

@ -126,6 +126,8 @@ class QgsCurve: QgsAbstractGeometry
virtual QgsAbstractGeometry *boundary() const /Factory/;
virtual QgsCurve *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0,
double tolerance = M_PI / 180., SegmentationToleranceType toleranceType = MaximumAngle ) const /Factory/;
virtual QgsCurve *segmentize( double tolerance = M_PI_2 / 90, SegmentationToleranceType toleranceType = MaximumAngle ) const /Factory/;

View File

@ -35,7 +35,7 @@ class QgsCurvePolygon: QgsSurface
virtual QString geometryType() const;
virtual int dimension() const;
virtual QgsCurvePolygon *createEmptyWithSameType() const /Factory/;
virtual QgsCurvePolygon *clone() const /Factory/;
virtual void clear();
@ -64,7 +64,8 @@ class QgsCurvePolygon: QgsSurface
virtual QgsPolygonV2 *surfaceToPolygon() const /Factory/;
virtual QgsAbstractGeometry *boundary() const /Factory/;
virtual QgsCurvePolygon *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0,
double tolerance = M_PI / 180., SegmentationToleranceType toleranceType = MaximumAngle ) const /Factory/;
int numInteriorRings() const;
%Docstring

View File

@ -26,6 +26,7 @@ class QgsGeometryCollection: QgsAbstractGeometry
QgsGeometryCollection( const QgsGeometryCollection &c );
virtual ~QgsGeometryCollection();
virtual QgsGeometryCollection *createEmptyWithSameType() const /Factory/;
virtual QgsGeometryCollection *clone() const /Factory/;
@ -50,7 +51,8 @@ class QgsGeometryCollection: QgsAbstractGeometry
virtual QString geometryType() const;
virtual void clear();
virtual QgsGeometryCollection *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0,
double tolerance = M_PI / 180., SegmentationToleranceType toleranceType = MaximumAngle ) const /Factory/;
virtual QgsAbstractGeometry *boundary() const /Factory/;
virtual void adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex /Out/, QgsVertexId &nextVertex /Out/ ) const;

View File

@ -170,13 +170,15 @@ Closes the line string by appending the first point to the end of the line, if i
virtual QString geometryType() const;
virtual int dimension() const;
virtual QgsLineString *createEmptyWithSameType() const /Factory/;
virtual QgsLineString *clone() const /Factory/;
virtual void clear();
virtual bool isEmpty() const;
virtual QgsLineString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0,
double tolerance = M_PI / 180., SegmentationToleranceType toleranceType = MaximumAngle ) const /Factory/;
virtual bool fromWkb( QgsConstWkbPtr &wkb );

View File

@ -21,7 +21,7 @@ class QgsMultiCurve: QgsGeometryCollection
public:
QgsMultiCurve();
virtual QString geometryType() const;
virtual QgsMultiCurve *createEmptyWithSameType() const /Factory/;
virtual QgsMultiCurve *clone() const /Factory/;
virtual void clear();

View File

@ -22,7 +22,7 @@ class QgsMultiLineString: QgsMultiCurve
QgsMultiLineString();
virtual QString geometryType() const;
virtual QgsMultiLineString *createEmptyWithSameType() const /Factory/;
virtual QgsMultiLineString *clone() const /Factory/;
virtual void clear();

View File

@ -22,7 +22,7 @@ class QgsMultiPointV2: QgsGeometryCollection
QgsMultiPointV2();
virtual QString geometryType() const;
virtual QgsMultiPointV2 *createEmptyWithSameType() const /Factory/;
virtual QgsMultiPointV2 *clone() const /Factory/;
virtual QgsMultiPointV2 *toCurveType() const /Factory/;

View File

@ -21,7 +21,7 @@ class QgsMultiPolygonV2: QgsMultiSurface
public:
QgsMultiPolygonV2();
virtual QString geometryType() const;
virtual QgsMultiPolygonV2 *createEmptyWithSameType() const /Factory/;
virtual void clear();
virtual QgsMultiPolygonV2 *clone() const /Factory/;

View File

@ -21,9 +21,8 @@ class QgsMultiSurface: QgsGeometryCollection
public:
QgsMultiSurface();
virtual QString geometryType() const;
virtual void clear();
virtual QgsMultiSurface *createEmptyWithSameType() const /Factory/;
virtual QgsMultiSurface *clone() const /Factory/;
virtual QgsMultiSurface *toCurveType() const /Factory/;

View File

@ -335,9 +335,10 @@ class QgsPoint: QgsAbstractGeometry
virtual QString geometryType() const;
virtual int dimension() const;
virtual QgsPoint *createEmptyWithSameType() const /Factory/;
virtual QgsPoint *clone() const /Factory/;
virtual QgsPoint *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0,
double tolerance = M_PI / 180., SegmentationToleranceType toleranceType = MaximumAngle ) const /Factory/;
virtual void clear();
virtual bool fromWkb( QgsConstWkbPtr &wkb );

View File

@ -23,7 +23,7 @@ class QgsPolygonV2: QgsCurvePolygon
QgsPolygonV2();
virtual QString geometryType() const;
virtual QgsPolygonV2 *createEmptyWithSameType() const /Factory/;
virtual QgsPolygonV2 *clone() const /Factory/;
virtual void clear();

View File

@ -25,6 +25,9 @@ class QgsSurface: QgsAbstractGeometry
:rtype: QgsPolygonV2
%End
virtual QgsSurface *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0,
double tolerance = M_PI / 180., SegmentationToleranceType toleranceType = MaximumAngle ) const /Factory/;
virtual QgsRectangle boundingBox() const;
%Docstring
Returns the minimal bounding box for the geometry

View File

@ -53,7 +53,7 @@ class QgsTriangle : QgsPolygonV2
%End
virtual QString geometryType() const;
virtual QgsTriangle *createEmptyWithSameType() const /Factory/;
virtual QgsTriangle *clone() const /Factory/;
virtual void clear();

View File

@ -467,6 +467,7 @@ SET(QGIS_CORE_SRCS
geometry/qgsrectangle.cpp
geometry/qgsreferencedgeometry.cpp
geometry/qgsregularpolygon.cpp
geometry/qgssurface.cpp
geometry/qgstriangle.cpp
geometry/qgswkbptr.cpp
geometry/qgswkbtypes.cpp

View File

@ -110,6 +110,14 @@ class CORE_EXPORT QgsAbstractGeometry
QgsAbstractGeometry( const QgsAbstractGeometry &geom );
QgsAbstractGeometry &operator=( const QgsAbstractGeometry &geom );
/** Makes a new geometry with the same class and same WKB and transfers ownership.
* To create it, the geometry is default constructedand then the WKB is changed.
* \returns the new empty geometry. Callee takes ownership.
* \see clone
* \since 3.0
*/
virtual QgsAbstractGeometry *createEmptyWithSameType() const = 0 SIP_FACTORY;
/**
* Clones the geometry by performing a deep copy
*/
@ -411,6 +419,30 @@ class CORE_EXPORT QgsAbstractGeometry
*/
virtual QgsAbstractGeometry *toCurveType() const = 0 SIP_FACTORY;
/** Makes a new geometry with all the points or vertices snapped to the closest point of the grid.
* It transfers ownership to the callee.
* If it couldn't make the gridified geometry it returns nullptr.
* It may generate an invalid geometry (in some corner cases).
* It can also be thought as rounding the edges and it may be useful for removing errors.
* If the geometry is curved, it will be segmentized before gridifying it.
* Example:
* \code
* geometry->snappedToGrid(1, 1);
* \endcode
* In this case we use a 2D grid of 1x1 to gridify.
* In this case, it can be thought like rounding the x and y of all the points/vertices to full units (remove all decimals).
* \param hSpacing Horizontal spacing of the grid (x axis). 0 to disable.
* \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 tolerance In case of segmentation, the tolerance to use (passed to segmentize as is).
* \param toleranceType In case of segmentation, the toleranceType to use (passed to segmentize as is).
* \returns the segmentized geometry or nullptr if it wasn't possible to make. Caller takes ownership.
* \see segmentize
* \since 3.0
*/
virtual QgsAbstractGeometry *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0, double tolerance = M_PI / 180., SegmentationToleranceType toleranceType = MaximumAngle ) const = 0 SIP_FACTORY;
/**
* Returns approximate angle at a vertex. This is usually the average angle between adjacent
* segments, and can be pictured as the orientation of a line following the curvature of the

View File

@ -66,6 +66,13 @@ bool QgsCircularString::operator!=( const QgsCurve &other ) const
return !operator==( other );
}
QgsCircularString *QgsCircularString::createEmptyWithSameType() const
{
auto result = new QgsCircularString();
result->mWkbType = mWkbType;
return result;
}
QString QgsCircularString::geometryType() const
{
return QStringLiteral( "CircularString" );

View File

@ -41,6 +41,7 @@ class CORE_EXPORT QgsCircularString: public QgsCurve
virtual QString geometryType() const override;
virtual int dimension() const override;
virtual QgsCircularString *createEmptyWithSameType() const override SIP_FACTORY;
virtual QgsCircularString *clone() const override SIP_FACTORY;
virtual void clear() override;

View File

@ -61,6 +61,13 @@ bool QgsCompoundCurve::operator!=( const QgsCurve &other ) const
return !operator==( other );
}
QgsCompoundCurve *QgsCompoundCurve::createEmptyWithSameType() const
{
auto result = new QgsCompoundCurve();
result->mWkbType = mWkbType;
return result;
}
QString QgsCompoundCurve::geometryType() const
{
return QStringLiteral( "CompoundCurve" );

View File

@ -41,6 +41,7 @@ class CORE_EXPORT QgsCompoundCurve: public QgsCurve
virtual QString geometryType() const override;
virtual int dimension() const override;
virtual QgsCompoundCurve *createEmptyWithSameType() const override SIP_FACTORY;
virtual QgsCompoundCurve *clone() const override SIP_FACTORY;
virtual void clear() override;

View File

@ -15,6 +15,8 @@
* *
***************************************************************************/
#include <memory>
#include "qgscurve.h"
#include "qgslinestring.h"
#include "qgspoint.h"
@ -129,6 +131,13 @@ QgsAbstractGeometry *QgsCurve::boundary() const
return multiPoint;
}
QgsCurve *QgsCurve::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing,
double tolerance, SegmentationToleranceType toleranceType ) const
{
std::unique_ptr<QgsLineString> line { curveToLine( tolerance, toleranceType ) };
return line->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing );
}
QgsCurve *QgsCurve::segmentize( double tolerance, SegmentationToleranceType toleranceType ) const
{
return curveToLine( tolerance, toleranceType );

View File

@ -123,6 +123,9 @@ class CORE_EXPORT QgsCurve: public QgsAbstractGeometry
QgsAbstractGeometry *boundary() const override SIP_FACTORY;
virtual QgsCurve *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0,
double tolerance = M_PI / 180., SegmentationToleranceType toleranceType = MaximumAngle ) const override SIP_FACTORY;
/**
* Returns a geometry without curves. Caller takes ownership
* \param tolerance segmentation tolerance

View File

@ -38,6 +38,13 @@ QgsCurvePolygon::~QgsCurvePolygon()
clear();
}
QgsCurvePolygon *QgsCurvePolygon::createEmptyWithSameType() const
{
auto result = new QgsCurvePolygon();
result->mWkbType = mWkbType;
return result;
}
QString QgsCurvePolygon::geometryType() const
{
return QStringLiteral( "CurvePolygon" );
@ -495,6 +502,49 @@ QgsAbstractGeometry *QgsCurvePolygon::boundary() const
}
}
QgsCurvePolygon *QgsCurvePolygon::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing,
double tolerance, SegmentationToleranceType toleranceType ) const
{
if ( !mExteriorRing )
return nullptr;
std::unique_ptr<QgsPolygonV2> polygon;
if ( QgsWkbTypes::flatType( mWkbType ) == QgsWkbTypes::Triangle || QgsWkbTypes::flatType( mWkbType ) == QgsWkbTypes::Polygon )
{
polygon = std::unique_ptr<QgsPolygonV2> { static_cast< QgsPolygonV2 const *>( this )->createEmptyWithSameType() };
}
else
{
polygon = std::unique_ptr<QgsPolygonV2> { new QgsPolygonV2() };
polygon->mWkbType = QgsWkbTypes::zmType( QgsWkbTypes::Polygon, QgsWkbTypes::hasZ( mWkbType ), QgsWkbTypes::hasM( mWkbType ) );
}
// exterior ring
auto exterior = std::unique_ptr<QgsCurve> { mExteriorRing->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, tolerance, toleranceType ) };
if ( !exterior )
return nullptr;
polygon->mExteriorRing = exterior.release();
//interior rings
for ( auto interior : mInteriorRings )
{
if ( !interior )
continue;
QgsCurve *gridifiedInterior = interior->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, tolerance, toleranceType );
if ( !gridifiedInterior )
continue;
polygon->mInteriorRings.append( gridifiedInterior );
}
return polygon.release();
}
QgsPolygonV2 *QgsCurvePolygon::toPolygon( double tolerance, SegmentationToleranceType toleranceType ) const
{
std::unique_ptr< QgsPolygonV2 > poly( new QgsPolygonV2() );

View File

@ -45,6 +45,7 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
QString geometryType() const override;
int dimension() const override;
virtual QgsCurvePolygon *createEmptyWithSameType() const override SIP_FACTORY;
QgsCurvePolygon *clone() const override SIP_FACTORY;
void clear() override;
@ -62,6 +63,8 @@ class CORE_EXPORT QgsCurvePolygon: public QgsSurface
double perimeter() const override;
QgsPolygonV2 *surfaceToPolygon() const override SIP_FACTORY;
QgsAbstractGeometry *boundary() const override SIP_FACTORY;
virtual QgsCurvePolygon *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0,
double tolerance = M_PI / 180., SegmentationToleranceType toleranceType = MaximumAngle ) const override SIP_FACTORY;
//curve polygon interface
int numInteriorRings() const;

View File

@ -26,6 +26,7 @@ email : marco.hugentobler at sourcepole dot com
#include "qgspolygon.h"
#include "qgsmultipolygon.h"
#include "qgswkbptr.h"
#include <memory>
QgsGeometryCollection::QgsGeometryCollection()
{
@ -63,6 +64,13 @@ QgsGeometryCollection::~QgsGeometryCollection()
clear();
}
QgsGeometryCollection *QgsGeometryCollection::createEmptyWithSameType() const
{
auto result = new QgsGeometryCollection();
result->mWkbType = mWkbType;
return result;
}
QgsGeometryCollection *QgsGeometryCollection::clone() const
{
return new QgsGeometryCollection( *this );
@ -75,6 +83,26 @@ void QgsGeometryCollection::clear()
clearCache(); //set bounding box invalid
}
QgsGeometryCollection *QgsGeometryCollection::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing,
double tolerance, SegmentationToleranceType toleranceType ) const
{
std::unique_ptr<QgsGeometryCollection> result;
for ( auto geom : mGeometries )
{
std::unique_ptr<QgsAbstractGeometry> gridified { geom->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing, tolerance, toleranceType ) };
if ( gridified )
{
if ( !result )
result = std::unique_ptr<QgsGeometryCollection> { createEmptyWithSameType() };
result->mGeometries.append( gridified.release() );
}
}
return result.release();
}
QgsAbstractGeometry *QgsGeometryCollection::boundary() const
{
return nullptr;

View File

@ -39,6 +39,7 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
QgsGeometryCollection &operator=( const QgsGeometryCollection &c );
virtual ~QgsGeometryCollection();
virtual QgsGeometryCollection *createEmptyWithSameType() const override SIP_FACTORY;
QgsGeometryCollection *clone() const override SIP_FACTORY;
/**
@ -64,6 +65,8 @@ class CORE_EXPORT QgsGeometryCollection: public QgsAbstractGeometry
int dimension() const override;
QString geometryType() const override;
void clear() override;
virtual QgsGeometryCollection *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0,
double tolerance = M_PI / 180., SegmentationToleranceType toleranceType = MaximumAngle ) const override SIP_FACTORY;
QgsAbstractGeometry *boundary() const override SIP_FACTORY;
void adjacentVertices( QgsVertexId vertex, QgsVertexId &previousVertex SIP_OUT, QgsVertexId &nextVertex SIP_OUT ) const override;
int vertexNumberFromVertexId( QgsVertexId id ) const override;

View File

@ -23,6 +23,8 @@
#include "qgsmaptopixel.h"
#include "qgswkbptr.h"
#include <cmath>
#include <memory>
#include <QPainter>
#include <limits>
#include <QDomDocument>
@ -187,6 +189,84 @@ bool QgsLineString::isEmpty() const
return mX.isEmpty();
}
QgsLineString *QgsLineString::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing,
double /*tolerance*/, SegmentationToleranceType /*toleranceType*/ ) const
{
int length = numPoints();
if ( length <= 0 )
return nullptr;
// prepare result
std::unique_ptr<QgsLineString> result { createEmptyWithSameType() };
// helper functions
auto roundVertex = [&]( QgsPoint & out, int i )
{
if ( hSpacing > 0 )
out.setX( std::round( mX.at( i ) / hSpacing ) * hSpacing );
if ( vSpacing > 0 )
out.setY( std::round( mY.at( i ) / vSpacing ) * vSpacing );
if ( dSpacing > 0 && QgsWkbTypes::hasZ( mWkbType ) )
out.setZ( std::round( mZ.at( i ) / dSpacing ) * dSpacing );
if ( mSpacing > 0 && QgsWkbTypes::hasM( mWkbType ) )
out.setM( std::round( mM.at( i ) / mSpacing ) * mSpacing );
};
auto append = [this, &result]( QgsPoint const & point )
{
result->mX.append( point.x() );
result->mY.append( point.y() );
if ( QgsWkbTypes::hasZ( mWkbType ) )
result->mZ.append( point.z() );
if ( QgsWkbTypes::hasM( mWkbType ) )
result->mM.append( point.m() );
};
auto isPointEqual = [&]( const QgsPoint & a, const QgsPoint & b )
{
return ( a.x() == b.x() )
&& ( a.y() == b.y() )
&& ( !QgsWkbTypes::hasZ( mWkbType ) || dSpacing <= 0 || a.z() == b.z() )
&& ( !QgsWkbTypes::hasM( mWkbType ) || mSpacing <= 0 || a.m() == b.m() );
};
// temporary values
QgsWkbTypes::Type pointType = QgsWkbTypes::zmType( QgsWkbTypes::Point, QgsWkbTypes::hasZ( mWkbType ), QgsWkbTypes::hasM( mWkbType ) );
QgsPoint last( pointType );
QgsPoint current( pointType );
// Actual code (what does all the work)
roundVertex( last, 0 );
append( last );
for ( int i = 1; i < length; ++i )
{
roundVertex( current, i );
if ( !isPointEqual( current, last ) )
{
append( current );
last = current;
}
}
// if it's not closed, with 2 points you get a correct line
// if it is, you need at least 4 (3 + the vertex that closes)
if ( result->mX.length() < 2 || ( isClosed() && result->mX.length() < 4 ) )
return nullptr;
return result.release();
}
bool QgsLineString::fromWkb( QgsConstWkbPtr &wkbPtr )
{
if ( !wkbPtr )
@ -711,6 +791,13 @@ void QgsLineString::extend( double startDistance, double endDistance )
}
}
QgsLineString *QgsLineString::createEmptyWithSameType() const
{
auto result = new QgsLineString();
result->mWkbType = mWkbType;
return result;
}
QString QgsLineString::geometryType() const
{
return QStringLiteral( "LineString" );

View File

@ -176,9 +176,12 @@ class CORE_EXPORT QgsLineString: public QgsCurve
QString geometryType() const override;
int dimension() const override;
virtual QgsLineString *createEmptyWithSameType() const override SIP_FACTORY;
QgsLineString *clone() const override SIP_FACTORY;
void clear() override;
bool isEmpty() const override;
virtual QgsLineString *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0,
double tolerance = M_PI / 180., SegmentationToleranceType toleranceType = MaximumAngle ) const override SIP_FACTORY;
bool fromWkb( QgsConstWkbPtr &wkb ) override;
bool fromWkt( const QString &wkt ) override;

View File

@ -33,6 +33,13 @@ QString QgsMultiCurve::geometryType() const
return QStringLiteral( "MultiCurve" );
}
QgsMultiCurve *QgsMultiCurve::createEmptyWithSameType() const
{
auto result = new QgsMultiCurve();
result->mWkbType = mWkbType;
return result;
}
QgsMultiCurve *QgsMultiCurve::clone() const
{
return new QgsMultiCurve( *this );

View File

@ -31,6 +31,7 @@ class CORE_EXPORT QgsMultiCurve: public QgsGeometryCollection
public:
QgsMultiCurve();
QString geometryType() const override;
virtual QgsMultiCurve *createEmptyWithSameType() const override SIP_FACTORY;
QgsMultiCurve *clone() const override SIP_FACTORY;
void clear() override;
QgsMultiCurve *toCurveType() const override SIP_FACTORY;

View File

@ -32,6 +32,13 @@ QString QgsMultiLineString::geometryType() const
return QStringLiteral( "MultiLineString" );
}
QgsMultiLineString *QgsMultiLineString::createEmptyWithSameType() const
{
auto result = new QgsMultiLineString();
result->mWkbType = mWkbType;
return result;
}
QgsMultiLineString *QgsMultiLineString::clone() const
{
return new QgsMultiLineString( *this );

View File

@ -32,6 +32,7 @@ class CORE_EXPORT QgsMultiLineString: public QgsMultiCurve
QgsMultiLineString();
QString geometryType() const override;
virtual QgsMultiLineString *createEmptyWithSameType() const override SIP_FACTORY;
QgsMultiLineString *clone() const override SIP_FACTORY;
void clear() override;
bool fromWkt( const QString &wkt ) override;

View File

@ -29,6 +29,13 @@ QString QgsMultiPointV2::geometryType() const
return QStringLiteral( "MultiPoint" );
}
QgsMultiPointV2 *QgsMultiPointV2::createEmptyWithSameType() const
{
auto result = new QgsMultiPointV2();
result->mWkbType = mWkbType;
return result;
}
QgsMultiPointV2 *QgsMultiPointV2::clone() const
{
return new QgsMultiPointV2( *this );

View File

@ -32,6 +32,7 @@ class CORE_EXPORT QgsMultiPointV2: public QgsGeometryCollection
QgsMultiPointV2();
QString geometryType() const override;
virtual QgsMultiPointV2 *createEmptyWithSameType() const override SIP_FACTORY;
QgsMultiPointV2 *clone() const override SIP_FACTORY;
QgsMultiPointV2 *toCurveType() const override SIP_FACTORY;
bool fromWkt( const QString &wkt ) override;

View File

@ -38,6 +38,13 @@ void QgsMultiPolygonV2::clear()
mWkbType = QgsWkbTypes::MultiPolygon;
}
QgsMultiPolygonV2 *QgsMultiPolygonV2::createEmptyWithSameType() const
{
auto result = new QgsMultiPolygonV2();
result->mWkbType = mWkbType;
return result;
}
QgsMultiPolygonV2 *QgsMultiPolygonV2::clone() const
{
return new QgsMultiPolygonV2( *this );

View File

@ -32,6 +32,7 @@ class CORE_EXPORT QgsMultiPolygonV2: public QgsMultiSurface
QgsMultiPolygonV2();
QString geometryType() const override;
void clear() override;
virtual QgsMultiPolygonV2 *createEmptyWithSameType() const override SIP_FACTORY;
QgsMultiPolygonV2 *clone() const override SIP_FACTORY;
bool fromWkt( const QString &wkt ) override;
QDomElement asGML2( QDomDocument &doc, int precision = 17, const QString &ns = "gml" ) const override;

View File

@ -39,6 +39,13 @@ void QgsMultiSurface::clear()
mWkbType = QgsWkbTypes::MultiSurface;
}
QgsMultiSurface *QgsMultiSurface::createEmptyWithSameType() const
{
auto result = new QgsMultiSurface();
result->mWkbType = mWkbType;
return result;
}
QgsMultiSurface *QgsMultiSurface::clone() const
{
return new QgsMultiSurface( *this );

View File

@ -32,6 +32,7 @@ class CORE_EXPORT QgsMultiSurface: public QgsGeometryCollection
QgsMultiSurface();
QString geometryType() const override;
void clear() override;
virtual QgsMultiSurface *createEmptyWithSameType() const override SIP_FACTORY;
QgsMultiSurface *clone() const override SIP_FACTORY;
QgsMultiSurface *toCurveType() const override SIP_FACTORY;
bool fromWkt( const QString &wkt ) override;

View File

@ -22,6 +22,8 @@
#include "qgsgeometryutils.h"
#include "qgsmaptopixel.h"
#include "qgswkbptr.h"
#include <cmath>
#include <QPainter>
#include <QRegularExpression>
@ -114,6 +116,28 @@ QgsPoint *QgsPoint::clone() const
return new QgsPoint( *this );
}
QgsPoint *QgsPoint::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing,
double /*tolerance*/, SegmentationToleranceType /*toleranceType*/ ) const
{
// helper function
auto gridifyValue = []( double value, double spacing, bool extraCondition = true ) -> double
{
if ( spacing > 0 && extraCondition )
return std::round( value / spacing ) * spacing;
else
return value;
};
// Get the new values
auto x = gridifyValue( mX, hSpacing );
auto y = gridifyValue( mY, vSpacing );
auto z = gridifyValue( mZ, dSpacing, QgsWkbTypes::hasZ( mWkbType ) );
auto m = gridifyValue( mM, mSpacing, QgsWkbTypes::hasM( mWkbType ) );
// return the new object
return new QgsPoint( mWkbType, x, y, z, m );
}
bool QgsPoint::fromWkb( QgsConstWkbPtr &wkbPtr )
{
QgsWkbTypes::Type type = wkbPtr.readHeader();
@ -669,3 +693,9 @@ QgsPoint QgsPoint::childPoint( int index ) const
Q_ASSERT( index == 0 );
return *this;
}
QgsPoint *QgsPoint::createEmptyWithSameType() const
{
double nan = std::numeric_limits<double>::quiet_NaN();
return new QgsPoint( nan, nan, nan, nan, mWkbType );
}

View File

@ -390,7 +390,10 @@ class CORE_EXPORT QgsPoint: public QgsAbstractGeometry
QgsRectangle boundingBox() const override;
QString geometryType() const override;
int dimension() const override;
virtual QgsPoint *createEmptyWithSameType() const override SIP_FACTORY;
QgsPoint *clone() const override SIP_FACTORY;
virtual QgsPoint *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0,
double tolerance = M_PI / 180., SegmentationToleranceType toleranceType = MaximumAngle ) const override SIP_FACTORY;
void clear() override;
bool fromWkb( QgsConstWkbPtr &wkb ) override;
bool fromWkt( const QString &wkt ) override;

View File

@ -32,6 +32,13 @@ QString QgsPolygonV2::geometryType() const
return QStringLiteral( "Polygon" );
}
QgsPolygonV2 *QgsPolygonV2::createEmptyWithSameType() const
{
auto result = new QgsPolygonV2();
result->mWkbType = mWkbType;
return result;
}
QgsPolygonV2 *QgsPolygonV2::clone() const
{
return new QgsPolygonV2( *this );

View File

@ -35,6 +35,7 @@ class CORE_EXPORT QgsPolygonV2: public QgsCurvePolygon
QString geometryType() const override;
QgsPolygonV2 *clone() const override SIP_FACTORY;
virtual QgsPolygonV2 *createEmptyWithSameType() const override SIP_FACTORY;
void clear() override;
bool fromWkb( QgsConstWkbPtr &wkb ) override;
QByteArray asWkb() const override;

View File

@ -0,0 +1,32 @@
/***************************************************************************
qgscurvepolygon.cpp
---------------------
begin : August 2017
copyright : (C) 2017 by Martí Angelats i Ribera
email : marti dot angelats at psig dot cat
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgssurface.h"
#include "qgspoint.h"
#include "qgspolygon.h"
#include <memory>
QgsSurface *QgsSurface::snappedToGrid( double hSpacing, double vSpacing, double dSpacing, double mSpacing,
double /*tolerance*/, SegmentationToleranceType /*toleranceType*/ ) const
{
std::unique_ptr<QgsPolygonV2> polygon { surfaceToPolygon() };
if ( !polygon )
return nullptr;
return polygon->snappedToGrid( hSpacing, vSpacing, dSpacing, mSpacing );
}

View File

@ -39,6 +39,9 @@ class CORE_EXPORT QgsSurface: public QgsAbstractGeometry
*/
virtual QgsPolygonV2 *surfaceToPolygon() const = 0 SIP_FACTORY;
virtual QgsSurface *snappedToGrid( double hSpacing, double vSpacing, double dSpacing = 0, double mSpacing = 0,
double tolerance = M_PI / 180., SegmentationToleranceType toleranceType = MaximumAngle ) const override SIP_FACTORY;
/**
* Returns the minimal bounding box for the geometry
*/

View File

@ -89,6 +89,13 @@ QString QgsTriangle::geometryType() const
return QStringLiteral( "Triangle" );
}
QgsTriangle *QgsTriangle::createEmptyWithSameType() const
{
auto result = new QgsTriangle();
result->mWkbType = mWkbType;
return result;
}
void QgsTriangle::clear()
{
QgsCurvePolygon::clear();

View File

@ -64,6 +64,7 @@ class CORE_EXPORT QgsTriangle : public QgsPolygonV2
QString geometryType() const override;
QgsTriangle *clone() const override SIP_FACTORY;
virtual QgsTriangle *createEmptyWithSameType() const override SIP_FACTORY;
void clear() override;
bool fromWkb( QgsConstWkbPtr &wkbPtr ) override;

View File

@ -13,6 +13,9 @@
* *
***************************************************************************/
#include "qgstest.h"
#include <cmath>
#include <memory>
#include <limits>
#include <QObject>
#include <QString>
#include <QStringList>
@ -101,6 +104,8 @@ class TestQgsGeometry : public QObject
void comparePolylines();
void comparePolygons();
void createEmptyWithSameType();
// MK, Disabled 14.11.2014
// Too unclear what exactly should be tested and which variations are allowed for the line
#if 0
@ -138,6 +143,7 @@ class TestQgsGeometry : public QObject
void minimalEnclosingCircle( );
void splitGeometry();
void snappedToGrid();
private:
//! A helper method to do a render check to see if the geometry op is as expected
@ -14749,6 +14755,66 @@ void TestQgsGeometry::comparePolygons()
}
// Helper function (in anonymous namespace to prevent possible link with the extirior)
namespace
{
template<typename T>
inline void testCreateEmptyWithSameType( bool canBeEmpty = true )
{
std::unique_ptr<QgsAbstractGeometry> geom { new T() };
std::unique_ptr<QgsAbstractGeometry> created { geom->createEmptyWithSameType() };
if ( canBeEmpty )
{
QVERIFY( created->isEmpty() );
}
// Check that it is the correct type
QVERIFY( static_cast<T *>( created.get() ) != nullptr );
}
}
void TestQgsGeometry::createEmptyWithSameType()
{
qDebug( "createEmptyWithSameType(): QgsCircularString" );
testCreateEmptyWithSameType<QgsCircularString>();
qDebug( "createEmptyWithSameType(): QgsCompoundCurve" );
testCreateEmptyWithSameType<QgsCompoundCurve>();
qDebug( "createEmptyWithSameType(): QgsLineString" );
testCreateEmptyWithSameType<QgsLineString>();
qDebug( "createEmptyWithSameType(): QgsGeometryCollection" );
testCreateEmptyWithSameType<QgsGeometryCollection>();
qDebug( "createEmptyWithSameType(): QgsMultiCurve" );
testCreateEmptyWithSameType<QgsMultiCurve>();
qDebug( "createEmptyWithSameType(): QgsMultiLineString" );
testCreateEmptyWithSameType<QgsMultiLineString>();
qDebug( "createEmptyWithSameType(): QgsMultiPointV2" );
testCreateEmptyWithSameType<QgsMultiPointV2>();
qDebug( "createEmptyWithSameType(): QgsMultiSurface" );
testCreateEmptyWithSameType<QgsMultiSurface>();
qDebug( "createEmptyWithSameType(): QgsPoint" );
testCreateEmptyWithSameType<QgsPoint>( false );
qDebug( "createEmptyWithSameType(): QgsCurvePolygon" );
testCreateEmptyWithSameType<QgsCurvePolygon>();
qDebug( "createEmptyWithSameType(): QgsPolygonV2" );
testCreateEmptyWithSameType<QgsPolygonV2>();
qDebug( "createEmptyWithSameType(): QgsTriangle" );
testCreateEmptyWithSameType<QgsTriangle>();
}
// MK, Disabled 14.11.2014
// Too unclear what exactly should be tested and which variations are allowed for the line
@ -15595,6 +15661,95 @@ void TestQgsGeometry::splitGeometry()
QVERIFY( newGeoms.isEmpty() );
}
void TestQgsGeometry::snappedToGrid()
{
qDebug( "SnappedToGrid" );
// points
{
qDebug( "\tPoints:" );
auto check = []( QgsPoint * _a, QgsPoint const & b )
{
std::unique_ptr<QgsPoint> a {_a};
// because it is to check after snapping, there shouldn't be small precision errors
qDebug( "\t\tGridified point: %f, %f, %f, %f", a->x(), a->y(), a->z(), a->m() );
qDebug( "\t\tExpected point: %f, %f, %f, %f", b.x(), b.y(), b.z(), b.m() );
if ( !std::isnan( b.x() ) )
QVERIFY( ( float )a->x() == ( float )b.x() );
if ( !std::isnan( b.y() ) )
QVERIFY( ( float )a->y() == ( float )b.y() );
if ( !std::isnan( b.z() ) )
QVERIFY( ( float )a->z() == ( float )b.z() );
if ( !std::isnan( b.m() ) )
QVERIFY( ( float )a->m() == ( float )b.m() );
};
check( QgsPoint( 0, 0 ).snappedToGrid( 1, 1 ),
QgsPoint( 0, 0 ) );
check( QgsPoint( 1, 2.732 ).snappedToGrid( 1, 1 ),
QgsPoint( 1, 3 ) );
check( QgsPoint( 1.3, 6.4 ).snappedToGrid( 1, 1 ),
QgsPoint( 1, 6 ) );
check( QgsPoint( 1.3, 6.4 ).snappedToGrid( 1, 0 ),
QgsPoint( 1, 6.4 ) );
// multiple checks with the same point
auto p1 = QgsPoint( 1.38, 2.4432 );
check( p1.snappedToGrid( 1, 1 ),
QgsPoint( 1, 2 ) );
check( p1.snappedToGrid( 1, 0.1 ),
QgsPoint( 1, 2.4 ) );
check( p1.snappedToGrid( 1, 0.01 ),
QgsPoint( 1, 2.44 ) );
// Let's test more dimensions
auto p2 = QgsPoint( 4.2134212, 543.1231, 0.123, 12.944145 );
check( p2.snappedToGrid( 0, 0, 0, 0 ),
p2 );
check( p2.snappedToGrid( 0, 0, 1, 1 ),
QgsPoint( 4.2134212, 543.1231, 0, 13 ) );
check( p2.snappedToGrid( 1, 0.1, 0.01, 0.001 ),
QgsPoint( 4, 543.1, 0.12, 12.944 ) );
}
// MultiPolygon (testing QgsCollection, QgsCurvePolygon and QgsLineString)
{
/*
* List of tested edge cases:
*
* - QgsLineString becoming a point
* - QgsLineString losing enough points so it is no longer closed
* - QgsCurvePolygon losing its external ring
* - QgsCurvePolygon losing an internal ring
* - QgsCurvePolygon losing all internal rings
* - QgsCollection losing one of its members
* - QgsCollection losing all its members
*/
auto in = QString( "MultiPolygon (((-1.2 -0.87, -0.943 0.8, 0.82 1.4, 1.2 0.9, 0.9 -0.6, -1.2 -0.87),(0.4 0, -0.4 0, 0 0.2, 0.4 0)),((2 0, 2.2 0.2, 2.2 -0.2)),((3 0, 4 0.2, 4 -0.2, 3 0)),((4 8, 3.6 4.1, 0.3 4.9, -0.2 7.8, 4 8),(6.7 7.3, 7 6.4, 5.6 5.9, 6.2 6.8, 6.7 7.3),(6 5.2, 4.9 5.3, 4.8 6.2, 6 5.2)))" );
auto out = QString( "MultiPolygon (((-1 -1, -1 1, 1 1, 1 -1, -1 -1)),((4 8, 4 4, 0 5, 0 8, 4 8),(7 7, 7 6, 6 6, 6 7, 7 7),(6 5, 5 5, 5 6, 6 5)))" );
auto inGeom = QgsGeometryFactory::geomFromWkt( in );
std::unique_ptr<QgsAbstractGeometry> snapped { inGeom->snappedToGrid( 1, 1 ) };
QCOMPARE( snapped->asWkt(), out );
}
}
QGSTEST_MAIN( TestQgsGeometry )
#include "testqgsgeometry.moc"