Return enum instead of int from QgsGeometry operations

This commit is contained in:
Denis Rouzaud 2016-08-23 13:25:58 +02:00 committed by Matthias Kuhn
parent 8d615543b7
commit 86e8da74cb
No known key found for this signature in database
GPG Key ID: A0E766808764D73F
13 changed files with 593 additions and 471 deletions

View File

@ -44,6 +44,23 @@ class QgsGeometry
#include "qgsgeometry.h"
%End
public:
enum OperationResult
{
Success,
NothingHappened,
InvalidBaseGeometry,
InvalidInput,
GeometryEngineError,
AddPartSelectedGeometryNotFound,
AddPartNotMultiGeometry,
AddRingNotClosed,
AddRingNotValid,
AddRingCrossesExistingRings,
AddRingNotInExistingFeature,
SplitCannotSplitPoint,
};
QgsGeometry();
%Docstring
Constructor
@ -380,63 +397,58 @@ Returns true if WKB of the geometry is of WKBMulti* type
:rtype: float
%End
int addRing( const QList<QgsPointXY> &ring );
OperationResult addRing( const QList<QgsPointXY> &ring );
%Docstring
Adds a new ring to this geometry. This makes only sense for polygon and multipolygons.
:return: 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed,
3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*
:rtype: int
\param ring The ring to be added
:return: OperationResult a result code: success or reason of failure
:rtype: OperationResult
%End
int addRing( QgsCurve *ring /Transfer/ );
OperationResult addRing( QgsCurve *ring /Transfer/ );
%Docstring
Adds a new ring to this geometry. This makes only sense for polygon and multipolygons.
:return: 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed,
3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*
:rtype: int
\param ring The ring to be added
:return: OperationResult a result code: success or reason of failure
:rtype: OperationResult
%End
int addPart( const QList<QgsPointXY> &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) /PyName=addPoints/;
OperationResult addPart( const QList<QgsPointXY> &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) /PyName=addPoints/;
%Docstring
Adds a new part to a the geometry.
\param points points describing part to add
\param geomType default geometry type to create if no existing geometry
:return: 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
not disjoint with existing polygons of the feature
:rtype: int
:return: OperationResult a result code: success or reason of failure
:rtype: OperationResult
%End
int addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) /PyName=addPointsV2/;
OperationResult addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) /PyName=addPointsV2/;
%Docstring
Adds a new part to a the geometry.
\param points points describing part to add
\param geomType default geometry type to create if no existing geometry
:return: 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
not disjoint with existing polygons of the feature
:rtype: int
:return: OperationResult a result code: success or reason of failure
:rtype: OperationResult
%End
int addPart( QgsAbstractGeometry *part /Transfer/, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry );
OperationResult addPart( QgsAbstractGeometry *part /Transfer/, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry );
%Docstring
Adds a new part to this geometry.
\param part part to add (ownership is transferred)
\param geomType default geometry type to create if no existing geometry
:return: 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
not disjoint with existing polygons of the feature
:rtype: int
:return: OperationResult a result code: success or reason of failure
:rtype: OperationResult
%End
int addPart( const QgsGeometry &newPart ) /PyName=addPartGeometry/;
OperationResult addPart( const QgsGeometry &newPart ) /PyName=addPartGeometry/;
%Docstring
Adds a new island polygon to a multipolygon feature
:return: 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
not disjoint with existing polygons of the feature
:return: OperationResult a result code: success or reason of failure
.. note::
available in Python bindings as addPartGeometry
.. versionadded:: 2.2
:rtype: int
available in python bindings as addPartGeometry
:rtype: OperationResult
%End
QgsGeometry removeInteriorRings( double minimumAllowedArea = -1 ) const;
@ -448,52 +460,52 @@ not disjoint with existing polygons of the feature
:rtype: QgsGeometry
%End
int translate( double dx, double dy );
OperationResult translate( double dx, double dy );
%Docstring
Translate this geometry by dx, dy
:return: 0 in case of success*
:rtype: int
:return: OperationResult a result code: success or reason of failure
:rtype: OperationResult
%End
int transform( const QgsCoordinateTransform &ct );
OperationResult transform( const QgsCoordinateTransform &ct );
%Docstring
Transform this geometry as described by CoordinateTransform ct
:return: 0 in case of success*
:rtype: int
:return: OperationResult a result code: success or reason of failure
:rtype: OperationResult
%End
int transform( const QTransform &ct );
OperationResult transform( const QTransform &ct );
%Docstring
Transform this geometry as described by QTransform ct
.. versionadded:: 2.8
:return: 0 in case of success*
:rtype: int
:return: OperationResult a result code: success or reason of failure
:rtype: OperationResult
%End
int rotate( double rotation, const QgsPointXY &center );
OperationResult rotate( double rotation, const QgsPointXY &center );
%Docstring
Rotate this geometry around the Z axis
.. versionadded:: 2.8
\param rotation clockwise rotation in degrees
\param center rotation center
:return: 0 in case of success*
:rtype: int
\param rotation clockwise rotation in degrees
\param center rotation center
:return: OperationResult a result code: success or reason of failure
:rtype: OperationResult
%End
int splitGeometry( const QList<QgsPointXY> &splitLine,
QList<QgsGeometry> &newGeometries /Out/,
bool topological,
QList<QgsPointXY> &topologyTestPoints /Out/ );
OperationResult splitGeometry( const QList<QgsPointXY> &splitLine, QList<QgsGeometry> &newGeometries, bool topological, QList<QgsPointXY> &topologyTestPoints );
%Docstring
:rtype: int
Splits this geometry according to a given line.
\param splitLine the line that splits the geometry
\param[out] newGeometries list of new geometries that have been created with the split
\param topological true if topological editing is enabled
\param[out] topologyTestPoints points that need to be tested for topological completeness in the dataset
:return: OperationResult a result code: success or reason of failure
:rtype: OperationResult
%End
int reshapeGeometry( const QgsLineString &reshapeLineString );
OperationResult reshapeGeometry( const QgsLineString &reshapeLineString );
%Docstring
Replaces a part of this geometry with another line
:return: 0 in case of success
.. versionadded:: 1.3
:rtype: int
:return: OperationResult a result code: success or reason of failure
:rtype: OperationResult
%End

View File

@ -21,6 +21,19 @@ class QgsGeometryEngine
#include "qgsgeometryengine.h"
%End
public:
enum EngineOperationResult
{
Success,
NothingHappened,
MethodNotImplemented,
EngineError,
NodedGeometryError,
InvalidBaseGeometry,
InvalidInput,
SplitCannotSplitPoint,
};
virtual ~QgsGeometryEngine();
virtual void geometryChanged() = 0;
@ -230,12 +243,12 @@ class QgsGeometryEngine
:rtype: bool
%End
virtual int splitGeometry( const QgsLineString &splitLine,
QList<QgsAbstractGeometry *> &newGeometries,
bool topological,
QgsPointSequence &topologyTestPoints, QString *errorMsg = 0 ) const;
virtual QgsGeometryEngine::EngineOperationResult splitGeometry( const QgsLineString &splitLine,
QList<QgsAbstractGeometry *> &newGeometries,
bool topological,
QgsPointSequence &topologyTestPoints, QString *errorMsg = 0 ) const;
%Docstring
:rtype: int
:rtype: QgsGeometryEngine.EngineOperationResult
%End
virtual QgsAbstractGeometry *offsetCurve( double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg = 0 ) const = 0 /Factory/;

View File

@ -63,29 +63,51 @@ class QgsVectorLayerEditUtils
:rtype: QgsVectorLayer.EditResult
%End
int addRing( const QList<QgsPointXY> &ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = 0 );
QgsGeometry::OperationResult addRing( const QList<QgsPointXY> &ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = 0 );
%Docstring
:rtype: int
Adds a ring to polygon/multipolygon features
@param ring ring to add
@param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise
all intersecting features are tested and the ring is added to the first valid feature.
@param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter
@return OperationResult result code: success or reason of failure
:rtype: QgsGeometry.OperationResult
%End
int addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = 0 ) /PyName=addCurvedRing/;
QgsGeometry::OperationResult addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = 0 ) /PyName=addCurvedRing/;
%Docstring
:rtype: int
Adds a ring to polygon/multipolygon features
@param ring ring to add
@param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise
all intersecting features are tested and the ring is added to the first valid feature.
@param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter
@return OperationResult result code: success or reason of failure
.. note::
available in python bindings as addCurvedRing
:rtype: QgsGeometry.OperationResult
%End
int addPart( const QList<QgsPointXY> &ring, QgsFeatureId featureId );
QgsGeometry::OperationResult addPart( const QList<QgsPointXY> &ring, QgsFeatureId featureId );
%Docstring
:rtype: int
Adds a new part polygon to a multipart feature
@returns QgsGeometry.OperationResult a result code: success or reason of failure
:rtype: QgsGeometry.OperationResult
%End
int addPart( const QgsPointSequence &ring, QgsFeatureId featureId );
QgsGeometry::OperationResult addPart( const QgsPointSequence &ring, QgsFeatureId featureId );
%Docstring
:rtype: int
Adds a new part polygon to a multipart feature
@returns QgsGeometry.OperationResult a result code: success or reason of failure
.. note::
available in python bindings as addPartV2
:rtype: QgsGeometry.OperationResult
%End
int addPart( QgsCurve *ring, QgsFeatureId featureId ) /PyName=addCurvedPart/;
QgsGeometry::OperationResult addPart( QgsCurve *ring, QgsFeatureId featureId ) /PyName=addCurvedPart/;
%Docstring
:rtype: int
:rtype: QgsGeometry.OperationResult
%End
int translateFeature( QgsFeatureId featureId, double dx, double dy );
@ -98,14 +120,14 @@ class QgsVectorLayerEditUtils
:rtype: int
%End
int splitParts( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
QgsGeometry::OperationResult splitParts( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
%Docstring
:rtype: int
:rtype: QgsGeometry.OperationResult
%End
int splitFeatures( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
QgsGeometry::OperationResult splitFeatures( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
%Docstring
:rtype: int
:rtype: QgsGeometry.OperationResult
%End
int addTopologicalPoints( const QgsGeometry &geom );
@ -130,15 +152,6 @@ class QgsVectorLayerEditUtils
:rtype: int
%End
protected:
int boundingBoxFromPointList( const QList<QgsPointXY> &list, double &xmin, double &ymin, double &xmax, double &ymax ) const;
%Docstring
Little helper function that gives bounding box from a list of points.
:return: 0 in case of success *
:rtype: int
%End
};
/************************************************************************

View File

@ -626,7 +626,7 @@ double QgsGeometry::closestSegmentWithContext(
return sqrDist;
}
int QgsGeometry::addRing( const QList<QgsPointXY> &ring )
QgsGeometry::OperationResult QgsGeometry::addRing( const QList<QgsPointXY> &ring )
{
detach( true );
@ -634,12 +634,12 @@ int QgsGeometry::addRing( const QList<QgsPointXY> &ring )
return addRing( ringLine );
}
int QgsGeometry::addRing( QgsCurve *ring )
QgsGeometry::OperationResult QgsGeometry::addRing( QgsCurve *ring )
{
if ( !d->geometry )
{
delete ring;
return 1;
return InvalidInput;
}
detach( true );
@ -647,14 +647,14 @@ int QgsGeometry::addRing( QgsCurve *ring )
return QgsGeometryEditUtils::addRing( d->geometry, ring );
}
int QgsGeometry::addPart( const QList<QgsPointXY> &points, QgsWkbTypes::GeometryType geomType )
QgsGeometry::OperationResult QgsGeometry::addPart( const QList<QgsPointXY> &points, QgsWkbTypes::GeometryType geomType )
{
QgsPointSequence l;
convertPointList( points, l );
return addPart( l, geomType );
}
int QgsGeometry::addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType )
QgsGeometry::OperationResult QgsGeometry::addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType )
{
QgsAbstractGeometry *partGeom = nullptr;
if ( points.size() == 1 )
@ -670,7 +670,7 @@ int QgsGeometry::addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryT
return addPart( partGeom, geomType );
}
int QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType geomType )
QgsGeometry::OperationResult QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType geomType )
{
if ( !d->geometry )
{
@ -687,7 +687,7 @@ int QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType g
d->geometry = new QgsMultiPolygonV2();
break;
default:
return 1;
return QgsGeometry::AddPartNotMultiGeometry;
}
}
else
@ -699,11 +699,15 @@ int QgsGeometry::addPart( QgsAbstractGeometry *part, QgsWkbTypes::GeometryType g
return QgsGeometryEditUtils::addPart( d->geometry, part );
}
int QgsGeometry::addPart( const QgsGeometry &newPart )
QgsGeometry::OperationResult QgsGeometry::addPart( const QgsGeometry &newPart )
{
if ( !d->geometry || !newPart.d || !newPart.d->geometry )
if ( !d->geometry )
{
return 1;
return QgsGeometry::InvalidBaseGeometry;
}
if ( !newPart.d || !newPart.d->geometry )
{
return QgsGeometry::AddPartNotMultiGeometry;
}
return addPart( newPart.d->geometry->clone() );
@ -744,11 +748,15 @@ QgsGeometry QgsGeometry::removeInteriorRings( double minimumRingArea ) const
}
}
int QgsGeometry::addPart( GEOSGeometry *newPart )
QgsGeometry::OperationResult QgsGeometry::addPart( GEOSGeometry *newPart )
{
if ( !d->geometry || !newPart )
if ( !d->geometry )
{
return 1;
return QgsGeometry::InvalidBaseGeometry;
}
if ( !newPart )
{
return QgsGeometry::AddPartNotMultiGeometry;
}
detach( true );
@ -757,24 +765,24 @@ int QgsGeometry::addPart( GEOSGeometry *newPart )
return QgsGeometryEditUtils::addPart( d->geometry, geom );
}
int QgsGeometry::translate( double dx, double dy )
QgsGeometry::OperationResult QgsGeometry::translate( double dx, double dy )
{
if ( !d->geometry )
{
return 1;
return QgsGeometry::InvalidBaseGeometry;
}
detach( true );
d->geometry->transform( QTransform::fromTranslate( dx, dy ) );
return 0;
return QgsGeometry::Success;
}
int QgsGeometry::rotate( double rotation, const QgsPointXY &center )
QgsGeometry::OperationResult QgsGeometry::rotate( double rotation, const QgsPointXY &center )
{
if ( !d->geometry )
{
return 1;
return QgsGeometry::InvalidBaseGeometry;
}
detach( true );
@ -783,14 +791,14 @@ int QgsGeometry::rotate( double rotation, const QgsPointXY &center )
t.rotate( -rotation );
t.translate( -center.x(), -center.y() );
d->geometry->transform( t );
return 0;
return QgsGeometry::Success;
}
int QgsGeometry::splitGeometry( const QList<QgsPointXY> &splitLine, QList<QgsGeometry> &newGeometries, bool topological, QList<QgsPointXY> &topologyTestPoints )
QgsGeometry::OperationResult QgsGeometry::splitGeometry( const QList<QgsPointXY> &splitLine, QList<QgsGeometry> &newGeometries, bool topological, QList<QgsPointXY> &topologyTestPoints )
{
if ( !d->geometry )
{
return 0;
return InvalidBaseGeometry;
}
QList<QgsAbstractGeometry *> newGeoms;
@ -798,9 +806,9 @@ int QgsGeometry::splitGeometry( const QList<QgsPointXY> &splitLine, QList<QgsGeo
QgsPointSequence tp;
QgsGeos geos( d->geometry );
int result = geos.splitGeometry( splitLineString, newGeoms, topological, tp );
QgsGeometryEngine::EngineOperationResult result = geos.splitGeometry( splitLineString, newGeoms, topological, tp );
if ( result == 0 )
if ( result == QgsGeometryEngine::Success )
{
detach( false );
d->geometry = newGeoms.at( 0 );
@ -813,27 +821,69 @@ int QgsGeometry::splitGeometry( const QList<QgsPointXY> &splitLine, QList<QgsGeo
}
convertPointList( tp, topologyTestPoints );
return result;
switch ( result )
{
case QgsGeometryEngine::Success:
return QgsGeometry::Success;
case QgsGeometryEngine::MethodNotImplemented:
case QgsGeometryEngine::EngineError:
case QgsGeometryEngine::NodedGeometryError:
return QgsGeometry::GeometryEngineError;
case QgsGeometryEngine::InvalidBaseGeometry:
return QgsGeometry::InvalidBaseGeometry;
case QgsGeometryEngine::InvalidInput:
return QgsGeometry::InvalidInput;
case QgsGeometryEngine::SplitCannotSplitPoint:
return QgsGeometry::SplitCannotSplitPoint;
case QgsGeometryEngine::NothingHappened:
return QgsGeometry::NothingHappened;
//default: do not implement default to handle properly all cases
}
// this should never be reached
Q_ASSERT( false );
return QgsGeometry::NothingHappened;
}
int QgsGeometry::reshapeGeometry( const QgsLineString &reshapeLineString )
QgsGeometry::OperationResult QgsGeometry::reshapeGeometry( const QgsLineString &reshapeLineString )
{
if ( !d->geometry )
{
return 0;
return InvalidBaseGeometry;
}
QgsGeos geos( d->geometry );
int errorCode = 0;
QgsGeometryEngine::EngineOperationResult errorCode = QgsGeometryEngine::Success;
QgsAbstractGeometry *geom = geos.reshapeGeometry( reshapeLineString, &errorCode );
if ( errorCode == 0 && geom )
if ( errorCode == QgsGeometryEngine::Success && geom )
{
detach( false );
delete d->geometry;
d->geometry = geom;
return 0;
return Success;
}
return errorCode;
switch ( errorCode )
{
case QgsGeometryEngine::Success:
return Success;
case QgsGeometryEngine::MethodNotImplemented:
case QgsGeometryEngine::EngineError:
case QgsGeometryEngine::NodedGeometryError:
return GeometryEngineError;
case QgsGeometryEngine::InvalidBaseGeometry:
return InvalidBaseGeometry;
case QgsGeometryEngine::InvalidInput:
return InvalidInput;
case QgsGeometryEngine::SplitCannotSplitPoint: // should not happen
return GeometryEngineError;
case QgsGeometryEngine::NothingHappened:
return NothingHappened;
}
// should not be reached
return GeometryEngineError;
}
int QgsGeometry::makeDifferenceInPlace( const QgsGeometry &other )
@ -2091,28 +2141,28 @@ bool QgsGeometry::requiresConversionToStraightSegments() const
return d->geometry->hasCurvedSegments();
}
int QgsGeometry::transform( const QgsCoordinateTransform &ct )
QgsGeometry::OperationResult QgsGeometry::transform( const QgsCoordinateTransform &ct )
{
if ( !d->geometry )
{
return 1;
return QgsGeometry::InvalidBaseGeometry;
}
detach();
d->geometry->transform( ct );
return 0;
return QgsGeometry::Success;
}
int QgsGeometry::transform( const QTransform &ct )
QgsGeometry::OperationResult QgsGeometry::transform( const QTransform &ct )
{
if ( !d->geometry )
{
return 1;
return QgsGeometry::InvalidBaseGeometry;
}
detach();
d->geometry->transform( ct );
return 0;
return QgsGeometry::Success;
}
void QgsGeometry::mapToPixel( const QgsMapToPixel &mtp )

View File

@ -96,6 +96,30 @@ struct QgsGeometryPrivate;
class CORE_EXPORT QgsGeometry
{
public:
/**
* Success or failure of a geometry operation.
* This gived details about cause of failure.
*/
enum OperationResult
{
Success = 0, //!< Operation succeeded
NothingHappened, //!< Nothing happened, without any error
InvalidBaseGeometry, //!< The base geometry on which the operation is done is invalid or empty
InvalidInput, //!< The input geometry (ring, part, split line, etc.) has not the correct geometry type
GeometryEngineError, //!< Geometry engine misses a method implemented or an error occured in the geometry engine
/* Add part issues */
AddPartSelectedGeometryNotFound, //!< The selected geometry cannot be found
AddPartNotMultiGeometry, //!< The source geometry is not multi
/* Add ring issues*/
AddRingNotClosed, //!< The imput ring is not closed
AddRingNotValid, //!< The input ring is not valid
AddRingCrossesExistingRings, //!< The input ring crosses existing rings (it is not disjoint)
AddRingNotInExistingFeature, //!< The input ring doesn't have any existing ring to fit into
/* Split features */
SplitCannotSplitPoint, //!< Cannot split points
};
//! Constructor
QgsGeometry();
@ -390,62 +414,58 @@ class CORE_EXPORT QgsGeometry
double closestSegmentWithContext( const QgsPointXY &point, QgsPointXY &minDistPoint SIP_OUT, int &afterVertex SIP_OUT ) const;
#endif
/** Adds a new ring to this geometry. This makes only sense for polygon and multipolygons.
\returns 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed,
3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*/
int addRing( const QList<QgsPointXY> &ring );
// TODO QGIS 3.0 returns an enum instead of a magic constant
/**
* Adds a new ring to this geometry. This makes only sense for polygon and multipolygons.
* \param ring The ring to be added
* \returns OperationResult a result code: success or reason of failure
*/
OperationResult addRing( const QList<QgsPointXY> &ring );
/** Adds a new ring to this geometry. This makes only sense for polygon and multipolygons.
\returns 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed,
3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*/
int addRing( QgsCurve *ring SIP_TRANSFER );
// TODO QGIS 3.0 returns an enum instead of a magic constant
/**
* Adds a new ring to this geometry. This makes only sense for polygon and multipolygons.
* \param ring The ring to be added
* \returns OperationResult a result code: success or reason of failure
*/
OperationResult addRing( QgsCurve *ring SIP_TRANSFER );
/** Adds a new part to a the geometry.
/**
* Adds a new part to a the geometry.
* \param points points describing part to add
* \param geomType default geometry type to create if no existing geometry
* \returns 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
* not disjoint with existing polygons of the feature
* \returns OperationResult a result code: success or reason of failure
*/
int addPart( const QList<QgsPointXY> &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) SIP_PYNAME( addPoints );
// TODO QGIS 3.0 returns an enum instead of a magic constant
OperationResult addPart( const QList<QgsPointXY> &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) SIP_PYNAME( addPoints );
/** Adds a new part to a the geometry.
/**
* Adds a new part to a the geometry.
* \param points points describing part to add
* \param geomType default geometry type to create if no existing geometry
* \returns 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
* not disjoint with existing polygons of the feature
* \returns OperationResult a result code: success or reason of failure
*/
int addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) SIP_PYNAME( addPointsV2 );
// TODO QGIS 3.0 returns an enum instead of a magic constant
OperationResult addPart( const QgsPointSequence &points, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry ) SIP_PYNAME( addPointsV2 );
/** Adds a new part to this geometry.
/**
* Adds a new part to this geometry.
* \param part part to add (ownership is transferred)
* \param geomType default geometry type to create if no existing geometry
* \returns 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
* not disjoint with existing polygons of the feature
* \returns OperationResult a result code: success or reason of failure
*/
int addPart( QgsAbstractGeometry *part SIP_TRANSFER, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry );
// TODO QGIS 3.0 returns an enum instead of a magic constant
OperationResult addPart( QgsAbstractGeometry *part SIP_TRANSFER, QgsWkbTypes::GeometryType geomType = QgsWkbTypes::UnknownGeometry );
/** Adds a new island polygon to a multipolygon feature
/**
* Adds a new island polygon to a multipolygon feature
* \param newPart part to add. Ownership is NOT transferred.
* \returns 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
* not disjoint with existing polygons of the feature
* \note not available in Python bindings
* \returns OperationResult a result code: success or reason of failure
* \note not available in python bindings
*/
int addPart( GEOSGeometry *newPart ) SIP_SKIP;
// TODO QGIS 3.0 returns an enum instead of a magic constant
OperationResult addPart( GEOSGeometry *newPart ) SIP_SKIP;
/** Adds a new island polygon to a multipolygon feature
\returns 0 in case of success, 1 if not a multipolygon, 2 if ring is not a valid geometry, 3 if new polygon ring
not disjoint with existing polygons of the feature
\note available in Python bindings as addPartGeometry
\since QGIS 2.2
/**
* Adds a new island polygon to a multipolygon feature
* \returns OperationResult a result code: success or reason of failure
* \note available in python bindings as addPartGeometry
*/
int addPart( const QgsGeometry &newPart ) SIP_PYNAME( addPartGeometry );
// TODO QGIS 3.0 returns an enum instead of a magic constant
OperationResult addPart( const QgsGeometry &newPart ) SIP_PYNAME( addPartGeometry );
/**
* Removes the interior rings from a (multi)polygon geometry. If the minimumAllowedArea
@ -455,52 +475,57 @@ class CORE_EXPORT QgsGeometry
*/
QgsGeometry removeInteriorRings( double minimumAllowedArea = -1 ) const;
/** Translate this geometry by dx, dy
\returns 0 in case of success*/
int translate( double dx, double dy );
/** Transform this geometry as described by CoordinateTransform ct
\returns 0 in case of success*/
int transform( const QgsCoordinateTransform &ct );
/** Transform this geometry as described by QTransform ct
\since QGIS 2.8
\returns 0 in case of success*/
int transform( const QTransform &ct );
/** Rotate this geometry around the Z axis
\since QGIS 2.8
\param rotation clockwise rotation in degrees
\param center rotation center
\returns 0 in case of success*/
int rotate( double rotation, const QgsPointXY &center );
/** Splits this geometry according to a given line.
\param splitLine the line that splits the geometry
\param[out] newGeometries list of new geometries that have been created with the split
\param topological true if topological editing is enabled
\param[out] topologyTestPoints points that need to be tested for topological completeness in the dataset
\returns 0 in case of success, 1 if geometry has not been split, error else*/
// TODO QGIS 3.0 returns an enum instead of a magic constant
int splitGeometry( const QList<QgsPointXY> &splitLine,
QList<QgsGeometry> &newGeometries SIP_OUT,
bool topological,
QList<QgsPointXY> &topologyTestPoints SIP_OUT );
/** Replaces a part of this geometry with another line
* \returns 0 in case of success
* \since QGIS 1.3
/**
* Translate this geometry by dx, dy
* \returns OperationResult a result code: success or reason of failure
*/
int reshapeGeometry( const QgsLineString &reshapeLineString );
OperationResult translate( double dx, double dy );
/** Changes this geometry such that it does not intersect the other geometry
/**
* Transform this geometry as described by CoordinateTransform ct
* \returns OperationResult a result code: success or reason of failure
*/
OperationResult transform( const QgsCoordinateTransform &ct );
/**
* Transform this geometry as described by QTransform ct
* \returns OperationResult a result code: success or reason of failure
*/
OperationResult transform( const QTransform &ct );
/**
* Rotate this geometry around the Z axis
* \param rotation clockwise rotation in degrees
* \param center rotation center
* \returns OperationResult a result code: success or reason of failure
*/
OperationResult rotate( double rotation, const QgsPointXY &center );
/**
* Splits this geometry according to a given line.
* \param splitLine the line that splits the geometry
* \param[out] newGeometries list of new geometries that have been created with the split
* \param topological true if topological editing is enabled
* \param[out] topologyTestPoints points that need to be tested for topological completeness in the dataset
* \returns OperationResult a result code: success or reason of failure
*/
OperationResult splitGeometry( const QList<QgsPointXY> &splitLine, QList<QgsGeometry> &newGeometries, bool topological, QList<QgsPointXY> &topologyTestPoints );
/**
* Replaces a part of this geometry with another line
* \returns OperationResult a result code: success or reason of failure
*/
OperationResult reshapeGeometry( const QgsLineString &reshapeLineString );
/**
* Changes this geometry such that it does not intersect the other geometry
* \param other geometry that should not be intersect
* \returns 0 in case of success
* \note Not available in Python
*/
int makeDifferenceInPlace( const QgsGeometry &other ) SIP_SKIP;
/** Returns the geometry formed by modifying this geometry such that it does not
/**
* Returns the geometry formed by modifying this geometry such that it does not
* intersect the other geometry.
* \param other geometry that should not be intersect
* \returns difference geometry, or empty geometry if difference could not be calculated

View File

@ -26,11 +26,12 @@ email : marco.hugentobler at sourcepole dot com
#include "qgsvectorlayer.h"
#include <limits>
int QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *ring )
QgsGeometry::OperationResult QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *r )
{
std::unique_ptr<QgsCurve> ring( r );
if ( !ring )
{
return 1;
return QgsGeometry::InvalidInput;
}
QList< QgsCurvePolygon * > polygonList;
@ -50,23 +51,20 @@ int QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *ring )
}
else
{
delete ring;
return 1; //not polygon / multipolygon;
return QgsGeometry::InvalidInput; //not polygon / multipolygon;
}
//ring must be closed
if ( !ring->isClosed() )
{
delete ring;
return 2;
return QgsGeometry::AddRingNotClosed;
}
else if ( !ring->isRing() )
{
delete ring;
return 3;
return QgsGeometry::AddRingNotValid;
}
std::unique_ptr<QgsGeometryEngine> ringGeom( QgsGeometry::createGeometryEngine( ring ) );
std::unique_ptr<QgsGeometryEngine> ringGeom( QgsGeometry::createGeometryEngine( ring.get() ) );
ringGeom->prepareGeometry();
//for each polygon, test if inside outer ring and no intersection with other interior ring
@ -81,8 +79,7 @@ int QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *ring )
{
if ( !ringGeom->disjoint( ( *polyIter )->interiorRing( i ) ) )
{
delete ring;
return 4;
return QgsGeometry::AddRingCrossesExistingRings;
}
}
@ -92,59 +89,62 @@ int QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, QgsCurve *ring )
if ( QgsWkbTypes::hasM( geom->wkbType() ) )
ring->addMValue( 0 );
( *polyIter )->addInteriorRing( ring );
return 0; //success
( *polyIter )->addInteriorRing( ring.release() );
return QgsGeometry::Success; //success
}
}
delete ring;
return 5; //not contained in any outer ring
return QgsGeometry::AddRingNotInExistingFeature; //not contained in any outer ring
}
int QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, QgsAbstractGeometry *part )
QgsGeometry::OperationResult QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, QgsAbstractGeometry *p )
{
std::unique_ptr<QgsAbstractGeometry> part( p );
if ( !geom )
{
return 1;
return QgsGeometry::InvalidBaseGeometry;
}
if ( !part )
{
return 2;
return QgsGeometry::InvalidInput;
}
//multitype?
QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom );
if ( !geomCollection )
{
return 1;
return QgsGeometry::AddPartNotMultiGeometry;
}
bool added = false;
if ( QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiSurface
|| QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiPolygon )
{
QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( part );
std::unique_ptr<QgsCurve> curve( qgsgeometry_cast<QgsCurve *>( part.get() ) );
if ( curve )
part.release();
if ( curve && curve->isClosed() && curve->numPoints() >= 4 )
{
QgsCurvePolygon *poly = nullptr;
std::unique_ptr<QgsCurvePolygon> poly;
if ( QgsWkbTypes::flatType( curve->wkbType() ) == QgsWkbTypes::LineString )
{
poly = new QgsPolygonV2();
poly.reset( new QgsPolygonV2() );
}
else
{
poly = new QgsCurvePolygon();
poly.reset( new QgsCurvePolygon() );
}
poly->setExteriorRing( curve );
added = geomCollection->addGeometry( poly );
poly->setExteriorRing( curve.release() );
added = geomCollection->addGeometry( poly.release() );
}
else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::Polygon )
{
added = geomCollection->addGeometry( part );
added = geomCollection->addGeometry( part.release() );
}
else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiPolygon )
{
QgsGeometryCollection *parts = static_cast<QgsGeometryCollection *>( part );
std::unique_ptr<QgsGeometryCollection> parts( static_cast<QgsGeometryCollection *>( part.release() ) );
int i;
int n = geomCollection->numGeometries();
@ -156,23 +156,19 @@ int QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, QgsAbstractGeometr
{
while ( geomCollection->numGeometries() > n )
geomCollection->removeGeometry( n );
delete part;
return 2;
return QgsGeometry::InvalidInput;
}
delete part;
}
else
{
delete part;
return 2;
return QgsGeometry::InvalidInput;
}
}
else
{
added = geomCollection->addGeometry( part );
added = geomCollection->addGeometry( part.release() );
}
return added ? 0 : 2;
return added ? QgsGeometry::Success : QgsGeometry::InvalidInput;
}
bool QgsGeometryEditUtils::deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum )

View File

@ -24,6 +24,7 @@ class QgsVectorLayer;
#define SIP_NO_FILE
#include "qgsfeature.h"
#include "qgsgeometry.h"
#include <QMap>
/** \ingroup core
@ -36,19 +37,24 @@ class QgsGeometryEditUtils
{
public:
/** Adds interior ring (taking ownership).
\returns 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed,
3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring*/
// TODO QGIS 3.0 returns an enum instead of a magic constant
static int addRing( QgsAbstractGeometry *geom, QgsCurve *ring );
/**
* Add an interior \a ring to a \a geometry.
* Ownership of the \a ring is transferred.
* \returns 0 in case of success (ring added), 1 problem with geometry type, 2 ring not closed,
* 3 ring is not valid geometry, 4 ring not disjoint with existing rings, 5 no polygon found which contained the ring
*/
static QgsGeometry::OperationResult addRing( QgsAbstractGeometry *geometry, QgsCurve *ring SIP_TRANSFER );
/** Adds part to multi type geometry (taking ownership)
\returns 0 in case of success, 1 if not a multigeometry, 2 if part is not a valid geometry, 3 if new polygon ring
not disjoint with existing polygons of the feature*/
// TODO QGIS 3.0 returns an enum instead of a magic constant
static int addPart( QgsAbstractGeometry *geom, QgsAbstractGeometry *part );
/**
* Add a \a part to multi type \a geometry.
* Ownership of the \a part is transferred.
* \returns 0 in case of success, 1 if not a multigeometry, 2 if part is not a valid geometry, 3 if new polygon ring
* not disjoint with existing polygons of the feature
*/
static QgsGeometry::OperationResult addPart( QgsAbstractGeometry *geometry, QgsAbstractGeometry *part SIP_TRANSFER );
/** Deletes a ring from a geometry.
/**
* Deletes a ring from a geometry.
* \returns true if delete was successful
*/
static bool deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum = 0 );

View File

@ -18,6 +18,7 @@ email : marco.hugentobler at sourcepole dot com
#include "qgis_core.h"
#include "qgslinestring.h"
#include "qgsgeometry.h"
#include <QList>
@ -31,6 +32,24 @@ class QgsAbstractGeometry;
class CORE_EXPORT QgsGeometryEngine
{
public:
/**
* Success or failure of a geometry operation.
* This gived details about cause of failure.
*/
enum EngineOperationResult
{
Success = 0, //!< Operation succeeded
NothingHappened, //!< Nothing happened, without any error
MethodNotImplemented, //!< Method not implemented in geometry engine
EngineError, //!< Error occured in the geometry engine
NodedGeometryError, //!< Error occured while creating a noded geometry
InvalidBaseGeometry, //!< The geometry on which the operation occurs is not valid
InvalidInput, //!< The input is not valid
/* split */
SplitCannotSplitPoint, //!< Points cannot be split
};
virtual ~QgsGeometryEngine() = default;
virtual void geometryChanged() = 0;
@ -190,17 +209,17 @@ class CORE_EXPORT QgsGeometryEngine
*/
virtual bool isSimple( QString *errorMsg = nullptr ) const = 0;
virtual int splitGeometry( const QgsLineString &splitLine,
QList<QgsAbstractGeometry *> &newGeometries,
bool topological,
QgsPointSequence &topologyTestPoints, QString *errorMsg = nullptr ) const
virtual QgsGeometryEngine::EngineOperationResult splitGeometry( const QgsLineString &splitLine,
QList<QgsAbstractGeometry *> &newGeometries,
bool topological,
QgsPointSequence &topologyTestPoints, QString *errorMsg = nullptr ) const
{
Q_UNUSED( splitLine );
Q_UNUSED( newGeometries );
Q_UNUSED( topological );
Q_UNUSED( topologyTestPoints );
Q_UNUSED( errorMsg );
return 2;
return MethodNotImplemented;
}
virtual QgsAbstractGeometry *offsetCurve( double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg = nullptr ) const = 0 SIP_FACTORY;

View File

@ -25,7 +25,6 @@ email : marco.hugentobler at sourcepole dot com
#include "qgsmultipolygon.h"
#include "qgslogger.h"
#include "qgspolygon.h"
#include "qgsgeometry.h"
#include <limits>
#include <cstdio>
#include <QtCore/qmath.h>
@ -519,32 +518,32 @@ double QgsGeos::length( QString *errorMsg ) const
return length;
}
int QgsGeos::splitGeometry( const QgsLineString &splitLine,
QList<QgsAbstractGeometry *> &newGeometries,
bool topological,
QgsPointSequence &topologyTestPoints,
QString *errorMsg ) const
QgsGeometryEngine::EngineOperationResult QgsGeos::splitGeometry( const QgsLineString &splitLine,
QList<QgsAbstractGeometry *> &newGeometries,
bool topological,
QgsPointSequence &topologyTestPoints,
QString *errorMsg ) const
{
int returnCode = 0;
if ( !mGeometry || !mGeos )
EngineOperationResult returnCode = Success;
if ( !mGeos || !mGeometry )
{
return 1;
return InvalidBaseGeometry;
}
//return if this type is point/multipoint
if ( mGeometry->dimension() == 0 )
{
return 1; //cannot split points
return SplitCannotSplitPoint; //cannot split points
}
if ( !GEOSisValid_r( geosinit.ctxt, mGeos ) )
return 7;
return InvalidBaseGeometry;
//make sure splitLine is valid
if ( ( mGeometry->dimension() == 1 && splitLine.numPoints() < 1 ) ||
( mGeometry->dimension() == 2 && splitLine.numPoints() < 2 ) )
return 1;
return InvalidInput;
newGeometries.clear();
GEOSGeometry *splitLineGeos = nullptr;
@ -561,20 +560,22 @@ int QgsGeos::splitGeometry( const QgsLineString &splitLine,
}
else
{
return 1;
return InvalidInput;
}
if ( !GEOSisValid_r( geosinit.ctxt, splitLineGeos ) || !GEOSisSimple_r( geosinit.ctxt, splitLineGeos ) )
{
GEOSGeom_destroy_r( geosinit.ctxt, splitLineGeos );
return 1;
return InvalidInput;
}
if ( topological )
{
//find out candidate points for topological corrections
if ( topologicalTestPointsSplit( splitLineGeos, topologyTestPoints ) != 0 )
return 1;
if ( !topologicalTestPointsSplit( splitLineGeos, topologyTestPoints ) )
{
return InvalidInput; // TODO: is it really an invalid input?
}
}
//call split function depending on geometry type
@ -590,17 +591,17 @@ int QgsGeos::splitGeometry( const QgsLineString &splitLine,
}
else
{
return 1;
return InvalidInput;
}
}
CATCH_GEOS_WITH_ERRMSG( 2 )
CATCH_GEOS_WITH_ERRMSG( EngineError )
return returnCode;
}
int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPointSequence &testPoints, QString *errorMsg ) const
bool QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPointSequence &testPoints, QString *errorMsg ) const
{
//Find out the intersection points between splitLineGeos and this geometry.
//These points need to be tested for topological correctness by the calling function
@ -608,7 +609,7 @@ int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPoint
if ( !mGeos )
{
return 1;
return false;
}
try
@ -616,7 +617,7 @@ int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPoint
testPoints.clear();
GEOSGeometry *intersectionGeom = GEOSIntersection_r( geosinit.ctxt, mGeos, splitLine );
if ( !intersectionGeom )
return 1;
return false;
bool simple = false;
int nIntersectGeoms = 1;
@ -656,7 +657,7 @@ int QgsGeos::topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPoint
}
CATCH_GEOS_WITH_ERRMSG( 1 )
return 0;
return true;
}
GEOSGeometry *QgsGeos::linePointDifference( GEOSGeometry *GEOSsplitPoint ) const
@ -725,22 +726,22 @@ GEOSGeometry *QgsGeos::linePointDifference( GEOSGeometry *GEOSsplitPoint ) const
return asGeos( &lines, mPrecision );
}
int QgsGeos::splitLinearGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const
QgsGeometryEngine::EngineOperationResult QgsGeos::splitLinearGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const
{
if ( !splitLine )
return 2;
return InvalidInput;
if ( !mGeos )
return 5;
return InvalidBaseGeometry;
//first test if linestring intersects geometry. If not, return straight away
if ( !GEOSIntersects_r( geosinit.ctxt, splitLine, mGeos ) )
return 1;
return NothingHappened;
//check that split line has no linear intersection
int linearIntersect = GEOSRelatePattern_r( geosinit.ctxt, mGeos, splitLine, "1********" );
if ( linearIntersect > 0 )
return 3;
return InvalidInput;
int splitGeomType = GEOSGeomTypeId_r( geosinit.ctxt, splitLine );
@ -778,25 +779,25 @@ int QgsGeos::splitLinearGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeom
}
GEOSGeom_destroy_r( geosinit.ctxt, splitGeom );
return 0;
return Success;
}
int QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const
QgsGeometryEngine::EngineOperationResult QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const
{
if ( !splitLine )
return 2;
return InvalidInput;
if ( !mGeos )
return 5;
return InvalidBaseGeometry;
//first test if linestring intersects geometry. If not, return straight away
if ( !GEOSIntersects_r( geosinit.ctxt, splitLine, mGeos ) )
return 1;
return NothingHappened;
//first union all the polygon rings together (to get them noded, see JTS developer guide)
GEOSGeometry *nodedGeometry = nodeGeometries( splitLine, mGeos );
if ( !nodedGeometry )
return 2; //an error occurred during noding
return NodedGeometryError; //an error occurred during noding
GEOSGeometry *polygons = GEOSPolygonize_r( geosinit.ctxt, &nodedGeometry, 1 );
if ( !polygons || numberOfGeometries( polygons ) == 0 )
@ -806,7 +807,7 @@ int QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeo
GEOSGeom_destroy_r( geosinit.ctxt, nodedGeometry );
return 4;
return InvalidBaseGeometry;
}
GEOSGeom_destroy_r( geosinit.ctxt, nodedGeometry );
@ -859,7 +860,7 @@ int QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeo
{
GEOSGeom_destroy_r( geosinit.ctxt, testedGeometries[i] );
}
return 1;
return NothingHappened;
}
int i;
@ -871,13 +872,13 @@ int QgsGeos::splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeo
for ( i = 0; i < testedGeometries.size(); ++i )
GEOSGeom_destroy_r( geosinit.ctxt, testedGeometries[i] );
return 3;
return InvalidBaseGeometry;
}
for ( i = 0; i < testedGeometries.size(); ++i )
newGeometries << fromGeos( testedGeometries[i] );
return 0;
return Success;
}
GEOSGeometry *QgsGeos::nodeGeometries( const GEOSGeometry *splitLine, const GEOSGeometry *geom )
@ -1892,11 +1893,17 @@ QgsAbstractGeometry *QgsGeos::singleSidedBuffer( double distance, int segments,
return fromGeos( geos.get() );
}
QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithLine, int *errorCode, QString *errorMsg ) const
QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithLine, EngineOperationResult *errorCode, QString *errorMsg ) const
{
if ( !mGeos || reshapeWithLine.numPoints() < 2 || mGeometry->dimension() == 0 )
if ( !mGeos || mGeometry->dimension() == 0 )
{
if ( errorCode ) { *errorCode = 1; }
if ( errorCode ) { *errorCode = InvalidBaseGeometry; }
return nullptr;
}
if ( reshapeWithLine.numPoints() < 2 )
{
if ( errorCode ) { *errorCode = InvalidInput; }
return nullptr;
}
@ -1906,7 +1913,7 @@ QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithL
int numGeoms = GEOSGetNumGeometries_r( geosinit.ctxt, mGeos );
if ( numGeoms == -1 )
{
if ( errorCode ) { *errorCode = 1; }
if ( errorCode ) { *errorCode = InvalidBaseGeometry; }
GEOSGeom_destroy_r( geosinit.ctxt, reshapeLineGeos );
return nullptr;
}
@ -1930,7 +1937,8 @@ QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithL
reshapedGeometry = reshapePolygon( mGeos, reshapeLineGeos, mPrecision );
}
if ( errorCode ) { *errorCode = 0; }
if ( errorCode )
*errorCode = Success;
QgsAbstractGeometry *reshapeResult = fromGeos( reshapedGeometry );
GEOSGeom_destroy_r( geosinit.ctxt, reshapedGeometry );
GEOSGeom_destroy_r( geosinit.ctxt, reshapeLineGeos );
@ -1978,13 +1986,14 @@ QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithL
delete[] newGeoms;
if ( !newMultiGeom )
{
if ( errorCode ) { *errorCode = 3; }
if ( errorCode ) { *errorCode = EngineError; }
return nullptr;
}
if ( reshapeTookPlace )
{
if ( errorCode ) { *errorCode = 0; }
if ( errorCode )
*errorCode = Success;
QgsAbstractGeometry *reshapedMultiGeom = fromGeos( newMultiGeom );
GEOSGeom_destroy_r( geosinit.ctxt, newMultiGeom );
return reshapedMultiGeom;
@ -1992,7 +2001,7 @@ QgsAbstractGeometry *QgsGeos::reshapeGeometry( const QgsLineString &reshapeWithL
else
{
GEOSGeom_destroy_r( geosinit.ctxt, newMultiGeom );
if ( errorCode ) { *errorCode = 1; }
if ( errorCode ) { *errorCode = NothingHappened; }
return nullptr;
}
}

View File

@ -20,6 +20,7 @@ email : marco.hugentobler at sourcepole dot com
#include "qgis_core.h"
#include "qgsgeometryengine.h"
#include "qgsgeometry.h"
#include <geos_c.h>
class QgsLineString;
@ -106,11 +107,11 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
\param[out] topologyTestPoints points that need to be tested for topological completeness in the dataset
\param[out] errorMsg error messages emitted, if any
\returns 0 in case of success, 1 if geometry has not been split, error else*/
int splitGeometry( const QgsLineString &splitLine,
QList<QgsAbstractGeometry *> &newGeometries,
bool topological,
QgsPointSequence &topologyTestPoints,
QString *errorMsg = nullptr ) const override;
EngineOperationResult splitGeometry( const QgsLineString &splitLine,
QList<QgsAbstractGeometry *> &newGeometries,
bool topological,
QgsPointSequence &topologyTestPoints,
QString *errorMsg = nullptr ) const override;
QgsAbstractGeometry *offsetCurve( double distance, int segments, int joinStyle, double miterLimit, QString *errorMsg = nullptr ) const override;
@ -131,8 +132,14 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
int joinStyle, double miterLimit,
QString *errorMsg = nullptr ) const;
QgsAbstractGeometry *reshapeGeometry( const QgsLineString &reshapeWithLine, int *errorCode, QString *errorMsg = nullptr ) const;
/**
* Reshapes the geometry using a line
* @param reshapeWithLine the line used to reshape lines or polygons
* @param errorCode if specified, provides result of operation (success or reason of failure)
* @param errorMsg if specified, provides more details about failure
* @return the reshaped geometry
*/
QgsAbstractGeometry *reshapeGeometry( const QgsLineString &reshapeWithLine, EngineOperationResult *errorCode, QString *errorMsg = nullptr ) const;
/** Merges any connected lines in a LineString/MultiLineString geometry and
* converts them to single line strings.
@ -261,10 +268,10 @@ class CORE_EXPORT QgsGeos: public QgsGeometryEngine
static GEOSGeometry *createGeosPolygon( const QgsAbstractGeometry *poly, double precision );
//utils for geometry split
int topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPointSequence &testPoints, QString *errorMsg = nullptr ) const;
bool topologicalTestPointsSplit( const GEOSGeometry *splitLine, QgsPointSequence &testPoints, QString *errorMsg = nullptr ) const;
GEOSGeometry *linePointDifference( GEOSGeometry *GEOSsplitPoint ) const;
int splitLinearGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const;
int splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const;
EngineOperationResult splitLinearGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const;
EngineOperationResult splitPolygonGeometry( GEOSGeometry *splitLine, QList<QgsAbstractGeometry *> &newGeometries ) const;
//utils for reshape
static GEOSGeometry *reshapeLine( const GEOSGeometry *line, const GEOSGeometry *reshapeLineGeos, double precision );

View File

@ -29,41 +29,41 @@
QgsVectorLayerEditUtils::QgsVectorLayerEditUtils( QgsVectorLayer *layer )
: L( layer )
: mLayer( layer )
{
}
bool QgsVectorLayerEditUtils::insertVertex( double x, double y, QgsFeatureId atFeatureId, int beforeVertex )
{
if ( !L->isSpatial() )
if ( !mLayer->isSpatial() )
return false;
QgsFeature f;
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
return false; // geometry not found
QgsGeometry geometry = f.geometry();
geometry.insertVertex( x, y, beforeVertex );
L->editBuffer()->changeGeometry( atFeatureId, geometry );
mLayer->editBuffer()->changeGeometry( atFeatureId, geometry );
return true;
}
bool QgsVectorLayerEditUtils::insertVertex( const QgsPoint &point, QgsFeatureId atFeatureId, int beforeVertex )
{
if ( !L->isSpatial() )
if ( !mLayer->isSpatial() )
return false;
QgsFeature f;
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
return false; // geometry not found
QgsGeometry geometry = f.geometry();
geometry.insertVertex( point, beforeVertex );
L->editBuffer()->changeGeometry( atFeatureId, geometry );
mLayer->editBuffer()->changeGeometry( atFeatureId, geometry );
return true;
}
@ -75,29 +75,29 @@ bool QgsVectorLayerEditUtils::moveVertex( double x, double y, QgsFeatureId atFea
bool QgsVectorLayerEditUtils::moveVertex( const QgsPoint &p, QgsFeatureId atFeatureId, int atVertex )
{
if ( !L->isSpatial() )
if ( !mLayer->isSpatial() )
return false;
QgsFeature f;
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
return false; // geometry not found
QgsGeometry geometry = f.geometry();
geometry.moveVertex( p, atVertex );
L->editBuffer()->changeGeometry( atFeatureId, geometry );
mLayer->editBuffer()->changeGeometry( atFeatureId, geometry );
return true;
}
QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId featureId, int vertex )
{
if ( !L->isSpatial() )
if ( !mLayer->isSpatial() )
return QgsVectorLayer::InvalidLayer;
QgsFeature f;
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
return QgsVectorLayer::FetchFeatureFailed; // geometry not found
QgsGeometry geometry = f.geometry();
@ -111,38 +111,38 @@ QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId f
geometry.setGeometry( nullptr );
}
L->editBuffer()->changeGeometry( featureId, geometry );
mLayer->editBuffer()->changeGeometry( featureId, geometry );
return !geometry.isNull() ? QgsVectorLayer::Success : QgsVectorLayer::EmptyGeometry;
}
int QgsVectorLayerEditUtils::addRing( const QList<QgsPointXY> &ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId )
QgsGeometry::OperationResult QgsVectorLayerEditUtils::addRing( const QList<QgsPointXY> &ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId )
{
QgsLineString *ringLine = new QgsLineString( ring );
return addRing( ringLine, targetFeatureIds, modifiedFeatureId );
}
int QgsVectorLayerEditUtils::addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId )
QgsGeometry::OperationResult QgsVectorLayerEditUtils::addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId )
{
if ( !L->isSpatial() )
if ( !mLayer->isSpatial() )
{
delete ring;
return 5;
return QgsGeometry::AddRingNotInExistingFeature;
}
int addRingReturnCode = 5; //default: return code for 'ring not inserted'
QgsGeometry::OperationResult addRingReturnCode = QgsGeometry::AddRingNotInExistingFeature; //default: return code for 'ring not inserted'
QgsFeature f;
QgsFeatureIterator fit;
if ( !targetFeatureIds.isEmpty() )
{
//check only specified features
fit = L->getFeatures( QgsFeatureRequest().setFilterFids( targetFeatureIds ) );
fit = mLayer->getFeatures( QgsFeatureRequest().setFilterFids( targetFeatureIds ) );
}
else
{
//check all intersecting features
QgsRectangle bBox = ring->boundingBox();
fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
fit = mLayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
}
//find first valid feature we can add the ring to
@ -156,21 +156,22 @@ int QgsVectorLayerEditUtils::addRing( QgsCurve *ring, const QgsFeatureIds &targe
addRingReturnCode = g.addRing( static_cast< QgsCurve * >( ring->clone() ) );
if ( addRingReturnCode == 0 )
{
L->editBuffer()->changeGeometry( f.id(), g );
if ( modifiedFeatureId )
*modifiedFeatureId = f.id();
if ( addRingReturnCode == QgsGeometry::Success )
{
mLayer->editBuffer()->changeGeometry( f.id(), g );
if ( modifiedFeatureId )
*modifiedFeatureId = f.id();
//setModified( true, true );
break;
}
//setModified( true, true );
break;
}
}
delete ring;
return addRingReturnCode;
}
int QgsVectorLayerEditUtils::addPart( const QList<QgsPointXY> &points, QgsFeatureId featureId )
QgsGeometry::OperationResult QgsVectorLayerEditUtils::addPart( const QList<QgsPointXY> &points, QgsFeatureId featureId )
{
QgsPointSequence l;
for ( QList<QgsPointXY>::const_iterator it = points.constBegin(); it != points.constEnd(); ++it )
@ -180,16 +181,16 @@ int QgsVectorLayerEditUtils::addPart( const QList<QgsPointXY> &points, QgsFeatur
return addPart( l, featureId );
}
int QgsVectorLayerEditUtils::addPart( const QgsPointSequence &points, QgsFeatureId featureId )
QgsGeometry::OperationResult QgsVectorLayerEditUtils::addPart( const QgsPointSequence &points, QgsFeatureId featureId )
{
if ( !L->isSpatial() )
return 6;
if ( !mLayer->isSpatial() )
return QgsGeometry::AddPartSelectedGeometryNotFound;
QgsGeometry geometry;
bool firstPart = false;
QgsFeature f;
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) )
return 6; //not found
if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) )
return QgsGeometry::AddPartSelectedGeometryNotFound; //not found
if ( !f.hasGeometry() )
{
@ -201,30 +202,30 @@ int QgsVectorLayerEditUtils::addPart( const QgsPointSequence &points, QgsFeature
geometry = f.geometry();
}
int errorCode = geometry.addPart( points, L->geometryType() ) ;
if ( errorCode == 0 )
QgsGeometry::OperationResult errorCode = geometry.addPart( points, mLayer->geometryType() ) ;
if ( errorCode == QgsGeometry::Success )
{
if ( firstPart && QgsWkbTypes::isSingleType( L->wkbType() )
&& L->dataProvider()->doesStrictFeatureTypeCheck() )
if ( firstPart && QgsWkbTypes::isSingleType( mLayer->wkbType() )
&& mLayer->dataProvider()->doesStrictFeatureTypeCheck() )
{
//convert back to single part if required by layer
geometry.convertToSingleType();
}
L->editBuffer()->changeGeometry( featureId, geometry );
mLayer->editBuffer()->changeGeometry( featureId, geometry );
}
return errorCode;
}
int QgsVectorLayerEditUtils::addPart( QgsCurve *ring, QgsFeatureId featureId )
QgsGeometry::OperationResult QgsVectorLayerEditUtils::addPart( QgsCurve *ring, QgsFeatureId featureId )
{
if ( !L->isSpatial() )
return 6;
if ( !mLayer->isSpatial() )
return QgsGeometry::AddPartSelectedGeometryNotFound;
QgsGeometry geometry;
bool firstPart = false;
QgsFeature f;
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) )
return 6; //not found
if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) )
return QgsGeometry::AddPartSelectedGeometryNotFound;
if ( !f.hasGeometry() )
{
@ -236,16 +237,16 @@ int QgsVectorLayerEditUtils::addPart( QgsCurve *ring, QgsFeatureId featureId )
geometry = f.geometry();
}
int errorCode = geometry.addPart( ring, L->geometryType() ) ;
if ( errorCode == 0 )
QgsGeometry::OperationResult errorCode = geometry.addPart( ring, mLayer->geometryType() ) ;
if ( errorCode == QgsGeometry::Success )
{
if ( firstPart && QgsWkbTypes::isSingleType( L->wkbType() )
&& L->dataProvider()->doesStrictFeatureTypeCheck() )
if ( firstPart && QgsWkbTypes::isSingleType( mLayer->wkbType() )
&& mLayer->dataProvider()->doesStrictFeatureTypeCheck() )
{
//convert back to single part if required by layer
geometry.convertToSingleType();
}
L->editBuffer()->changeGeometry( featureId, geometry );
mLayer->editBuffer()->changeGeometry( featureId, geometry );
}
return errorCode;
}
@ -253,11 +254,11 @@ int QgsVectorLayerEditUtils::addPart( QgsCurve *ring, QgsFeatureId featureId )
int QgsVectorLayerEditUtils::translateFeature( QgsFeatureId featureId, double dx, double dy )
{
if ( !L->isSpatial() )
if ( !mLayer->isSpatial() )
return 1;
QgsFeature f;
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
return 1; //geometry not found
QgsGeometry geometry = f.geometry();
@ -265,30 +266,30 @@ int QgsVectorLayerEditUtils::translateFeature( QgsFeatureId featureId, double dx
int errorCode = geometry.translate( dx, dy );
if ( errorCode == 0 )
{
L->editBuffer()->changeGeometry( featureId, geometry );
mLayer->editBuffer()->changeGeometry( featureId, geometry );
}
return errorCode;
}
int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPointXY> &splitLine, bool topologicalEditing )
QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPointXY> &splitLine, bool topologicalEditing )
{
if ( !L->isSpatial() )
return 4;
if ( !mLayer->isSpatial() )
return QgsGeometry::InvalidBaseGeometry;
QgsFeatureList newFeatures; //store all the newly created features
double xMin, yMin, xMax, yMax;
QgsRectangle bBox; //bounding box of the split line
int returnCode = 0;
int splitFunctionReturn; //return code of QgsGeometry::splitGeometry
QgsGeometry::OperationResult returnCode = QgsGeometry::Success;
QgsGeometry::OperationResult splitFunctionReturn; //return code of QgsGeometry::splitGeometry
int numberOfSplitFeatures = 0;
QgsFeatureIterator features;
const QgsFeatureIds selectedIds = L->selectedFeatureIds();
const QgsFeatureIds selectedIds = mLayer->selectedFeatureIds();
if ( !selectedIds.isEmpty() ) //consider only the selected features if there is a selection
{
features = L->getSelectedFeatures();
features = mLayer->getSelectedFeatures();
}
else //else consider all the feature that intersect the bounding box of the split line
{
@ -301,7 +302,7 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPointXY> &splitLine,
}
else
{
return 1;
return QgsGeometry::InvalidInput;
}
if ( bBox.isEmpty() )
@ -321,7 +322,7 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPointXY> &splitLine,
{
//If we have a single point, we still create a non-null box
double bufferDistance = 0.000001;
if ( L->crs().isGeographic() )
if ( mLayer->crs().isGeographic() )
bufferDistance = 0.00000001;
bBox.setXMinimum( bBox.xMinimum() - bufferDistance );
bBox.setXMaximum( bBox.xMaximum() + bufferDistance );
@ -330,7 +331,7 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPointXY> &splitLine,
}
}
features = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
features = mLayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
}
QgsFeature feat;
@ -344,16 +345,16 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPointXY> &splitLine,
QList<QgsPointXY> topologyTestPoints;
QgsGeometry featureGeom = feat.geometry();
splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints );
if ( splitFunctionReturn == 0 )
if ( splitFunctionReturn == QgsGeometry::Success )
{
//change this geometry
L->editBuffer()->changeGeometry( feat.id(), featureGeom );
mLayer->editBuffer()->changeGeometry( feat.id(), featureGeom );
//insert new features
for ( int i = 0; i < newGeometries.size(); ++i )
{
QgsFeature f = QgsVectorLayerUtils::createFeature( L, newGeometries.at( i ), feat.attributes().toMap() );
L->editBuffer()->addFeature( f );
QgsFeature f = QgsVectorLayerUtils::createFeature( mLayer, newGeometries.at( i ), feat.attributes().toMap() );
mLayer->editBuffer()->addFeature( f );
}
if ( topologicalEditing )
@ -366,7 +367,7 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPointXY> &splitLine,
}
++numberOfSplitFeatures;
}
else if ( splitFunctionReturn > 1 ) //1 means no split but also no error
else if ( splitFunctionReturn != QgsGeometry::Success && splitFunctionReturn != QgsGeometry::NothingHappened ) // i.e. no split but no error occurred
{
returnCode = splitFunctionReturn;
}
@ -376,28 +377,28 @@ int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPointXY> &splitLine,
{
//There is a selection but no feature has been split.
//Maybe user forgot that only the selected features are split
returnCode = 4;
returnCode = QgsGeometry::NothingHappened;
}
return returnCode;
}
int QgsVectorLayerEditUtils::splitParts( const QList<QgsPointXY> &splitLine, bool topologicalEditing )
QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitParts( const QList<QgsPointXY> &splitLine, bool topologicalEditing )
{
if ( !L->isSpatial() )
return 4;
if ( !mLayer->isSpatial() )
return QgsGeometry::InvalidBaseGeometry;
double xMin, yMin, xMax, yMax;
QgsRectangle bBox; //bounding box of the split line
int returnCode = 0;
int splitFunctionReturn; //return code of QgsGeometry::splitGeometry
QgsGeometry::OperationResult returnCode = QgsGeometry::Success;
QgsGeometry::OperationResult splitFunctionReturn; //return code of QgsGeometry::splitGeometry
int numberOfSplitParts = 0;
QgsFeatureIterator fit;
if ( L->selectedFeatureCount() > 0 ) //consider only the selected features if there is a selection
if ( mLayer->selectedFeatureCount() > 0 ) //consider only the selected features if there is a selection
{
fit = L->getSelectedFeatures();
fit = mLayer->getSelectedFeatures();
}
else //else consider all the feature that intersect the bounding box of the split line
{
@ -410,7 +411,7 @@ int QgsVectorLayerEditUtils::splitParts( const QList<QgsPointXY> &splitLine, boo
}
else
{
return 1;
return QgsGeometry::InvalidInput;
}
if ( bBox.isEmpty() )
@ -430,7 +431,7 @@ int QgsVectorLayerEditUtils::splitParts( const QList<QgsPointXY> &splitLine, boo
{
//If we have a single point, we still create a non-null box
double bufferDistance = 0.000001;
if ( L->crs().isGeographic() )
if ( mLayer->crs().isGeographic() )
bufferDistance = 0.00000001;
bBox.setXMinimum( bBox.xMinimum() - bufferDistance );
bBox.setXMaximum( bBox.xMaximum() + bufferDistance );
@ -439,7 +440,7 @@ int QgsVectorLayerEditUtils::splitParts( const QList<QgsPointXY> &splitLine, boo
}
}
fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
fit = mLayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
}
int addPartRet = 0;
@ -469,7 +470,7 @@ int QgsVectorLayerEditUtils::splitParts( const QList<QgsPointXY> &splitLine, boo
if ( !addPartRet )
{
L->editBuffer()->changeGeometry( feat.id(), featureGeom );
mLayer->editBuffer()->changeGeometry( feat.id(), featureGeom );
}
else
{
@ -489,7 +490,7 @@ int QgsVectorLayerEditUtils::splitParts( const QList<QgsPointXY> &splitLine, boo
break;
}
}
L->editBuffer()->changeGeometry( feat.id(), featureGeom );
mLayer->editBuffer()->changeGeometry( feat.id(), featureGeom );
if ( topologicalEditing )
{
@ -507,11 +508,11 @@ int QgsVectorLayerEditUtils::splitParts( const QList<QgsPointXY> &splitLine, boo
}
}
if ( numberOfSplitParts == 0 && L->selectedFeatureCount() > 0 && returnCode == 0 )
if ( numberOfSplitParts == 0 && mLayer->selectedFeatureCount() > 0 && returnCode == 0 )
{
//There is a selection but no feature has been split.
//Maybe user forgot that only the selected features are split
returnCode = 4;
returnCode = QgsGeometry::NothingHappened;
}
return returnCode;
@ -520,7 +521,7 @@ int QgsVectorLayerEditUtils::splitParts( const QList<QgsPointXY> &splitLine, boo
int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsGeometry &geom )
{
if ( !L->isSpatial() )
if ( !mLayer->isSpatial() )
return 1;
if ( geom.isNull() )
@ -628,18 +629,18 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsGeometry &geom )
int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p )
{
if ( !L->isSpatial() )
if ( !mLayer->isSpatial() )
return 1;
double segmentSearchEpsilon = L->crs().isGeographic() ? 1e-12 : 1e-8;
double segmentSearchEpsilon = mLayer->crs().isGeographic() ? 1e-12 : 1e-8;
//work with a tolerance because coordinate projection may introduce some rounding
double threshold = 0.0000001;
if ( L->crs().mapUnits() == QgsUnitTypes::DistanceMeters )
if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceMeters )
{
threshold = 0.001;
}
else if ( L->crs().mapUnits() == QgsUnitTypes::DistanceFeet )
else if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceFeet )
{
threshold = 0.0001;
}
@ -649,7 +650,7 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p )
double sqrSnappingTolerance = threshold * threshold;
QgsFeature f;
QgsFeatureIterator fit = L->getFeatures( QgsFeatureRequest()
QgsFeatureIterator fit = mLayer->getFeatures( QgsFeatureRequest()
.setFilterRect( searchRect )
.setFlags( QgsFeatureRequest::ExactIntersect )
.setSubsetOfAttributes( QgsAttributeList() ) );
@ -685,7 +686,7 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p )
if ( sqrDistVertexSnap < sqrSnappingTolerance )
continue; // the vertex already exists - do not insert it
if ( !L->insertVertex( p.x(), p.y(), fid, segmentAfterVertex ) )
if ( !mLayer->insertVertex( p.x(), p.y(), fid, segmentAfterVertex ) )
{
QgsDebugMsg( "failed to insert topo point" );
}
@ -695,11 +696,11 @@ int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p )
}
int QgsVectorLayerEditUtils::boundingBoxFromPointList( const QList<QgsPointXY> &list, double &xmin, double &ymin, double &xmax, double &ymax ) const
bool QgsVectorLayerEditUtils::boundingBoxFromPointList( const QList<QgsPointXY> &list, double &xmin, double &ymin, double &xmax, double &ymax ) const
{
if ( list.size() < 1 )
{
return 1;
return false;
}
xmin = std::numeric_limits<double>::max();
@ -727,5 +728,5 @@ int QgsVectorLayerEditUtils::boundingBoxFromPointList( const QList<QgsPointXY> &
}
}
return 0;
return true;
}

View File

@ -19,9 +19,8 @@
#include "qgis_core.h"
#include "qgis.h"
#include "qgsfeature.h"
#include "qgsvectorlayer.h"
#include "qgsgeometry.h"
#include "qgsvectorlayer.h"
class QgsCurve;
@ -66,68 +65,40 @@ class CORE_EXPORT QgsVectorLayerEditUtils
QgsVectorLayer::EditResult deleteVertex( QgsFeatureId featureId, int vertex );
/** Adds a ring to polygon/multipolygon features
* \param ring ring to add
* \param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise
* @param ring ring to add
* @param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise
* all intersecting features are tested and the ring is added to the first valid feature.
* \param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter
* \returns
* 0 in case of success,
* 1 problem with feature type,
* 2 ring not closed,
* 3 ring not valid,
* 4 ring crosses existing rings,
* 5 no feature found where ring can be inserted
* @param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter
* @return OperationResult result code: success or reason of failure
*/
// TODO QGIS 3.0 returns an enum instead of a magic constant
int addRing( const QList<QgsPointXY> &ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr );
QgsGeometry::OperationResult addRing( const QList<QgsPointXY> &ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr );
/** Adds a ring to polygon/multipolygon features
* \param ring ring to add
* \param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise
/**
* Adds a ring to polygon/multipolygon features
* @param ring ring to add
* @param targetFeatureIds if specified, only these features will be the candidates for adding a ring. Otherwise
* all intersecting features are tested and the ring is added to the first valid feature.
* \param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter
* \returns
* 0 in case of success,
* 1 problem with feature type,
* 2 ring not closed,
* 3 ring not valid,
* 4 ring crosses existing rings,
* 5 no feature found where ring can be inserted
* \note available in Python bindings as addCurvedRing
* @param modifiedFeatureId if specified, feature ID for feature that ring was added to will be stored in this parameter
* @return OperationResult result code: success or reason of failure
* @note available in python bindings as addCurvedRing
*/
// TODO QGIS 3.0 returns an enum instead of a magic constant
int addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr ) SIP_PYNAME( addCurvedRing );
QgsGeometry::OperationResult addRing( QgsCurve *ring, const QgsFeatureIds &targetFeatureIds = QgsFeatureIds(), QgsFeatureId *modifiedFeatureId = nullptr ) SIP_PYNAME( addCurvedRing );
/** Adds a new part polygon to a multipart feature
* \returns
* 0 in case of success,
* 1 if selected feature is not multipart,
* 2 if ring is not a valid geometry,
* 3 if new polygon ring not disjoint with existing rings,
* 4 if no feature was selected,
* 5 if several features are selected,
* 6 if selected geometry not found
/**
* Adds a new part polygon to a multipart feature
* @returns QgsGeometry::OperationResult a result code: success or reason of failure
*/
// TODO QGIS 3.0 returns an enum instead of a magic constant
int addPart( const QList<QgsPointXY> &ring, QgsFeatureId featureId );
QgsGeometry::OperationResult addPart( const QList<QgsPointXY> &ring, QgsFeatureId featureId );
/** Adds a new part polygon to a multipart feature
* \returns
* 0 in case of success,
* 1 if selected feature is not multipart,
* 2 if ring is not a valid geometry,
* 3 if new polygon ring not disjoint with existing rings,
* 4 if no feature was selected,
* 5 if several features are selected,
* 6 if selected geometry not found
* \note available in Python bindings as addPartV2
/**
* Adds a new part polygon to a multipart feature
* @returns QgsGeometry::OperationResult a result code: success or reason of failure
* @note available in python bindings as addPartV2
*/
// TODO QGIS 3.0 returns an enum instead of a magic constant
int addPart( const QgsPointSequence &ring, QgsFeatureId featureId );
QgsGeometry::OperationResult addPart( const QgsPointSequence &ring, QgsFeatureId featureId );
//! \note available in Python bindings as addCurvedPart
// TODO QGIS 3.0 returns an enum instead of a magic constant
int addPart( QgsCurve *ring, QgsFeatureId featureId ) SIP_PYNAME( addCurvedPart );
// @note available in python bindings as addCurvedPart
QgsGeometry::OperationResult addPart( QgsCurve *ring, QgsFeatureId featureId ) SIP_PYNAME( addCurvedPart );
/** Translates feature by dx, dy
* \param featureId id of the feature to translate
@ -145,7 +116,7 @@ class CORE_EXPORT QgsVectorLayerEditUtils
* 4 if there is a selection but no feature split
*/
// TODO QGIS 3.0 returns an enum instead of a magic constant
int splitParts( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
QgsGeometry::OperationResult splitParts( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
/** Splits features cut by the given line
* \param splitLine line that splits the layer features
@ -155,7 +126,7 @@ class CORE_EXPORT QgsVectorLayerEditUtils
* 4 if there is a selection but no feature split
*/
// TODO QGIS 3.0 returns an enum instead of a magic constant
int splitFeatures( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
QgsGeometry::OperationResult splitFeatures( const QList<QgsPointXY> &splitLine, bool topologicalEditing = false );
/** Adds topological points for every vertex of the geometry.
* \param geom the geometry where each vertex is added to segments of other features
@ -173,15 +144,15 @@ class CORE_EXPORT QgsVectorLayerEditUtils
*/
int addTopologicalPoints( const QgsPointXY &p );
protected:
/** Little helper function that gives bounding box from a list of points.
\returns 0 in case of success */
int boundingBoxFromPointList( const QList<QgsPointXY> &list, double &xmin, double &ymin, double &xmax, double &ymax ) const;
private:
QgsVectorLayer *L = nullptr;
/**
* Little helper function that gives bounding box from a list of points.
* \returns True in case of success
*/
bool boundingBoxFromPointList( const QList<QgsPointXY> &list, double &xmin, double &ymin, double &xmax, double &ymax ) const;
QgsVectorLayer *mLayer = nullptr;
};
#endif // QGSVECTORLAYEREDITUTILS_H

View File

@ -1423,14 +1423,14 @@ class TestQgsGeometry(unittest.TestCase):
]
polyline = QgsGeometry.fromPolyline(points[0])
self.assertEqual(polyline.addPoints(points[1][0:1]), 2, "addPoints with one point line unexpectedly succeeded.")
self.assertEqual(polyline.addPoints(points[1][0:2]), 0, "addPoints with two point line failed.")
self.assertEqual(polyline.addPoints(points[1][0:1]), QgsGeometry.InvalidInput, "addPoints with one point line unexpectedly succeeded.")
self.assertEqual(polyline.addPoints(points[1][0:2]), QgsGeometry.Success, "addPoints with two point line failed.")
expwkt = "MultiLineString ((0 0, 1 0, 1 1, 2 1, 2 0), (3 0, 3 1))"
wkt = polyline.exportToWkt()
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
polyline = QgsGeometry.fromPolyline(points[0])
self.assertEqual(polyline.addPoints(points[1]), 0, "addPoints with %d point line failed." % len(points[1]))
self.assertEqual(polyline.addPoints(points[1]), QgsGeometry.Success, "addPoints with %d point line failed." % len(points[1]))
expwkt = "MultiLineString ((0 0, 1 0, 1 1, 2 1, 2 0), (3 0, 3 1, 5 1, 5 0, 6 0))"
wkt = polyline.exportToWkt()
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
@ -1439,7 +1439,7 @@ class TestQgsGeometry(unittest.TestCase):
polyline = QgsGeometry.fromPolyline(points[0])
polyline.geometry().addZValue(4.0)
points2 = [QgsPoint(p[0], p[1], 3.0, wkbType=QgsWkbTypes.PointZ) for p in points[1]]
self.assertEqual(polyline.addPointsV2(points2), 0)
self.assertEqual(polyline.addPointsV2(points2), QgsGeometry.Success)
expwkt = "MultiLineStringZ ((0 0 4, 1 0 4, 1 1 4, 2 1 4, 2 0 4),(3 0 3, 3 1 3, 5 1 3, 5 0 3, 6 0 3))"
wkt = polyline.exportToWkt()
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
@ -1456,12 +1456,12 @@ class TestQgsGeometry(unittest.TestCase):
polygon = QgsGeometry.fromPolygon(points[0])
self.assertEqual(polygon.addPoints(points[1][0][0:1]), 2, "addPoints with one point ring unexpectedly succeeded.")
self.assertEqual(polygon.addPoints(points[1][0][0:2]), 2, "addPoints with two point ring unexpectedly succeeded.")
self.assertEqual(polygon.addPoints(points[1][0][0:3]), 2, "addPoints with unclosed three point ring unexpectedly succeeded.")
self.assertEqual(polygon.addPoints([QgsPointXY(4, 0), QgsPointXY(5, 0), QgsPointXY(4, 0)]), 2, "addPoints with 'closed' three point ring unexpectedly succeeded.")
self.assertEqual(polygon.addPoints(points[1][0][0:1]), QgsGeometry.InvalidInput, "addPoints with one point ring unexpectedly succeeded.")
self.assertEqual(polygon.addPoints(points[1][0][0:2]), QgsGeometry.InvalidInput, "addPoints with two point ring unexpectedly succeeded.")
self.assertEqual(polygon.addPoints(points[1][0][0:3]), QgsGeometry.InvalidInput, "addPoints with unclosed three point ring unexpectedly succeeded.")
self.assertEqual(polygon.addPoints([QgsPointXY(4, 0), QgsPointXY(5, 0), QgsPointXY(4, 0)]), QgsGeometry.InvalidInput, "addPoints with 'closed' three point ring unexpectedly succeeded.")
self.assertEqual(polygon.addPoints(points[1][0]), 0, "addPoints failed")
self.assertEqual(polygon.addPoints(points[1][0]), QgsGeometry.Success, "addPoints failed")
expwkt = "MultiPolygon (((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0)),((4 0, 5 0, 5 2, 3 2, 3 1, 4 1, 4 0)))"
wkt = polygon.exportToWkt()
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
@ -1469,13 +1469,13 @@ class TestQgsGeometry(unittest.TestCase):
mp = QgsGeometry.fromMultiPolygon(points[:1])
p = QgsGeometry.fromPolygon(points[1])
self.assertEqual(mp.addPartGeometry(p), 0)
self.assertEqual(mp.addPartGeometry(p), QgsGeometry.Success)
wkt = mp.exportToWkt()
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
mp = QgsGeometry.fromMultiPolygon(points[:1])
mp2 = QgsGeometry.fromMultiPolygon(points[1:])
self.assertEqual(mp.addPartGeometry(mp2), 0)
self.assertEqual(mp.addPartGeometry(mp2), QgsGeometry.Success)
wkt = mp.exportToWkt()
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
@ -1483,7 +1483,7 @@ class TestQgsGeometry(unittest.TestCase):
polygon = QgsGeometry.fromPolygon(points[0])
polygon.geometry().addZValue(4.0)
points2 = [QgsPoint(pi[0], pi[1], 3.0, wkbType=QgsWkbTypes.PointZ) for pi in points[1][0]]
self.assertEqual(polygon.addPointsV2(points2), 0)
self.assertEqual(polygon.addPointsV2(points2), QgsGeometry.Success)
expwkt = "MultiPolygonZ (((0 0 4, 1 0 4, 1 1 4, 2 1 4, 2 2 4, 0 2 4, 0 0 4)),((4 0 3, 5 0 3, 5 2 3, 3 2 3, 3 1 3, 4 1 3, 4 0 3)))"
wkt = polygon.exportToWkt()
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
@ -1492,38 +1492,38 @@ class TestQgsGeometry(unittest.TestCase):
empty = QgsGeometry()
# if not default type specified, addPart should fail
result = empty.addPoints([QgsPointXY(4, 0)])
assert result != 0, 'Got return code {}'.format(result)
assert result != QgsGeometry.Success, 'Got return code {}'.format(result)
result = empty.addPoints([QgsPointXY(4, 0)], QgsWkbTypes.PointGeometry)
self.assertEqual(result, 0, 'Got return code {}'.format(result))
self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result))
wkt = empty.exportToWkt()
expwkt = 'MultiPoint ((4 0))'
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
result = empty.addPoints([QgsPointXY(5, 1)])
self.assertEqual(result, 0, 'Got return code {}'.format(result))
self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result))
wkt = empty.exportToWkt()
expwkt = 'MultiPoint ((4 0),(5 1))'
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
# next try with lines
empty = QgsGeometry()
result = empty.addPoints(points[0][0], QgsWkbTypes.LineGeometry)
self.assertEqual(result, 0, 'Got return code {}'.format(result))
self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result))
wkt = empty.exportToWkt()
expwkt = 'MultiLineString ((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0))'
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
result = empty.addPoints(points[1][0])
self.assertEqual(result, 0, 'Got return code {}'.format(result))
self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result))
wkt = empty.exportToWkt()
expwkt = 'MultiLineString ((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0),(4 0, 5 0, 5 2, 3 2, 3 1, 4 1, 4 0))'
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
# finally try with polygons
empty = QgsGeometry()
result = empty.addPoints(points[0][0], QgsWkbTypes.PolygonGeometry)
self.assertEqual(result, 0, 'Got return code {}'.format(result))
self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result))
wkt = empty.exportToWkt()
expwkt = 'MultiPolygon (((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0)))'
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)
result = empty.addPoints(points[1][0])
self.assertEqual(result, 0, 'Got return code {}'.format(result))
self.assertEqual(result, QgsGeometry.Success, 'Got return code {}'.format(result))
wkt = empty.exportToWkt()
expwkt = 'MultiPolygon (((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0)),((4 0, 5 0, 5 2, 3 2, 3 1, 4 1, 4 0)))'
assert compareWkt(expwkt, wkt), "Expected:\n%s\nGot:\n%s\n" % (expwkt, wkt)